From 7fcdc1c788725f866de71fc9dfd8c4d1cb132b57 Mon Sep 17 00:00:00 2001 From: Orangerot Date: Fri, 24 May 2024 17:42:08 +0200 Subject: Initial commit --- .../assets/.gitignore | 3 + .../assets/.gitingnore | 3 + .../assets/commits-dashboard.png | Bin 0 -> 44171 bytes .../assets/commits-server.png | Bin 0 -> 36705 bytes .../assets/contributors-dashboard.png | Bin 0 -> 193750 bytes .../assets/contributors-server.png | Bin 0 -> 222521 bytes .../assets/diagrams/backendComponentDiagram.puml | 61 ++++++ .../diagrams/classdiagram-authentication.puml | 112 +++++++++++ .../diagrams/classdiagram-episode-actions.puml | 84 ++++++++ .../assets/diagrams/classdiagram-model.puml | 109 +++++++++++ .../diagrams/classdiagram-subscriptions.puml | 75 ++++++++ .../assets/diagrams/classdiagram-util.puml | 43 +++++ .../assets/diagrams/classdiagram.puml | 68 +++++++ .../assets/diagrams/componentdiagram.puml | 53 ++++++ .../assets/diagrams/db.puml | 78 ++++++++ .../assets/diagrams/deployment.puml | 59 ++++++ .../diagrams/sequencediagram-forgotAndResetPW.puml | 41 ++++ .../sequencediagram-getEpisodeActions.puml | 38 ++++ ...ncediagram-getEpisodeActionsOfPodcastSince.puml | 32 ++++ .../diagrams/sequencediagram-getSubscriptions.puml | 38 ++++ .../assets/diagrams/sequencediagram-register.puml | 26 +++ .../sequencediagram-uploadEpisodeActions.puml | 38 ++++ .../sequencediagram-uploadSubscriptions.puml | 32 ++++ .../assets/episode.png | Bin 0 -> 10778 bytes .../assets/gantt-plan.puml | 31 +++ .../assets/gantt-reality.puml | 39 ++++ 21-implementierungsheft-kolloquium/assets/help.png | Bin 0 -> 14423 bytes .../assets/lastupdate.png | Bin 0 -> 9363 bytes 21-implementierungsheft-kolloquium/assets/logo.svg | 211 +++++++++++++++++++++ .../assets/navbar.png | Bin 0 -> 8940 bytes .../assets/password-margin.png | Bin 0 -> 8701 bytes .../assets/password.png | Bin 0 -> 8781 bytes .../assets/subscription.png | Bin 0 -> 79929 bytes 33 files changed, 1274 insertions(+) create mode 100644 21-implementierungsheft-kolloquium/assets/.gitignore create mode 100644 21-implementierungsheft-kolloquium/assets/.gitingnore create mode 100644 21-implementierungsheft-kolloquium/assets/commits-dashboard.png create mode 100644 21-implementierungsheft-kolloquium/assets/commits-server.png create mode 100644 21-implementierungsheft-kolloquium/assets/contributors-dashboard.png create mode 100644 21-implementierungsheft-kolloquium/assets/contributors-server.png create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/db.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/episode.png create mode 100644 21-implementierungsheft-kolloquium/assets/gantt-plan.puml create mode 100644 21-implementierungsheft-kolloquium/assets/gantt-reality.puml create mode 100644 21-implementierungsheft-kolloquium/assets/help.png create mode 100644 21-implementierungsheft-kolloquium/assets/lastupdate.png create mode 100644 21-implementierungsheft-kolloquium/assets/logo.svg create mode 100644 21-implementierungsheft-kolloquium/assets/navbar.png create mode 100644 21-implementierungsheft-kolloquium/assets/password-margin.png create mode 100644 21-implementierungsheft-kolloquium/assets/password.png create mode 100644 21-implementierungsheft-kolloquium/assets/subscription.png (limited to '21-implementierungsheft-kolloquium/assets') diff --git a/21-implementierungsheft-kolloquium/assets/.gitignore b/21-implementierungsheft-kolloquium/assets/.gitignore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/.gitignore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/21-implementierungsheft-kolloquium/assets/.gitingnore b/21-implementierungsheft-kolloquium/assets/.gitingnore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/.gitingnore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/21-implementierungsheft-kolloquium/assets/commits-dashboard.png b/21-implementierungsheft-kolloquium/assets/commits-dashboard.png new file mode 100644 index 0000000..89672d6 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/commits-dashboard.png differ diff --git a/21-implementierungsheft-kolloquium/assets/commits-server.png b/21-implementierungsheft-kolloquium/assets/commits-server.png new file mode 100644 index 0000000..a9d1a5c Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/commits-server.png differ diff --git a/21-implementierungsheft-kolloquium/assets/contributors-dashboard.png b/21-implementierungsheft-kolloquium/assets/contributors-dashboard.png new file mode 100644 index 0000000..3621c31 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/contributors-dashboard.png differ diff --git a/21-implementierungsheft-kolloquium/assets/contributors-server.png b/21-implementierungsheft-kolloquium/assets/contributors-server.png new file mode 100644 index 0000000..500bb43 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/contributors-server.png differ diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml b/21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml new file mode 100644 index 0000000..e382351 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml @@ -0,0 +1,61 @@ +@startuml +' skinparam linetype ortho + +'######################################################################### +'SubscriptionsAPI +component SubscriptionsAPI { + + component SubscriptionService + component SubscriptionController + component SubscriptionDataAccessLayer + + portout "Webserver" as wSub + portin "Database" as dSub + } + +dSub --0)- SubscriptionDataAccessLayer +SubscriptionDataAccessLayer --0)- SubscriptionService +SubscriptionService --0)- SubscriptionController +SubscriptionController --0)- wSub + +'######################################################################### + + +'######################################################################### +'EpisodeActionsAPI + +component EpisodeActionsAPI { + component EpisodeActionService + component EpisodeActionController + component EpisodeActionDataAccessLayer + + portout "Webserver" as wEpisode + portin "Database" as dEpisode +} + +dEpisode --0)- EpisodeActionDataAccessLayer +EpisodeActionController --0)- wEpisode +EpisodeActionDataAccessLayer --0)- EpisodeActionService +EpisodeActionService --0)- EpisodeActionController + +'######################################################################### + + +'######################################################################### +'AuthenticationAPI + +component AuthenticationAPI { + component AuthenticationService + component AuthenticationController + component AuthenticationDataAccessLayer + + portout "Webserver" as wAuth + portin "Database" as dAuth +} + +dAuth --0)- AuthenticationDataAccessLayer +AuthenticationController --0)- wAuth +AuthenticationDataAccessLayer --0)- AuthenticationService +AuthenticationService --0)- AuthenticationController + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml new file mode 100644 index 0000000..a2b3518 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml @@ -0,0 +1,112 @@ +@startuml + +package authenticationAPI <> { + package authenticationDataAccessLayer <> { + ' interface AuthenticationDao { + ' String login(String username) + ' int logout(String username) + ' } + + ' class AuthenticationDataAccessService <<@Respository>> { + ' <> AuthenticationDataAccessService(JpaTemplate jpaTemplate) + ' String login(String username) + ' int logout(String username) + ' } + + interface UserDetailsManager { + void createUser(UserDetails userDetails) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note left + Aus org.springframework.security.provisioning + - liefert Methoden zum Erstellen neuer User + und zum Aktualisieren bestehender. + end note + + class JdbcUserDetailsManager <<@Repository>> { + <> JdbcUserDetailsManager(DataSource dataSource) + void createUser(UserDetails user) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note right + User Management Service aus dem Paket + org.springframework.security.provisioning + der CRUD Operationen für User bereitstellt. + Hier sind nur die relevanten Methoden modelliert. + end note + } + + package authenticationService <> { + class AuthenticationService <<@Service>> { + -- + <> AuthenticationService(UserDetailsManager userDetailsManager) + List verifyLogin(String username) + int logout(String username) + int forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + .. via JdbcUserDetailsManager .. + int resetPassword(String username, RequestWithPassword requestWithPassword) + int registerUser(UserDetails user) + int changePassword(String username, ChangePasswordRequest changePasswordRequest) + int deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class JavaMailSenderImpl {} + note left + Aus org.springframework.mail.javamail. + Implementierung des JavaMailSender Interfaces, + welches das MailSender Interface durch Unterstützung + von MIME Nachrichten erweitert. + Das MailSender Interface definiert dabei eine + Strategie zum Versenden einfacher Mails. + Unterstützt sowohl JavaMail MimeMessages und + Spring SimpleMailMessages. + end note + } + + package authenticationController <> { + class AuthenticationController <<@Controller>> { + <> AuthenticationController(AuthenticationService authenticationService) + ResponseEntity> verifyLogin(String username) + ResponseEntity logout(String username) + ResponseEntity forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + ResponseEntity resetPassword(String username, RequestWithPassword requestWithPassword) + ResponseEntity registerUser(UserDetails user) + ResponseEntity changePassword(String username, ChangePasswordRequest changePasswordRequest) + ResponseEntity deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class ChangePasswordRequest { + <> ChangePasswordRequest(String oldPassword, String newPassword) + String getOldPassword() + String getNewPassword() + } + + class ForgotPasswordRequest { + <> ForgotPasswordRequest(String email) + String getEmail() + } + + class RequestWithPassword { + <> ResetPasswordRequest(String password) + String getPassword() + } + } +} + +' User <.. AuthenticationDataAccessService: DB +' User <.. JdbcUserDetailsManager: DB +UserDetailsManager <.. AuthenticationService: <> +' AuthenticationDao <.. AuthenticationService: <> +AuthenticationService --o AuthenticationController +' AuthenticationDao <|. AuthenticationDataAccessService: <> +UserDetailsManager <|. JdbcUserDetailsManager: <> +JavaMailSenderImpl <. AuthenticationService: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml new file mode 100644 index 0000000..7a4530e --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml @@ -0,0 +1,84 @@ +@startuml + +package episodeActionsAPI <> { + package episodeActionDataAccessLayer <> { + class EpisodeActionDataAccessService <<@Repository>> { + <> EpisodeActionDataAccessService (JpaTemplate jpaTemplate) + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + + interface EpisodeActionDao { + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionService <> { + class EpisodeActionService <<@Service>> { + <> EpisodeActionService (EpisodeActionDao episodeActionDao) + LocalDateTime addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionController <> { + class EpisodeActionController <<@Controller>>{ + <> EpisodeActionController (EpisodeActionService episodeActionService) + ResponseEntity addEpisodeActions(String username, EpisodeActionPostRequest episodeActionPostRequest) + ResponseEntity getEpisodeActions(String username, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcast(String username, String podcastURL, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsSince(String username, String deviceID, long since, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcastSince(String username, String podcastURL, String deviceID, long since, boolean aggregated) + } + + class EpisodeActionPostResponse { + <> EpisodeActionPostResponse(List> updateURLs) + long getTimestamp() + List> getUpdatedURLs() + } + + class EpisodeActionPost { + <> EpisodeActionPost(String podcastURL, String episodeURL, Action action, LocalDateTime timestamp, int started, int position) + String getPodcastURL() + String getEpisodeURL() + int getGUID() + Action getAction() + LocalDateTime getTimestamp() + int getStarted() + int getPosition() + EpisodeAction getEpisodeAction() + } + + class EpisodeActionPostRequest { + <> EpisodeActionPostRequest(List episodeActionPosts) + List getEpisodeActionPosts() + } + + class EpisodeActionGetResponse { + <> EpisodeActionGetResponse(List episodeActionPosts) + List getEpisodeActionPosts() + long getTimestamp() + } + } +} + +EpisodeActionPost -o EpisodeActionGetResponse +EpisodeActionPost -o EpisodeActionPostRequest +' EpisodeAction <.. EpisodeActionDataAccessService: DB +' Episode <.. EpisodeActionDataAccessService: DB +EpisodeActionDao <.. EpisodeActionService: <> +EpisodeActionService --o EpisodeActionController +EpisodeActionDao <|. EpisodeActionDataAccessService: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml new file mode 100644 index 0000000..72ad49f --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml @@ -0,0 +1,109 @@ +@startuml + +package model <> { + class Subscription { + <> Subscription(String url, String title) + int getID() + String getURL() + long getLastActionTimestamp() + String getTitle() + } + + class SubscriptionAction { + <> SubscriptionAction(int userID, int subscriptionID) + int getID() + int getUserID() + int getSubscriptionID() + long getTimestamp() + boolean getAdded() + } + + class Episode { + <> Episode(int subscriptionID, int id, String url, String title, String thumbnailURL, int total) + int getSubscriptionID() + int getID() + int getGUID() + String getURL() + String getTitle() + int getTotal() + } + + enum Action { + Download + Play + Delete + New + Flattr + String getJsonProperty() + } + + class EpisodeAction { + <> EpisodeAction(Action action, LocalDateTime timestamp, int started, int position) + int getEpisodeID() + Action getAction() + long getTimestamp() + int getStarted() + int getPosition() + void setEpisodeID() + EpisodeActionPost getEpisodeActionPost(String podcastURL, String episodeURL) + } + + interface UserDetails { + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + note left + Aus org.springframework.security.core.userdetails. + Wird für die Schnittstelle UserDetailsManager benötigt. + Stellt wichtige Informationen eines Users bereit. + Diese werden nur indirekt von Spring Security + benutzt, indem sie vorher in Authentication Objekten + gekapselt werden. + end note + + class User { + -- + <> User(String username, String password) + int getID() + String getSessionToken() + boolean getEmailIsValidated() + .. interface methods .. + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + + interface GrantedAuthority { + String getAuthority() + } + note right + Aus org.springframework.security.core. + Wird für die Schnittstelle UserDetails benötigt. + Repräsentiert eine Autorisierung, die einem + Authentication Objekt gewährt wird. + end note + + class Authority { + <> Authority() + String getAuthority() + } +} + +Subscription <. SubscriptionAction: ID +Action <-- EpisodeAction +EpisodeAction .> Episode: ID +UserDetails <|.. User: <> +User -> Authority +GrantedAuthority <|.. Authority: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml new file mode 100644 index 0000000..432f185 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml @@ -0,0 +1,75 @@ +@startuml + +package subscriptionsAPI <> { + package subscriptionDataAccessLayer <> { + class SubscriptionDataAccessService <<@Repository>> { + <> SubscriptionDataAccessService(JpaTemplate jpaTemplate) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + + interface SubscriptionDao { + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionService <> { + class SubscriptionService <<@Service>> { + <> SubscriptionService(SubscriptionDao subscriptionDao) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionController <> { + class SubscriptionController <<@Controller>>{ + ' @Autowired + <> SubscriptionController(SubscriptionService subscriptionService) + ' @GetMapping + ResponseEntity> getSubscriptions(String username, String deviceID, String functionJSONP) + ' @PutMapping + ResponseEntity uploadSubscriptions(String username, String deviceID, List subscriptions) + ' @PostMapping + ResponseEntity applySubscriptionDelta(String username, String deviceID, SubscriptionDelta delta) + ' @GetMapping + ResponseEntity getSubscriptionDelta(String username, String deviceID, long since) + ResponseEntity> getTitles(String username, String deviceID) + } + + class SubscriptionTitles { + <> SubscriptionTitles(Subscription subscription, List episodeTitles) + Subscription getSubscription() + List getEpisodesTitles() + } + + class SubscriptionDelta { + <> SubscriptionDelta(List add, List remove) + List getRemove() + LocalDate getTimestamp() + List> getUpdate_urls() + } + } + +} + +' Subscription <.. SubscriptionDataAccessService: DB +' SubscriptionAction <.. SubscriptionDataAccessService: DB +SubscriptionService --o SubscriptionController +SubscriptionDao <.. SubscriptionService: <> +SubscriptionDao <|. SubscriptionDataAccessService: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml new file mode 100644 index 0000000..03dfc9a --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml @@ -0,0 +1,43 @@ +@startuml + +package util <> { + class RSSParser { + <> RSSParser(String subscriptionURL) + String getSubscriptionTitle() + List getEpisodes() + Episode getEpisodeForURL(String episodeURL) + } + note bottom + Verwendet intern Spring um + HTTP-Anfragen zu erstellen. + end note + + class CleanCronJob { + <> CleanCronJob(JdbcUserDetailsManager jdbcUserDetailsManager) + void cleanInvalidUsers() + } + note bottom + Hintergrundservice, der in periodischen Abständen + Nutzer, die ihre E-Mail-Adresse nicht nach 24 Stunden + bestätigt haben, wieder aus der Datenbank löscht. + (Auf die Assoziation zu JdbcUserDetailsManager wird + im Sinne der Übersichtlichkeit verzichtet.) + end note + + class ResponseEntity { + <> ResponseEntity(T body, HttpStatusCode status) + T getBody() + HttpStatusCode getStatusCode() + } + note bottom + Aus org.springframework.http. + Erweitert die Klasse HttpEntity, welche + ein HTTP Anfrage- oder Antwort-Objekt + repräsentiert, durch einen HttpStatusCode. + Wird von den Controller-Methoden als + Rückgabewert verwendet. + end note +} + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml new file mode 100644 index 0000000..f833aa2 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml @@ -0,0 +1,68 @@ +@startuml +' skinparam linetype ortho +' skinparam groupInheritance 2 +allowmixing + +!include classdiagram-authentication.puml +!include classdiagram-episode-actions.puml +!include classdiagram-model.puml +!include classdiagram-subscriptions.puml +!include classdiagram-util.puml + +class SecurityConfigurationBasicAuth { + <> SecurityConfigurationBasicAuth() + PasswordEncoder encoder() + UserDetailsManager userDetailsService() + SecuryFilterChain fiterChain(HTTPSecurity http) throws Excpetion +} +note top + Erstellt einen Servlet Filter (springSecurityFilterChain) + der für die gesamte Sicherheit zuständig ist (Schutz der URLs, + Validierung von Anmeldedaten, Weiterleitung zur Anmeldung, etc.). +end note + +class PSEApplication { + <> PSEApplication() + void main(String[] args) +} + +database Datenbank +Datenbank <-[hidden]d- subscriptionsAPI +Datenbank <-[hidden]d- episodeActionsAPI +Datenbank <-[hidden]d- authenticationAPI +() SQL as SQLSub +() SQL as SQLAuth +() SQL as SQLEpisode + +Datenbank -- SQLSub +Datenbank -- SQLAuth +Datenbank -- SQLEpisode + +Subscription --o SubscriptionTitles +EpisodeActionPost -o SubscriptionTitles +UserDetailsManager <.. SecurityConfigurationBasicAuth: <> + +SubscriptionController ..o PSEApplication +AuthenticationController ..o PSEApplication +EpisodeActionController ..o PSEApplication +SecurityConfigurationBasicAuth ..o PSEApplication + +PSEApplication --() HTTP + +SQLSub )-- SubscriptionDataAccessService: JPA +' SQLAuth )-- AuthenticationDataAccessService: JPA +SQLAuth )-- JdbcUserDetailsManager: JDBC +SQLEpisode )-- EpisodeActionDataAccessService: JPA + +RSSParser <. SubscriptionDataAccessService: <> +RSSParser <. EpisodeActionDataAccessService: <> +' JdbcUserDetailsManager <-- CleanCronJob + +model .o Datenbank: ORM (User, SubscriptionAction, Subscription, EpisodeAction, Episode) +' Datenbank o.. Subscription: ORM +' Datenbank o.. SubscriptionAction: ORM +' Datenbank o.. Episode: ORM +' Datenbank o.. EpisodeAction: ORM +' Datenbank o.. User: ORM + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml b/21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml new file mode 100644 index 0000000..dea4a1d --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml @@ -0,0 +1,53 @@ +@startuml + +[App] as app +[VueRouter] as router +[NavbarComponent] as navbar +[LoginPage] as login_page +[SubscriptionsPage] as abo_page +[EpisodesPage] as episodes_page +[SettingsPage] as settings_page +[ForgotPasswordPage] as forgot_page +[ResetPasswordPage] as reset_page +note top + Wird in der E-Mail zum Zurücksetzen des Passworts mit dem JWT-Token verlinkt. + Sendet das alte und neue Passwort und den JWT an die API. +end note +[RegistrationPage] as registration_page + +[SubscriptionComponent] as sub +[EpisodeComponent] as episode +[LastUpdateComponent] as last_update +[PasswordValidatorComponent] as password + +app --> router + +app --> navbar +router --> login_page +router --> forgot_page +router --> reset_page +router --> registration_page +router --> abo_page +router --> episodes_page +router --> settings_page + +navbar -[hidden] router + +episodes_page -[hidden] abo_page +login_page -[hidden] forgot_page +registration_page -[hidden] reset_page +abo_page -[hidden] settings_page +forgot_page -[hidden] episodes_page +' forgot_page -[hidden] settings_page + +abo_page --> sub +episodes_page --> episode + +sub --> last_update +episode --> last_update + +settings_page --> password +reset_page --> password +registration_page --> password + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/db.puml b/21-implementierungsheft-kolloquium/assets/diagrams/db.puml new file mode 100644 index 0000000..bdefaea --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/db.puml @@ -0,0 +1,78 @@ +@startuml +' Type Symbol +' Zero or One |o-- +' Exactly One ||-- +' Zero or Many }o-- +' One or Many }|-- + +skinparam linetype ortho + +entity User { + * int id <> + * String email + * String password + * boolean verified + * long created_at +} + +entity SubscriptionAction { + * int id <> + * int user_id + * long timestamp + * int subscription_id + * boolean added +} + +entity Subscription { + * int id <> + * String url + * long timestamp + * String title +} + +entity Episode { + * int id <> + * int guid <> + * String url + * String title + * int total + * int subscription_id +} +note right + Wenn der Client eine GUID aus dem Feed mitsendet, wird + diese statt der URL verwendet um die Episode zu finden. + So wird die Episode auch noch gefunden, nachdem sich + die URL geändert hat. +end note +note bottom of Episode + Wenn für die Episoden-URL einer EpisodeAction noch keine Episode in der Datenbank steht, + dann schreibe dafür ein Dummy-Objekt in Datenbank und lade asynchron die Episoden der Subscription. + Ersetze dann die Dummy-Objekte durch die Episoden und setze den Timestamp der Subscription auf + die aktuelle Zeit. + Um DoS-Angriffe auf den Backend-Server abzuwenden, können die Episoden einer Subscription erst + nach einer Stunde erneut gefetched werden. Bis dahin werden für EpisodeActions, die sich auf noch + nicht geladene Episoden beziehen, nur Dummy-Objekte für die Episoden in die Datenbank geschrieben. + Es sei noch darauf hingewiesen, dass diese Dummy-Episoden bei Anfragen nicht mit ausgegeben werden. +end note + +entity EpisodeAction { + * int id <> + * int user_id + * int episode_id + * long timestamp + * int action + * int started + * int position +} +note right + Speichere für jede Episode + nur letzte Play-Action. +endnote + +User ||--o{ EpisodeAction +User ||--o{ SubscriptionAction +SubscriptionAction }|--|| Subscription +EpisodeAction }|--|| Episode +Subscription ||-right-|{ Episode + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml b/21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml new file mode 100644 index 0000000..b8d0491 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml @@ -0,0 +1,59 @@ +@startuml + +node "<> \nBackend Server" as backendServer{ + database " <> \n MariaDB Server 10.6" as database { + rectangle rectangle1 [ + <> + User + ] + rectangle rectangle2 [ + <> + SubscriptionAction + ] + rectangle rectangle3 [ + <> + EpisodeAction + ] + rectangle rectangle4 [ + <> + Subscription + ] + rectangle rectangle5 [ + <> + Episode + ] + } + + node "<> \nJava Spring" as javaSpring{ + node " <> \n Tomcat Webserver" + } +} + +node "<> \nFrontend" as frontendServer { + +} + +node "<> \nEndgerät" as terminal { + node "<> \nBrowser" as browser + node "<> \nPodcatcher" as podcatcher +} + +backendServer "1" - "*" podcatcher + +node "<> \nFrontend Server" as frontendServer{ + node "<> \nVue.js" as vuejs { + + } +} + +podcatcher -[hidden] browser + +backendServer - "1" frontendServer + +database "1" -- "1" javaSpring + +browser "*" -- frontendServer + + + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml new file mode 100644 index 0000000..603130c --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml @@ -0,0 +1,41 @@ +@startuml + +skinparam ParticipantPadding 30 + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/forgot.json"" \n//@RequestBody ForgotPasswordRequest forgotPasswordRequest// \n\n-> forgotPassword(//forgotPasswordRequest//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: forgotPassword(//forgotPasswordRequest//) +activate AuthenticationService +participant JavaMailSenderImpl << (C, #ADD1B2) >> +AuthenticationService -> JavaMailSenderImpl: create link to reset password with JWT as URL parameter \n-> send(SimpleMailMessage simpleMessage) with link +activate JavaMailSenderImpl +<<- JavaMailSenderImpl: sends email with link containing a JWT to reset password +JavaMailSenderImpl --> AuthenticationService +deactivate JavaMailSenderImpl +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController +||60|| +-> AuthenticationController: ""PUT /api/2/auth/{username}/resetpassword.json"" \n//@RequestParam String jwt// \n//@RequestBody ResetPasswordRequest resetPasswordRequest// \n\n-> login user (""username"") via JWT (//jwt//) \n-> resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationController +AuthenticationController -> AuthenticationService: resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: String oldPassword = //resetPasswordRequest//.getOldPassword() \nString newPassword = //resetPasswordRequest//.getNewPassword() \n-> changePassword(newPassword, oldPassword) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: change password of logged in user +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml new file mode 100644 index 0000000..47497d5 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("device") String deviceID// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActions(""username"", //deviceID//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert, + da nicht zwischen Geräten unterschieden und für jede + Episode sowieso nur die letzte Play-Action gespeichert + wird. Dies gilt für alle GET-Anfragen der Episode Actions API. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActions(""username"") +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActions(""username"") +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> EpisodeActionDataAccessService: getEpisodeActionsSince(""username"", \nLocalDateTime.MIN.toEpochSecond(ZoneOffset.UTC)) +database Database +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get all EpisodeActions for all subscribed podcasts +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than LocalDateTime.MIN (none) +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionDataAccessService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml new file mode 100644 index 0000000..d8797d1 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Get Episode Actions of Podcast Since** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("podcast") String podcastURL// \n//@RequestParam("device") String deviceID// \n//@RequestParam("since") long since// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //deviceID//, //since//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert. + Siehe Notiz in Sequenzdiagramm **Get All Episode Actions**. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionDataAccessService +database Database +EpisodeActionDataAccessService -> Database: get all EpisodeActions the given podcast (//podcastURL//) +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than //since// +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml new file mode 100644 index 0000000..4d8ab90 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""GET /subscriptions/{username}.json"" \n"" /subscriptions/{username}/{deviceid}.json"" \n//@RequestParam("jsonp") String functionJSONP// \n\n-> getSubscriptions(""username"", ""deviceid"", //functionJSONP//) +activate SubscriptionController +note right + Die Parameter ""deviceid"" und + //functionJSONP// werden ignoriert, + da nicht zwischen Geräten unterschieden + und JSONP nicht unterstützt wird. +end note +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: getSubscriptions(""username"") +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: getSubscriptions(""username"") +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: getSubscriptionsSince(""username"", LocalDateTime.MIN) +database Database +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: get all Subscriptions for ""username"" +activate Database +Database --> SubscriptionDataAccessService: List subscriptions +SubscriptionDataAccessService -> Database: get Podcasts from Subscriptions +Database --> SubscriptionDataAccessService: List subscribedPodcasts +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: List podcastURLs +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity> podcastURLs \n \n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml new file mode 100644 index 0000000..b7b7aa1 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml @@ -0,0 +1,26 @@ +@startuml + +' title =**Register** + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/register.json"" \n//@RequestBody UserDetails user// \n\n-> registerUser(//user//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: registerUser(//user//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: createUser(//user//) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: create new User with given UserDetails (//user//) +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml new file mode 100644 index 0000000..d3dac57 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Upload Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""POST /api/2/episodes/{username}.json"" \n//@RequestBody EpisodeActionPostRequest episodeActionPostRequest// \n\n-> addEpisodeActions(""username"", //episodeActionPostRequest//) +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: addEpisodeActions(""username"", \nepisodeActionPosts = //episodeActionPostRequest//.getEpisodeActionPosts()) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: addEpisodeActions(""username"", episodeActionPosts) +database Database +activate EpisodeActionDataAccessService +loop for each EpisodeActionPost in episodeActionPosts -> episodeAction = episodeActionPost.getEpisodeAction() +opt episodeAction.getAction().equals(Action.PLAY) +EpisodeActionDataAccessService -> Database: set episodeID field of episodeAction for this ""username"" via podcastURL and episodeURL +activate Database +Database --> EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get last EpisodeAction with this episodeID if present +Database --> EpisodeActionDataAccessService: Optional lastEpisodeAction +opt lastEpisodeAction.isPresent() +EpisodeActionDataAccessService -> Database: replace lastEpisodeAction with episodeAction +else else +EpisodeActionDataAccessService -> Database: add episodeAction to DB as new entry +end +Database --> EpisodeActionDataAccessService +deactivate Database +end +end +EpisodeActionDataAccessService --> EpisodeActionService: long latestTimestamp +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: LocalDateTime timestamp = LocalDateTime.ofEpochSecond(latestTimestamp, 0, ZoneOffset.UTC) +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity \n(with empty list for updateURLs) \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml new file mode 100644 index 0000000..1edc8cf --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Upload Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""PUT /subscriptions/{username}/{deviceid}.json"" \n//@RequestBody List subscriptions// \n\n-> uploadSubscriptions(""username"", ""deviceid"", //subscriptions//) +activate SubscriptionController +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +database Database +SubscriptionDataAccessService -> Database: delete all subsciptions of ""username"" +activate Database +Database --> SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: addSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: upload all subscriptions (//subscriptions//) for ""username"" +Database --> SubscriptionDataAccessService +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: int indicating status +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity with empty String for success \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/episode.png b/21-implementierungsheft-kolloquium/assets/episode.png new file mode 100644 index 0000000..c0db4a2 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/episode.png differ diff --git a/21-implementierungsheft-kolloquium/assets/gantt-plan.puml b/21-implementierungsheft-kolloquium/assets/gantt-plan.puml new file mode 100644 index 0000000..0e90aa2 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/gantt-plan.puml @@ -0,0 +1,31 @@ +@startgantt + +printscale daily zoom 5 +project starts on 2023-01-30 + +-- Backend -- +[Controller-Schicht] on {Immanuel} lasts 2 days +[Service-Schicht (Daten durchreichen)] on {Daniel} lasts 2 days +[Authentifizierung] on {Gero} lasts 4 days +[Model-Paket] on {Daniel} lasts 1 days +[Datenbank aufsetzen] on {Immanuel} lasts 4 days +[Util-Paket (RSSParser)] on {Daniel} {Lukas} lasts 6 days +[DataAccess-Schicht] on {Immanuel} {Julius} lasts 8 days +[Service-Schicht (Geschäftslogik)] on {Daniel} {Immanuel} lasts 8 days +[Util-Paket (CleanCronJob)] on {Julius} lasts 2 days +-- Frontend -- +[Komponenten] on {Gero} {Julius} {Lukas} lasts 15 days +[API-Anbindung] on {Gero} {Lukas} lasts 4 days + +'Backend +[Service-Schicht (Daten durchreichen)] starts at [Controller-Schicht]'s end +[Datenbank aufsetzen] starts at [Model-Paket]'s end +[Authentifizierung] starts at [Controller-Schicht]'s end +[DataAccess-Schicht] starts at [Datenbank aufsetzen]'s end +[Util-Paket (RSSParser)] starts at [Datenbank aufsetzen]'s end +[Service-Schicht (Geschäftslogik)] starts at [DataAccess-Schicht]'s end +[Util-Paket (CleanCronJob)] starts at [DataAccess-Schicht]'s end +'Frontend +[API-Anbindung] starts at [DataAccess-Schicht]'s end + +@endgantt \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/gantt-reality.puml b/21-implementierungsheft-kolloquium/assets/gantt-reality.puml new file mode 100644 index 0000000..f726c56 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/gantt-reality.puml @@ -0,0 +1,39 @@ +@startgantt + +printscale daily zoom 5 +project starts on 2023-01-30 + +-- Backend -- +[Controller-Schicht] on {Immanuel} lasts 3 days +[Model-Paket] on {Daniel} lasts 3 days +[Datenbank aufsetzen] on {Immanuel} lasts 6 days +[Util-Paket (RSSParser)] on {Daniel} {Lukas} lasts 32 days +[DAO-Interfaces] on {Julius} {Immanuel} lasts 6 days +[Authentifizierung] on {Immanuel} lasts 13 days +[Service-Schicht mit Datenzugriff] on {Julius} lasts 14 days +[Util-Paket (CleanCronJob)] on {Daniel} lasts 2 days +[Docker] on {Daniel} lasts 12 days +[EMailService] on {Gero} lasts 1 days +-- Frontend -- +[Komponenten] on {Gero} {Julius} lasts 15 days +[Mehrsprachigkeit] on {Lukas} lasts 5 days +[Router] on {Gero} lasts 1 days +[API-Anbindung] on {Gero} {Lukas} lasts 28 days +[Error-Handling] on {Gero} lasts 5 days + +'Backend +[Datenbank aufsetzen] starts at [Model-Paket]'s end +[Util-Paket (RSSParser)] starts at [Datenbank aufsetzen]'s end +[DAO-Interfaces] starts at [Datenbank aufsetzen]'s end +[Authentifizierung] starts at [DAO-Interfaces]'s end +[Service-Schicht mit Datenzugriff] starts at [DAO-Interfaces]'s end +[Util-Paket (CleanCronJob)] starts at [DAO-Interfaces]'s end +[Docker] starts at [Util-Paket (CleanCronJob)]'s end +[EMailService] starts 2023-02-14 +'Frontend +[Mehrsprachigkeit] starts 2023-02-01 +[Router] starts at [Mehrsprachigkeit]'s end +[API-Anbindung] starts at [Router]'s end +[Error-Handling] starts 2023-02-05 + +@endgantt \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/help.png b/21-implementierungsheft-kolloquium/assets/help.png new file mode 100644 index 0000000..39a1b84 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/help.png differ diff --git a/21-implementierungsheft-kolloquium/assets/lastupdate.png b/21-implementierungsheft-kolloquium/assets/lastupdate.png new file mode 100644 index 0000000..e9b7f5c Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/lastupdate.png differ diff --git a/21-implementierungsheft-kolloquium/assets/logo.svg b/21-implementierungsheft-kolloquium/assets/logo.svg new file mode 100644 index 0000000..1609066 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/logo.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Podcast Synchronisation made Efficient + + + + diff --git a/21-implementierungsheft-kolloquium/assets/navbar.png b/21-implementierungsheft-kolloquium/assets/navbar.png new file mode 100644 index 0000000..dd9f8e8 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/navbar.png differ diff --git a/21-implementierungsheft-kolloquium/assets/password-margin.png b/21-implementierungsheft-kolloquium/assets/password-margin.png new file mode 100644 index 0000000..d9d4fa3 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/password-margin.png differ diff --git a/21-implementierungsheft-kolloquium/assets/password.png b/21-implementierungsheft-kolloquium/assets/password.png new file mode 100644 index 0000000..68248a0 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/password.png differ diff --git a/21-implementierungsheft-kolloquium/assets/subscription.png b/21-implementierungsheft-kolloquium/assets/subscription.png new file mode 100644 index 0000000..58a84f9 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/subscription.png differ -- cgit v1.2.3