diff --git a/Jenkinsfile b/Jenkinsfile index 1b3c0a4ea09bd67f178de8e1efcfc97d57854f59..797871e69d0bf22a4e404ba46eff9ca3f8307a50 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -341,20 +341,26 @@ Void setAnsibleKubeConfig() { Void checkoutProvisioningRepo(String stage) { withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) { dir(stage) { - sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/mgm/provisioning.git' + sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/mgm/provisioning.git' - // todo remove git checkout - //dir('provisioning') { - // sh 'git checkout ozg-2552-jenkins-e2e' - //} + if (env.BRANCH_NAME == 'release') { + dir('provisioning') { + sh 'git checkout release' + } + } } } } Void copyTestEnvironmentToDev(stage) { dir("${stage}/provisioning") { - def devEnvFile = "playbook/inventory/versions/dev.yml" - def testEnvFile = "playbook/inventory/versions/test.yml" + def devEnvFile = "inventories/group_vars/dev/versions" + def testEnvFile = "inventories/group_vars/test/versions" + + if (env.BRANCH_NAME == 'release') { + devEnvFile = "playbook/inventory/versions/dev.yml" + testEnvFile = "playbook/inventory/versions/test.yml" + } def devVersions = readYaml file: devEnvFile def testVersions = readYaml file: testEnvFile @@ -368,7 +374,11 @@ Void copyTestEnvironmentToDev(stage) { Void editEnvironemntVersion(String stage, String imageTag, Boolean isEa) { dir("${stage}/provisioning") { - def editFile = "playbook/inventory/versions/dev.yml" + def editFile = "inventories/group_vars/dev/versions" + + if (env.BRANCH_NAME == 'release') { + editFile = "playbook/inventory/versions/dev.yml" + } def devVersions = readYaml file: editFile @@ -380,8 +390,6 @@ Void editEnvironemntVersion(String stage, String imageTag, Boolean isEa) { devVersions.versions.goofy.image.tag = imageTag writeYaml file: editFile, data: devVersions, overwrite: true - - sh "cat ${editFile}" } } @@ -395,7 +403,12 @@ String getSpringProfile(Boolean isEa) { Void setupEaEnvironment(String stage) { dir("${stage}/provisioning") { - def editFile = "playbook/inventory/group_vars/all.yml" + def editFile = "inventories/group_vars/all/env" + + if (env.BRANCH_NAME == 'release') { + editFile = "playbook/inventory/group_vars/all.yml" + } + def groupVars = readYaml file: editFile groupVars.kop_einheitlicher_ansprechpartner = true @@ -406,7 +419,12 @@ Void setupEaEnvironment(String stage) { Void setPlutoDatabasePassword(String stage) { dir("${stage}/provisioning") { - def editFile = "playbook/inventory/versions/dev.yml" + def editFile = "inventories/group_vars/dev/versions" + + if (env.BRANCH_NAME == 'release') { + editFile = "playbook/inventory/versions/dev.yml" + } + def devVars = readYaml file: editFile devVars.values.pluto.database.password = "XnHhfznNWg65NNd" @@ -427,7 +445,7 @@ Void rolloutKopStack(String bezeichner, String stage) { "install_fs_adapter":false, \ "external_db_enabled":false}""" - sh "ansible-playbook playbook/rollout.yml --extra-vars '${ansibleVars}'" + sh "ansible-playbook playbooks/rollout.yml --extra-vars '${ansibleVars}'" } } } @@ -446,7 +464,7 @@ Void addKeycloakGroups(String bezeichner, String stage) { }""" dir("${stage}/provisioning") { - sh "ansible-playbook playbook/add-keycloak-group.yml --extra-vars '${ansibleVars}'" + sh "ansible-playbook playbooks/add-keycloak-group.yml --extra-vars '${ansibleVars}'" } } } @@ -466,7 +484,7 @@ Void addKeycloakUser(String bezeichner, String stage) { }""" dir("${stage}/provisioning") { - sh "ansible-playbook playbook/add-keycloak-user.yml --extra-vars '${ansibleVars}'" + sh "ansible-playbook playbooks/add-keycloak-user.yml --extra-vars '${ansibleVars}'" } } } @@ -479,7 +497,7 @@ Void deleteKopStack(String bezeichner, String stage) { "kop_env":"dev", \ "kop_bezeichner":${bezeichner}}""" - sh "ansible-playbook playbook/delete-commune.yml --extra-vars '${ansibleVars}'" + sh "ansible-playbook playbooks/delete-commune.yml --extra-vars '${ansibleVars}'" } } } diff --git a/goofy-client/.gitignore b/goofy-client/.gitignore index 92abdb41d7fa17686e58d3ccf2fcb843520c83e7..29e0a78dc708bcd148daaa6b8c960be1b03544e4 100644 --- a/goofy-client/.gitignore +++ b/goofy-client/.gitignore @@ -26,7 +26,7 @@ test-report.xml # IDE - VSCode .vscode/* -!.vscode/settings.json +!.vscode/user-settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json diff --git a/goofy-client/angular.json b/goofy-client/angular.json index 15cbd85cda3cb44f19ed835425974b5093c59d1b..defe2a61c3ccbc8cc46373d4f05e3d9ba15c8fca 100644 --- a/goofy-client/angular.json +++ b/goofy-client/angular.json @@ -800,6 +800,58 @@ }, "tags": [] }, + "user-settings": { + "projectType": "library", + "root": "libs/user-settings", + "sourceRoot": "libs/user-settings/src", + "prefix": "goofy-client", + "architect": { + "test": { + "builder": "@nrwl/jest:jest", + "outputs": ["coverage/libs/user-settings"], + "options": { + "jestConfig": "libs/user-settings/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": [ + "libs/user-settings/**/*.ts", + "libs/user-settings/**/*.html" + ] + } + } + }, + "tags": [] + }, + "user-settings-shared": { + "projectType": "library", + "root": "libs/user-settings-shared", + "sourceRoot": "libs/user-settings-shared/src", + "prefix": "goofy-client", + "architect": { + "test": { + "builder": "@nrwl/jest:jest", + "outputs": ["coverage/libs/user-settings-shared"], + "options": { + "jestConfig": "libs/user-settings-shared/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "builder": "@nrwl/linter:eslint", + "options": { + "lintFilePatterns": [ + "libs/user-settings-shared/**/*.ts", + "libs/user-settings-shared/**/*.html" + ] + } + } + }, + "tags": [] + }, "vorgang": { "$schema": "../../node_modules/nx/schemas/project-schema.json", "projectType": "library", diff --git a/goofy-client/libs/api-root-shared/src/lib/api-root.linkrel.ts b/goofy-client/libs/api-root-shared/src/lib/api-root.linkrel.ts index 8000a325a35eb37f6e7a3df85aa1381ae2aae312..986aaff9c951d9dd86635790a61096a7a24b05ce 100644 --- a/goofy-client/libs/api-root-shared/src/lib/api-root.linkrel.ts +++ b/goofy-client/libs/api-root-shared/src/lib/api-root.linkrel.ts @@ -4,5 +4,6 @@ export enum ApiRootLinkRel { SEARCH = 'search', SEARCH_MY_VORGAENGE = 'searchMyVorgaenge', SEARCH_USER_PROFILES = 'search-user-profiles', - DOWNLOAD_TOKEN = 'downloadToken' + DOWNLOAD_TOKEN = 'downloadToken', + CURRENT_USER = 'currentUser' } \ No newline at end of file diff --git a/goofy-client/libs/navigation/src/lib/header-container/header-container.component.html b/goofy-client/libs/navigation/src/lib/header-container/header-container.component.html index 6c154413f00bbf66382205fede99a5854ba3474d..b1849bcf80fc12db4abd88f5e7cf5135b8ec4776 100644 --- a/goofy-client/libs/navigation/src/lib/header-container/header-container.component.html +++ b/goofy-client/libs/navigation/src/lib/header-container/header-container.component.html @@ -1,7 +1,5 @@ <goofy-client-header - [darkMode]="darkMode$ | async" [navigationCollapse]="navigationCollapse$ | async" (toggleMenuEvent)="toggleNavigation($event)" - (logoutEmitter)="logout()" - (darkModeEmitter)="changeColorMode($event)"> + (logoutEmitter)="logout()"> </goofy-client-header> diff --git a/goofy-client/libs/navigation/src/lib/header-container/header-container.component.ts b/goofy-client/libs/navigation/src/lib/header-container/header-container.component.ts index 62ee403fd64eaa32008e16c64506ff62d84666c8..1ac12eadeda9850e6c1a9ac4f24b74114a2b91ac 100644 --- a/goofy-client/libs/navigation/src/lib/header-container/header-container.component.ts +++ b/goofy-client/libs/navigation/src/lib/header-container/header-container.component.ts @@ -1,8 +1,7 @@ -import { DOCUMENT } from '@angular/common'; -import { Component, Inject, Renderer2 } from '@angular/core'; -import { Observable } from 'rxjs'; -import { AppService, localStorageDark } from '@goofy-client/app-shared'; +import { Component } from '@angular/core'; +import { AppService } from '@goofy-client/app-shared'; import { OAuthService } from 'angular-oauth2-oidc'; +import { Observable } from 'rxjs'; @Component({ selector: 'goofy-client-header-container', @@ -12,40 +11,18 @@ import { OAuthService } from 'angular-oauth2-oidc'; export class HeaderContainerComponent { navigationCollapse$: Observable<boolean>; - darkMode$: Observable<boolean>; constructor( private appService: AppService, - private renderer: Renderer2, private authService: OAuthService, - @Inject(DOCUMENT) private document: Document, ) { this.navigationCollapse$ = this.appService.getNavigationCollapse(); - this.darkMode$ = this.appService.getDarkMode(); - - this.subscribeToDarkMode(); - } - - private subscribeToDarkMode(): void { - this.darkMode$.subscribe(darkMode => darkMode ? this.addClass(localStorageDark) : this.removeClass(localStorageDark)); - } - - private addClass(styleClass: string): void { - this.renderer.addClass(this.document.body, styleClass); - } - - private removeClass(styleClass: string): void { - this.renderer.removeClass(this.document.body, styleClass); } toggleNavigation(isToggle: boolean): void { this.appService.setNavigationCollapse(isToggle); } - changeColorMode(darkMode: boolean): void { - this.appService.setDarkMode(darkMode); - } - logout(): void { this.authService.logOut(); } diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html index 134e2eb48e0fc15ec4a0acefda7fcb6d1a787974..c78c10edbecd236a8aade17986afee2b91bb5f00 100644 --- a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html +++ b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html @@ -15,7 +15,7 @@ <div class="right"> - <goofy-client-settings [darkMode]="darkMode" (darkModeEmitter)="darkModeEmitter.emit($event)"></goofy-client-settings> + <goofy-client-user-settings-container></goofy-client-user-settings-container> <!-- TODO: User Profile in eigene Componente auslagern --> <button mat-icon-button [matMenuTriggerFor]="accountMenu" class="big-button" aria-label="Benutzerkonto" data-test-id="user-icon-button" diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.spec.ts b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.spec.ts index 91c6f922af079ff0e8ffbaf4c983bf507f95096f..95d1a9f99f545a4a2e68d68ddfddb3285d697027 100644 --- a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.spec.ts +++ b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.spec.ts @@ -5,7 +5,6 @@ import { UserIconComponent } from '@goofy-client/user-profile'; import { VorgangSearchContainerComponent } from '@goofy-client/vorgang-shared-ui'; import { MockComponent } from 'ng-mocks'; import { HeaderComponent } from './header.component'; -import { SettingsComponent } from './settings/settings.component'; describe('HeaderComponent', () => { let component: HeaderComponent; @@ -19,7 +18,6 @@ describe('HeaderComponent', () => { ], declarations: [ HeaderComponent, - MockComponent(SettingsComponent), MockComponent(VorgangSearchContainerComponent), MockComponent(UserIconComponent) ] diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.ts b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.ts index afe00d897777519bc30950381a274dbef22f842c..a36d7cf9fcac0bb9118819fe7a20f149ee1b72db 100644 --- a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.ts +++ b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.ts @@ -7,10 +7,8 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; }) export class HeaderComponent { - @Input() darkMode: boolean; @Input() navigationCollapse: boolean; @Output() public toggleMenuEvent: EventEmitter<boolean> = new EventEmitter<boolean>() - @Output() public darkModeEmitter: EventEmitter<boolean> = new EventEmitter<boolean>() @Output() public logoutEmitter: EventEmitter<void> = new EventEmitter<void>() } diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.html b/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.html deleted file mode 100644 index 328924bb484a216e2585348bd05c347a55c1d031..0000000000000000000000000000000000000000 --- a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.html +++ /dev/null @@ -1,17 +0,0 @@ -<goofy-client-icon-button-with-spinner - icon="settings" - toolTip="Einstellungen" - [matMenuTriggerFor]="settingsMenu" - (menuOpened)="toggle.focus()"> -</goofy-client-icon-button-with-spinner> - -<mat-menu #settingsMenu="matMenu" > - <div class="menu-container" - (click)="$event.stopPropagation()"> - - <mat-slide-toggle color="primary" #toggle - [checked]="darkMode" - (change)="darkModeEmitter.emit($event.checked)">Dark Mode - </mat-slide-toggle> - </div> -</mat-menu> diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.spec.ts b/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.spec.ts deleted file mode 100644 index 1f02826aa49ea3fcd1ad2dcd2cc715a9dd4dc826..0000000000000000000000000000000000000000 --- a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { UiModule } from '@goofy-client/ui'; -import { SettingsComponent } from './settings.component'; - -describe('SettingsComponent', () => { - let component: SettingsComponent; - let fixture: ComponentFixture<SettingsComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - UiModule - ], - declarations: [SettingsComponent] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(SettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.ts b/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.ts deleted file mode 100644 index 7bc806c429c5aff658956b1fd6947f143a2384c5..0000000000000000000000000000000000000000 --- a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; - -@Component({ - selector: 'goofy-client-settings', - templateUrl: './settings.component.html', - styleUrls: ['./settings.component.scss'] -}) -export class SettingsComponent { - - @Input() darkMode: boolean; - - @Output() public darkModeEmitter: EventEmitter<boolean> = new EventEmitter(); -} \ No newline at end of file diff --git a/goofy-client/libs/navigation/src/lib/navigation.module.ts b/goofy-client/libs/navigation/src/lib/navigation.module.ts index 78ac06b69038c5a945f9eeb24bea8af80aef88d0..6c8d699901ecfb1d27ce898e9e2031d8b6be0c77 100644 --- a/goofy-client/libs/navigation/src/lib/navigation.module.ts +++ b/goofy-client/libs/navigation/src/lib/navigation.module.ts @@ -3,11 +3,11 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { UiModule } from '@goofy-client/ui'; import { UserProfileModule } from '@goofy-client/user-profile'; +import { UserSettingsModule } from '@goofy-client/user-settings'; import { VorgangSharedUiModule } from '@goofy-client/vorgang-shared-ui'; import { BuildInfoComponent } from './build-info/build-info.component'; import { HeaderContainerComponent } from './header-container/header-container.component'; import { HeaderComponent } from './header-container/header/header.component'; -import { SettingsComponent } from './header-container/header/settings/settings.component'; import { NavigationContainerComponent } from './navigation-container/navigation-container.component'; import { AllVorgaengeNavigationItemComponent } from './navigation/all-vorgaenge-navigation-item/all-vorgaenge-navigation-item.component'; import { MyVorgaengeNavigationItemComponent } from './navigation/my-vorgaenge-navigation-item/my-vorgaenge-navigation-item.component'; @@ -18,23 +18,23 @@ import { NavigationComponent } from './navigation/navigation.component'; BuildInfoComponent, HeaderComponent, NavigationComponent, - SettingsComponent, HeaderContainerComponent, AllVorgaengeNavigationItemComponent, MyVorgaengeNavigationItemComponent, - NavigationContainerComponent + NavigationContainerComponent, ], imports: [ CommonModule, UiModule, RouterModule, VorgangSharedUiModule, - UserProfileModule + UserProfileModule, + UserSettingsModule, ], exports: [ BuildInfoComponent, NavigationContainerComponent, - HeaderContainerComponent - ] + HeaderContainerComponent, + ], }) -export class NavigationModule { } +export class NavigationModule {} diff --git a/goofy-client/libs/postfach/src/lib/postfach.module.ts b/goofy-client/libs/postfach/src/lib/postfach.module.ts index 73e5115ee1bee6a0e9e982106aca8e73328180bb..51873ed48f38cdce082bd9ff7acd6df11c9c46e0 100644 --- a/goofy-client/libs/postfach/src/lib/postfach.module.ts +++ b/goofy-client/libs/postfach/src/lib/postfach.module.ts @@ -27,7 +27,7 @@ const routes: Routes = [ { path: 'postfach', component: PostfachPageContainerComponent, - title: 'Postfach zum Vorgang | Alfa' + title: 'Nachrichten zum Vorgang | Alfa' } ]; diff --git a/goofy-client/libs/user-profile-shared/src/lib/user-profile.linkrel.ts b/goofy-client/libs/user-profile-shared/src/lib/user-profile.linkrel.ts index 4104372b1212e0fdc02175fe2ed998e8515afd02..73fa1ade46d27383297e8da2b2c3f1a8144cd841 100644 --- a/goofy-client/libs/user-profile-shared/src/lib/user-profile.linkrel.ts +++ b/goofy-client/libs/user-profile-shared/src/lib/user-profile.linkrel.ts @@ -1,3 +1,7 @@ export enum UserProfileListLinkRel { USER_PROFILE_LIST = 'userProfileList' +} + +export enum UserProfileLinkRel { + SETTINGS = 'settings' } \ No newline at end of file diff --git a/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.spec.ts b/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.spec.ts index 8bda09c735375376e7164654c0cbf99d35c01276..90347798b1f7fed959be287e47a0f5b8d625c96b 100644 --- a/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.spec.ts +++ b/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.spec.ts @@ -6,8 +6,8 @@ import { ResourceFactory } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { createVorgangResource } from 'libs/vorgang-shared/test/vorgang'; -import { createUserProfileResource } from '../../test/user-profile'; -import { UserProfileResource } from './user-profile.model'; +import { createUserProfileListResource, createUserProfileResource } from '../../test/user-profile'; +import { UserProfileListResource, UserProfileResource } from './user-profile.model'; import { UserProfileRepository } from './user-profile.repository'; describe('User profile repository', () => { @@ -62,6 +62,11 @@ describe('User profile repository', () => { describe('search', () => { const searchBy: string = 'SearchMe'; + const userProfileListResource: UserProfileListResource = createUserProfileListResource(); + + beforeEach(() => { + resourceWrapper.get.mockReturnValue(cold('a', { a: userProfileListResource })); + }) it('should call resource factory', () => { repository.search(apiRoot, searchBy); @@ -76,5 +81,34 @@ describe('User profile repository', () => { expect(repository.buildSearchByUrl).toHaveBeenCalledWith(apiRoot, searchBy); }) + + it('should return result', () => { + const result = repository.search(apiRoot, searchBy); + + expect(result).not.toBeNull(); + expect(result).toBeObservable(hot('a', { a: userProfileListResource })); + }) + }) + + describe('getCurrentUser', () => { + + it('should call resource Factory', () => { + repository.getCurrentUser(apiRoot); + + expect(resourceFactory.from).toHaveBeenCalledWith(apiRoot); + }) + + it('should call get', () => { + repository.getCurrentUser(apiRoot); + + expect(resourceWrapper.get).toHaveBeenCalledWith(ApiRootLinkRel.CURRENT_USER); + }) + + it('should return result', () => { + const result = repository.getCurrentUser(apiRoot); + + expect(result).not.toBeNull(); + expect(result).toBeObservable(hot('a', { a: userProfile })); + }) }) }) \ No newline at end of file diff --git a/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.ts b/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.ts index fc4b664f89c47233192df93ebbf8ed721ce71b12..f2c00f2de50c0bddc7d1d1b824895300cd406bb5 100644 --- a/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.ts +++ b/goofy-client/libs/user-profile-shared/src/lib/user-profile.repository.ts @@ -17,6 +17,10 @@ export class UserProfileRepository { return this.resourceFactory.from(resource).get(linkRel); } + public getCurrentUser(apiRootResource: ApiRootResource): Observable<UserProfileResource> { + return this.resourceFactory.from(apiRootResource).get(ApiRootLinkRel.CURRENT_USER); + } + @SkipInterceptor() public search(apiRoot: ApiRootResource, searchBy: string): Observable<UserProfileListResource> { return this.resourceFactory.fromId(this.buildSearchByUrl(apiRoot, searchBy)).get(); diff --git a/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.spec.ts b/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.spec.ts index 907e09c4255f11136747e727c7cdcd7cfa9ad89b..ea98a04cdb6d5ab2e475ce288d58d92b64717a17 100644 --- a/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.spec.ts +++ b/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.spec.ts @@ -40,6 +40,29 @@ describe('UserProfileService', () => { expect(service).toBeTruthy(); }) + describe('getCurrentUser', () => { + + const apiRootResource = createApiRootResource(["currentUser"]); + + it('should call repository', (done) => { + apiRootFacade.getApiRoot.mockReturnValue(of(createStateResource(apiRootResource))); + + service.getCurrentUser().subscribe(() => { + expect(repository.getCurrentUser).toHaveBeenCalledWith(apiRootResource); + done(); + }) + }) + + it('should call apiRoot facade', (done) => { + apiRootFacade.getApiRoot.mockReturnValue(of(createStateResource(apiRootResource))); + + service.getCurrentUser().subscribe(() => { + expect(apiRootFacade.getApiRoot).toHaveBeenCalled(); + done(); + }) + }) + }) + describe('load user profile', () => { const linkRel: string = VorgangHeaderLinkRel.ASSIGNED_TO; @@ -94,8 +117,6 @@ describe('UserProfileService', () => { describe('createStateResourceForUpdate', () => { - const linkRel: string = VorgangHeaderLinkRel.ASSIGNED_TO; - beforeEach(() => { service.updateUserProfile = jest.fn(); }) diff --git a/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.ts b/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.ts index 833069c79479588526257058ed0e80cf78c8fc25..e82eacaaabf0849ec1ee239dbaf8a7d717cabc17 100644 --- a/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.ts +++ b/goofy-client/libs/user-profile-shared/src/lib/user-profile.service.ts @@ -18,6 +18,7 @@ export class UserProfileService { private userProfiles = {}; private userProfileSearchList: BehaviorSubject<StateResource<UserProfileListResource>> = new BehaviorSubject(createEmptyStateResource<UserProfileListResource>()); private userProfileSearchVisibility: BehaviorSubject<boolean> = new BehaviorSubject(false); + private currentUser: BehaviorSubject<StateResource<UserProfileResource>> = new BehaviorSubject(createEmptyStateResource<UserProfileResource>()); private navigationSubscription: Subscription; @@ -66,6 +67,24 @@ export class UserProfileService { return this.userProfiles[uri].asObservable(); } + public getCurrentUser(): Observable<StateResource<UserProfileResource>> { + doIfLoadingRequired(this.currentUser.getValue(), () => { + this.loadCurrentUser().subscribe(currentUser => { + this.currentUser.next(createStateResource(currentUser)); + }); + }); + + return this.currentUser; + } + + private loadCurrentUser(): Observable<UserProfileResource> { + return this.apiRootFacade.getApiRoot().pipe( + filter(stateResource => stateResource.loaded), + mergeMap(stateResource => this.repository.getCurrentUser(stateResource.resource)), + first() + ) + } + createIfNotExist(uri: ResourceUri): void { if (isNil(this.userProfiles[uri])) this.userProfiles[uri] = new BehaviorSubject(createEmptyStateResource<UserProfileResource>()); } diff --git a/goofy-client/libs/user-settings-shared/.eslintrc.json b/goofy-client/libs/user-settings-shared/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..28f19b630511beab4ad3ff61ffce5a45f7430728 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nrwl/nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "goofyClient", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "goofy-client", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nrwl/nx/angular-template"], + "rules": {} + } + ] +} diff --git a/goofy-client/libs/user-settings-shared/README.md b/goofy-client/libs/user-settings-shared/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ecf49402144e0af5e146cd92f6cdfb84d8e3b27d --- /dev/null +++ b/goofy-client/libs/user-settings-shared/README.md @@ -0,0 +1,7 @@ +# user-settings-shared + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test user-settings-shared` to execute the unit tests. diff --git a/goofy-client/libs/user-settings-shared/jest.config.ts b/goofy-client/libs/user-settings-shared/jest.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..30536d499921584f63ac37a1ff20e268104c0727 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'user-settings-shared', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], + globals: { + 'ts-jest': { + tsconfig: '<rootDir>/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + }, + coverageDirectory: '../../coverage/libs/user-settings-shared', + transform: { + '^.+\\.(ts|mjs|js|html)$': 'jest-preset-angular', + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/goofy-client/libs/user-settings-shared/src/index.ts b/goofy-client/libs/user-settings-shared/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..235538f5c463f0d4fb95039df9d22d2544b60eb4 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/index.ts @@ -0,0 +1,10 @@ +export * from './lib/+state/user-settings.actions'; +export * from './lib/+state/user-settings.facade'; +export * from './lib/+state/user-settings.models'; +export * from './lib/+state/user-settings.reducer'; +export * from './lib/+state/user-settings.selectors'; +export * from './lib/user-settings-shared.module'; +export * from './lib/user-settings.linkrel'; +export * from './lib/user-settings.model'; +export * from './lib/user-settings.service'; + diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.actions.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.actions.ts new file mode 100644 index 0000000000000000000000000000000000000000..012f5a12ecd628fbb071b37ffe31d3c4a849315f --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.actions.ts @@ -0,0 +1,55 @@ +import { ApiError } from '@goofy-client/tech-shared'; +import { UserProfileResource } from '@goofy-client/user-profile-shared'; +import { ActionCreator, createAction, props } from '@ngrx/store'; +import { TypedAction } from '@ngrx/store/src/models'; +import { UserSettings, UserSettingsResource } from '../user-settings.model'; + +//TODO centralize ActionCreate +export interface UserSettingsActionCreator<T> extends ActionCreator<string, (props: T) => T & TypedAction<string>> { } +export interface TypedActionCreator extends ActionCreator<string, () => TypedAction<string>> { } + +export interface UserProfileAction { + currentUser: UserProfileResource +} + +export interface LoadedUserSettingsAction { + userSettings: UserSettingsResource +} + +export interface SetUserSettingsAction { + userSettings: UserSettings +} + +export interface ApiErrorAction { + apiError: ApiError +} + +export const loadUserSettings: UserSettingsActionCreator<UserProfileAction> = createAction( + '[UserSettings] Load UserSettings', + props<{ currentUser: UserProfileResource }>() +); + +export const loadUserSettingsSuccess: UserSettingsActionCreator<LoadedUserSettingsAction> = createAction( + '[UserSettings/API] Load UserSettings Success', + props<{ userSettings: UserSettingsResource }>() +); + +export const loadUserSettingsFailure: UserSettingsActionCreator<ApiErrorAction> = createAction( + '[UserSettings/API] Load UserSettings Failure', + props<ApiErrorAction>() +); + +export const setUserSettings: UserSettingsActionCreator<SetUserSettingsAction> = createAction( + '[UserSettings] Set UserSettings', + props<{ userSettings: UserSettings }>() +); + +export const setUserSettingsSuccess: UserSettingsActionCreator<LoadedUserSettingsAction> = createAction( + '[UserSettings/API] Set UserSettings Success', + props<{ userSettings: UserSettingsResource }>() +); + +export const setUserSettingsFailure: UserSettingsActionCreator<ApiErrorAction> = createAction( + '[UserSettings/API] Set UserSettings Failure', + props<ApiErrorAction>() +); diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.effects.spec.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..610edb8a004d9e7e3f0aa780a9bc6772403edcd9 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.effects.spec.ts @@ -0,0 +1,123 @@ +import { TestBed } from '@angular/core/testing'; +import { ApiError, createStateResource } from '@goofy-client/tech-shared'; +import { Mock, mock } from '@goofy-client/test-utils'; +import { UserProfileResource } from '@goofy-client/user-profile-shared'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { Action } from '@ngrx/store'; +import { provideMockStore } from '@ngrx/store/testing'; +import { NxModule } from '@nrwl/angular'; +import { cold, hot } from 'jest-marbles'; +import { createApiError } from 'libs/tech-shared/test/error'; +import { createUserProfileResource } from 'libs/user-profile-shared/test/user-profile'; +import { createUserSettings, createUserSettingsResource } from 'libs/user-settings-shared/test/user-settings'; +import { Observable, of } from 'rxjs'; +import { UserSettings, UserSettingsResource } from '../user-settings.model'; +import { UserSettingsRepository } from '../user-settings.repository'; +import * as UserSettingsActions from './user-settings.actions'; +import { UserSettingsEffects } from './user-settings.effects'; +import { UserSettingsFacade } from './user-settings.facade'; + +describe('UserSettingsEffects', () => { + let actions: Observable<Action>; + let effects: UserSettingsEffects; + + const userSettingsFacade: Mock<UserSettingsFacade> = mock(UserSettingsFacade); + const userSettingsRepository: Mock<UserSettingsRepository> = mock(UserSettingsRepository); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NxModule.forRoot()], + providers: [ + UserSettingsEffects, + provideMockActions(() => actions), + provideMockStore(), + { + provide: UserSettingsRepository, + useValue: userSettingsRepository + }, + { + provide: UserSettingsFacade, + useValue: userSettingsFacade + } + ], + }); + + effects = TestBed.inject(UserSettingsEffects); + }); + + describe('loadUserSettings', () => { + const currentUser: UserProfileResource = createUserProfileResource(); + const action = UserSettingsActions.loadUserSettings({ currentUser }); + const userSettings: UserSettingsResource = createUserSettingsResource(); + + it('should call repository', () => { + actions = of(action); + + effects.loadUserSettings$.subscribe(); + + expect(userSettingsRepository.getUserSettings).toHaveBeenCalledWith(currentUser); + }) + + it('should dispatch success action', () => { + userSettingsRepository.getUserSettings.mockReturnValue(of(userSettings)); + + actions = hot('-a', { a: action }); + + const expected = cold('-b', { b: UserSettingsActions.loadUserSettingsSuccess({ userSettings }) }); + expect(effects.loadUserSettings$).toBeObservable(expected); + }) + + it('should dispatch error action', () => { + const apiError: ApiError = createApiError() + const error = { error: { error: apiError } }; + const errorResponse = cold('-#', {}, error); + userSettingsRepository.getUserSettings = jest.fn(() => errorResponse); + + const expected = cold('--b', { b: UserSettingsActions.loadUserSettingsFailure({ apiError }) }); + actions = hot('-a', { a: action }); + + expect(effects.loadUserSettings$).toBeObservable(expected); + }) + }) + + describe('set user settings', () => { + + const userSettingsResource: UserSettingsResource = createUserSettingsResource(); + const userSettings: UserSettings = createUserSettings(); + const action = UserSettingsActions.setUserSettings({ userSettings }); + + beforeEach(() => { + userSettingsFacade.getUserSettings.mockReturnValue(of(createStateResource(userSettingsResource))); + userSettingsRepository.setUserSettings.mockReturnValue(of(userSettingsResource)); + }) + + it('should call repository', () => { + actions = of(action); + + effects.setUserSettings$.subscribe(); + + expect(userSettingsRepository.setUserSettings).toHaveBeenCalledWith(userSettingsResource, userSettings); + }) + + it('should dispatch success action', () => { + userSettingsRepository.setUserSettings.mockReturnValue(of(userSettingsResource)); + + actions = hot('-a-|', { a: action }); + + const expected = cold('-a-|', { a: UserSettingsActions.setUserSettingsSuccess({ userSettings: userSettingsResource }) }); + expect(effects.setUserSettings$).toBeObservable(expected); + }) + + it('should dispatch error action', () => { + const apiError: ApiError = createApiError() + const error = { error: { error: apiError } }; + const errorResponse = cold('-#', {}, error); + userSettingsRepository.setUserSettings = jest.fn(() => errorResponse); + + const expected = cold('--b', { b: UserSettingsActions.setUserSettingsFailure({ apiError }) }); + actions = hot('-a', { a: action }); + + expect(effects.setUserSettings$).toBeObservable(expected); + }) + }) +}); diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.effects.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.effects.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ecf7fad1b20fa49b197011e8c6aba69d220597e --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.effects.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { getApiErrorFromHttpErrorResponse } from '@goofy-client/tech-shared'; +import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; +import { of } from 'rxjs'; +import { catchError, map, switchMap } from 'rxjs/operators'; +import { UserSettingsRepository } from '../user-settings.repository'; +import * as UserSettingsActions from './user-settings.actions'; +import { UserSettingsFacade } from './user-settings.facade'; + +@Injectable() +export class UserSettingsEffects { + + constructor( + private readonly actions$: Actions, + private readonly repository: UserSettingsRepository, + private readonly userSettingsFacade: UserSettingsFacade + ) { } + + loadUserSettings$ = createEffect(() => + this.actions$.pipe( + ofType(UserSettingsActions.loadUserSettings), + switchMap((action) => this.repository.getUserSettings(action.currentUser).pipe( + map(userSettings => UserSettingsActions.loadUserSettingsSuccess({ userSettings })), + catchError(error => of(UserSettingsActions.loadUserSettingsFailure({ apiError: getApiErrorFromHttpErrorResponse(error) }))) + )) + ) + ) + + setUserSettings$ = createEffect(() => + this.actions$.pipe( + ofType(UserSettingsActions.setUserSettings), + concatLatestFrom(() => this.userSettingsFacade.getUserSettings()), + switchMap(([action, userSettingsResource]) => this.repository.setUserSettings(userSettingsResource.resource, action.userSettings).pipe( + map(userSettings => UserSettingsActions.setUserSettingsSuccess({ userSettings })), + catchError(error => of(UserSettingsActions.setUserSettingsFailure({ apiError: getApiErrorFromHttpErrorResponse(error) }))) + )) + ) + ) +} \ No newline at end of file diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.facade.spec.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.facade.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..6430fd793bebf2dbe96d971fe307920e2f793121 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.facade.spec.ts @@ -0,0 +1,57 @@ +import { createStateResource, StateResource } from '@goofy-client/tech-shared'; +import { Mock, mock, useFromMock } from '@goofy-client/test-utils'; +import { UserProfileResource } from '@goofy-client/user-profile-shared'; +import { Store } from '@ngrx/store'; +import { createUserProfileResource } from 'libs/user-profile-shared/test/user-profile'; +import { createUserSettings, createUserSettingsResource } from 'libs/user-settings-shared/test/user-settings'; +import { Subject } from 'rxjs'; +import { UserSettings, UserSettingsResource } from '../user-settings.model'; +import * as UserSettingsActions from './user-settings.actions'; +import { UserSettingsFacade } from './user-settings.facade'; + + +describe('UserSettingsFacade', () => { + const store: Mock<Store> = mock(Store); + let facade: UserSettingsFacade; + let userSettingsSubj: Subject<StateResource<UserSettingsResource>>; + + beforeEach(() => { + userSettingsSubj = new Subject(); + store.select.mockReturnValue(userSettingsSubj); + + facade = new UserSettingsFacade(useFromMock(store)); + }); + + describe('loadUserSettings', () => { + it('should dispatch "loadUserSettings"', () => { + const currentUser: UserProfileResource = createUserProfileResource(); + + facade.loadUserSettings(currentUser); + + expect(store.dispatch).toHaveBeenCalledWith(UserSettingsActions.loadUserSettings({ currentUser })) + }) + }) + + describe('getUserSettings', () => { + it('should return selected value', (done) => { + const expected: StateResource<UserSettingsResource> = createStateResource(createUserSettingsResource()); + + facade.getUserSettings().subscribe(userSettings => { + expect(userSettings).toBe(expected); + done(); + }); + + userSettingsSubj.next(expected); + }) + }) + + describe('setUserSettings', () => { + it('should dispatch "setUserSettings"', () => { + const userSettings: UserSettings = createUserSettings(); + + facade.setUserSettings(userSettings); + + expect(store.dispatch).toHaveBeenCalledWith(UserSettingsActions.setUserSettings({ userSettings })); + }) + }) +}); diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.facade.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.facade.ts new file mode 100644 index 0000000000000000000000000000000000000000..7fd419459644778fcc0186b3d1ea37600ec40f17 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.facade.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { StateResource } from '@goofy-client/tech-shared'; +import { UserProfileResource } from '@goofy-client/user-profile-shared'; +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { UserSettings, UserSettingsResource } from '../user-settings.model'; +import * as UserSettingsAction from './user-settings.actions'; +import * as UserSettingsSelectors from './user-settings.selectors'; + +@Injectable() +export class UserSettingsFacade { + + constructor(private readonly store: Store) { } + + public loadUserSettings(currentUser: UserProfileResource): void { + this.store.dispatch(UserSettingsAction.loadUserSettings({ currentUser })); + } + + public getUserSettings(): Observable<StateResource<UserSettingsResource>> { + return this.store.select(UserSettingsSelectors.userSettings) + } + + public setUserSettings(userSettings: UserSettings): void { + this.store.dispatch(UserSettingsAction.setUserSettings({ userSettings })) + } +} diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.models.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.models.ts new file mode 100644 index 0000000000000000000000000000000000000000..4eacd56495066d2f8f7f7f3ae6d135d47014c6d6 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.models.ts @@ -0,0 +1,7 @@ +/** + * Interface for the 'UserSettings' data + */ +export interface UserSettingsEntity { + id: string | number; // Primary ID + name: string; +} diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.reducer.spec.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c887fe17b046ec9affbcbc0fd2ad7c308df1fae6 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.reducer.spec.ts @@ -0,0 +1,100 @@ +import { ApiError, createStateResource } from '@goofy-client/tech-shared'; +import { Action } from '@ngrx/store'; +import { createUserProfileResource } from 'libs/user-profile-shared/test/user-profile'; +import { createUserSettings, createUserSettingsResource } from 'libs/user-settings-shared/test/user-settings'; +import { createApiError } from '../../../../tech-shared/test/error'; +import { UserSettingsResource } from '../user-settings.model'; +import * as UserSettingsActions from './user-settings.actions'; +import { initialUserSettingsState, userSettingsReducer, UserSettingsState } from './user-settings.reducer'; + +describe('User Settings Reducer', () => { + + describe('unknown action', () => { + + it('should return current state', () => { + const action: Action = {} as Action; + + const result = userSettingsReducer(initialUserSettingsState, action); + + expect(result).toBe(initialUserSettingsState); + }) + }) + + describe('loadUserSettings', () => { + describe('on "loadUserSettings" action', () => { + it('should set loading to true', () => { + const action: Action = UserSettingsActions.loadUserSettings({ currentUser: createUserProfileResource() }); + + const state: UserSettingsState = userSettingsReducer(initialUserSettingsState, action); + + expect(state.userSettings.loading).toBeTruthy(); + }) + }) + }) + + describe('on "loadUserSettingsSuccess"', () => { + const userSettings: UserSettingsResource = createUserSettingsResource(); + const action: Action = UserSettingsActions.loadUserSettingsSuccess({ userSettings }); + + it('should set loaded resource', () => { + const state: UserSettingsState = userSettingsReducer(initialUserSettingsState, action); + + expect(state.userSettings).toEqual(createStateResource(userSettings)); + }) + + it('should has property "notificationsSendFor"', () => { + const state: UserSettingsState = userSettingsReducer(initialUserSettingsState, action); + + expect(state.userSettings).toHaveProperty('resource.notificationsSendFor'); + }) + }) + + describe('on "loadUserSettingsFailure"', () => { + it('should set apiError', () => { + const apiError: ApiError = createApiError(); + const action: Action = UserSettingsActions.loadUserSettingsFailure({ apiError }); + + const state: UserSettingsState = userSettingsReducer(initialUserSettingsState, action); + + expect(state.userSettings.error).toStrictEqual(apiError); + }) + }) + + describe('setUserSettings', () => { + + describe('on "setUserSettings" action', () => { + + it('should set state resource to loading', () => { + const action: Action = UserSettingsActions.setUserSettings({ userSettings: createUserSettings() }); + + const state: UserSettingsState = userSettingsReducer(initialUserSettingsState, action); + + expect(state.userSettings.loading).toBeTruthy(); + }) + }) + + describe('on "setUserSettingsSuccess" action', () => { + + const userSettingsResource: UserSettingsResource = createUserSettingsResource(); + const action: Action = UserSettingsActions.setUserSettingsSuccess({ userSettings: userSettingsResource }); + + it('should set loaded resource', () => { + const state: UserSettingsState = userSettingsReducer(initialUserSettingsState, action); + + expect(state.userSettings).toEqual(createStateResource(userSettingsResource)); + }) + }) + + describe('on "setUserSettingsFailure" action', () => { + + it('should set API Error', () => { + const apiError: ApiError = createApiError(); + const action: Action = UserSettingsActions.setUserSettingsFailure({ apiError }); + + const state: UserSettingsState = userSettingsReducer(initialUserSettingsState, action); + + expect(state.userSettings.error).toStrictEqual(apiError); + }) + }) + }) +}) \ No newline at end of file diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.reducer.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.reducer.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ee7052bcaac3e302d41150bf0c2a6394a2c57ac --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.reducer.ts @@ -0,0 +1,52 @@ +import { createEmptyStateResource, createErrorStateResource, createStateResource, StateResource } from '@goofy-client/tech-shared'; +import { Action, ActionReducer, createReducer, on } from '@ngrx/store'; +import { UserSettingsResource } from '../user-settings.model'; +import * as UserSettingsActions from './user-settings.actions'; +import { ApiErrorAction, LoadedUserSettingsAction } from './user-settings.actions'; + +export const SETTINGS_FEATURE_KEY = 'UserSettingsState'; + +export interface UserSettingsPartialState { + readonly [SETTINGS_FEATURE_KEY]: UserSettingsState; +} + +export interface UserSettingsState { + userSettings: StateResource<UserSettingsResource> +} + +export const initialUserSettingsState: UserSettingsState = { + userSettings: createEmptyStateResource() +}; + +const reducer: ActionReducer<UserSettingsState, Action> = createReducer( + initialUserSettingsState, + on(UserSettingsActions.loadUserSettings, (state: UserSettingsState): UserSettingsState => ({ + ...state, + userSettings: { ...state.userSettings, loading: true } + })), + on(UserSettingsActions.loadUserSettingsSuccess, (state: UserSettingsState, action: LoadedUserSettingsAction) => ({ + ...state, + userSettings: createStateResource<UserSettingsResource>(action.userSettings) + })), + on(UserSettingsActions.loadUserSettingsFailure, (state: UserSettingsState, action: ApiErrorAction) => ({ + ...state, + userSettings: createErrorStateResource<UserSettingsResource>(action.apiError) + })), + + on(UserSettingsActions.setUserSettings, (state: UserSettingsState): UserSettingsState => ({ + ...state, + userSettings: { ...state.userSettings, loading: true } + })), + on(UserSettingsActions.setUserSettingsSuccess, (state: UserSettingsState, action: LoadedUserSettingsAction) => ({ + ...state, + userSettings: createStateResource<UserSettingsResource>(action.userSettings) + })), + on(UserSettingsActions.setUserSettingsFailure, (state: UserSettingsState, action: ApiErrorAction) => ({ + ...state, + userSettings: createErrorStateResource<UserSettingsResource>(action.apiError) + })) +); + +export function userSettingsReducer(state: UserSettingsState, action: Action): UserSettingsState { + return reducer(state, action); +} diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.selectors.spec.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.selectors.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..60ae1f81e3a3cf22b87c43b112363abf016b7178 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.selectors.spec.ts @@ -0,0 +1,28 @@ +import { createStateResource, StateResource } from '@goofy-client/tech-shared'; +import { createUserSettingsResource } from 'libs/user-settings-shared/test/user-settings'; +import { UserSettingsResource } from '../user-settings.model'; +import { initialUserSettingsState, UserSettingsPartialState } from './user-settings.reducer'; +import * as UserSettingsSelectors from './user-settings.selectors'; + +describe('UserSettings Selectors', () => { + + let state: UserSettingsPartialState; + + const userSettings: StateResource<UserSettingsResource> = createStateResource(createUserSettingsResource()); + + beforeEach(() => { + state = { + UserSettingsState: { + ...initialUserSettingsState, + userSettings + } + } + }) + + describe('userSettingsSelector', () => { + + it('should return userSettings from state', () => { + expect(UserSettingsSelectors.userSettings.projector(state.UserSettingsState)).toEqual(userSettings); + }) + }) +}) \ No newline at end of file diff --git a/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.selectors.ts b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.selectors.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f5096de99e181cb89e81213a99d6d1931df6d45 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/+state/user-settings.selectors.ts @@ -0,0 +1,8 @@ +import { StateResource } from '@goofy-client/tech-shared'; +import { createFeatureSelector, createSelector, MemoizedSelector } from '@ngrx/store'; +import { UserSettingsResource } from '../user-settings.model'; +import { SETTINGS_FEATURE_KEY, UserSettingsState } from './user-settings.reducer'; + +export const getUserSettingsState: MemoizedSelector<object, UserSettingsState> = createFeatureSelector<UserSettingsState>(SETTINGS_FEATURE_KEY); + +export const userSettings: MemoizedSelector<UserSettingsState, StateResource<UserSettingsResource>> = createSelector(getUserSettingsState, (state: UserSettingsState) => state.userSettings); \ No newline at end of file diff --git a/goofy-client/libs/user-settings-shared/src/lib/user-settings-shared.module.ts b/goofy-client/libs/user-settings-shared/src/lib/user-settings-shared.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..1d384a3a513634113441b1dc9ddb2267274fd1da --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/user-settings-shared.module.ts @@ -0,0 +1,24 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { EffectsModule } from '@ngrx/effects'; +import { StoreModule } from '@ngrx/store'; +import { UserSettingsEffects } from './+state/user-settings.effects'; +import { UserSettingsFacade } from './+state/user-settings.facade'; +import * as fromUserSettings from './+state/user-settings.reducer'; + +@NgModule({ + imports: [ + CommonModule, + StoreModule.forFeature( + fromUserSettings.SETTINGS_FEATURE_KEY, + fromUserSettings.userSettingsReducer + ), + EffectsModule.forFeature([UserSettingsEffects]), + StoreModule.forFeature( + fromUserSettings.SETTINGS_FEATURE_KEY, + fromUserSettings.userSettingsReducer + ), + ], + providers: [UserSettingsFacade], +}) +export class UserSettingsSharedModule { } diff --git a/goofy-client/libs/user-settings-shared/src/lib/user-settings.linkrel.ts b/goofy-client/libs/user-settings-shared/src/lib/user-settings.linkrel.ts new file mode 100644 index 0000000000000000000000000000000000000000..45eb1e78d78171a1b3579ded1e35a01557be01da --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/user-settings.linkrel.ts @@ -0,0 +1,3 @@ +export enum UserSettingsLinkRel { + EDIT = 'edit', +} diff --git a/goofy-client/libs/user-settings-shared/src/lib/user-settings.model.ts b/goofy-client/libs/user-settings-shared/src/lib/user-settings.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..2407b80dd1e27e3da5c8fc476e6e793c13ceef2d --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/user-settings.model.ts @@ -0,0 +1,12 @@ +import { Resource } from '@ngxp/rest'; + +export enum NotificationsSendFor { + NONE = "NONE", + ALL = "ALL" +} + +export interface UserSettings { + notificationsSendFor: string; +} + +export interface UserSettingsResource extends UserSettings, Resource { } diff --git a/goofy-client/libs/user-settings-shared/src/lib/user-settings.repository.spec.ts b/goofy-client/libs/user-settings-shared/src/lib/user-settings.repository.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce24b371135751e376ffb67b94962c7876e2fa3d --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/user-settings.repository.spec.ts @@ -0,0 +1,78 @@ +import { mock, useFromMock } from '@goofy-client/test-utils'; +import { UserProfileLinkRel, UserProfileResource } from '@goofy-client/user-profile-shared'; +import { ResourceFactory } from '@ngxp/rest'; +import { cold, hot } from 'jest-marbles'; +import { createUserProfileResource } from 'libs/user-profile-shared/test/user-profile'; +import { createUserSettings, createUserSettingsResource } from 'libs/user-settings-shared/test/user-settings'; +import { UserSettingsLinkRel } from './user-settings.linkrel'; +import { UserSettings, UserSettingsResource } from './user-settings.model'; +import { UserSettingsRepository } from './user-settings.repository'; + +describe('UserSettingsRepository', () => { + let repository: UserSettingsRepository; + let resourceFactory = mock(ResourceFactory); + let resourceWrapper = { get: jest.fn(), patch: jest.fn() }; + + beforeEach(() => { + repository = new UserSettingsRepository(useFromMock(resourceFactory)); + + resourceFactory.from.mockReturnValue(resourceWrapper); + }) + + describe('get user settings', () => { + const userProfileResource: UserProfileResource = createUserProfileResource(); + const responseResource: UserProfileResource = createUserProfileResource(); + + beforeEach(() => { + resourceWrapper.get.mockReturnValue(hot('a', { a: responseResource })); + }) + + it('should call resourceFactory with resource', () => { + repository.getUserSettings(userProfileResource); + + expect(resourceFactory.from).toHaveBeenCalledWith(userProfileResource); + }) + + it('should call resourceWrapper with link', () => { + repository.getUserSettings(userProfileResource); + + expect(resourceWrapper.get).toHaveBeenCalledWith(UserProfileLinkRel.SETTINGS) + }) + + it('should return result', () => { + let result = repository.getUserSettings(userProfileResource); + + expect(result).not.toBeNull(); + expect(result).toBeObservable(cold('a', { a: responseResource })); + }) + }) + + describe('set user settings', () => { + const userSettingsResource: UserSettingsResource = createUserSettingsResource(); + const responseResource: UserProfileResource = createUserProfileResource(); + const userSettings: UserSettings = createUserSettings(); + + beforeEach(() => { + resourceWrapper.patch.mockReturnValue(hot('a', { a: responseResource })); + }) + + it('should call resourceFactory with resource', () => { + repository.setUserSettings(userSettingsResource, userSettings); + + expect(resourceFactory.from).toHaveBeenCalledWith(userSettingsResource); + }) + + it('should call resourceWrapper with link', () => { + repository.setUserSettings(userSettingsResource, userSettings); + + expect(resourceWrapper.patch).toHaveBeenCalledWith(UserSettingsLinkRel.EDIT, userSettings) + }) + + it('should return result', () => { + let result = repository.setUserSettings(userSettingsResource, userSettings); + + expect(result).not.toBeNull(); + expect(result).toBeObservable(cold('a', { a: responseResource })); + }) + }) +}) \ No newline at end of file diff --git a/goofy-client/libs/user-settings-shared/src/lib/user-settings.repository.ts b/goofy-client/libs/user-settings-shared/src/lib/user-settings.repository.ts new file mode 100644 index 0000000000000000000000000000000000000000..a142e67c8d4ba45dc3a8ce37a1aafcc70fb9efe9 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/user-settings.repository.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { UserProfileLinkRel, UserProfileResource } from '@goofy-client/user-profile-shared'; +import { ResourceFactory } from '@ngxp/rest'; +import { Observable } from 'rxjs'; +import { UserSettingsLinkRel } from './user-settings.linkrel'; +import { UserSettings, UserSettingsResource } from './user-settings.model'; + +@Injectable({ providedIn: 'root' }) +export class UserSettingsRepository { + + constructor(private resourceFactory: ResourceFactory) { } + + public getUserSettings(userProfileResource: UserProfileResource): Observable<UserSettingsResource> { + return this.resourceFactory.from(userProfileResource).get(UserProfileLinkRel.SETTINGS); + } + + public setUserSettings(userSettingsResource: UserSettingsResource, userSettings: UserSettings): Observable<UserSettingsResource> { + return this.resourceFactory.from(userSettingsResource).patch(UserSettingsLinkRel.EDIT, userSettings); + } +} \ No newline at end of file diff --git a/goofy-client/libs/user-settings-shared/src/lib/user-settings.service.spec.ts b/goofy-client/libs/user-settings-shared/src/lib/user-settings.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ec12c7e2c0d69cc04bba9923d0c1b3183690040c --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/user-settings.service.spec.ts @@ -0,0 +1,71 @@ +import { createEmptyStateResource, createStateResource, StateResource } from '@goofy-client/tech-shared'; +import { mock, Mock, useFromMock } from '@goofy-client/test-utils'; +import { UserProfileResource, UserProfileService } from '@goofy-client/user-profile-shared'; +import { cold, hot } from 'jest-marbles'; +import { createUserProfileResource } from 'libs/user-profile-shared/test/user-profile'; +import { createUserSettings, createUserSettingsResource } from 'libs/user-settings-shared/test/user-settings'; +import { Observable } from 'rxjs'; +import { UserSettingsFacade } from './+state/user-settings.facade'; +import { UserSettings, UserSettingsResource } from './user-settings.model'; +import { UserSettingsService } from './user-settings.service'; + +describe('UserSettingsService', () => { + let facade: Mock<UserSettingsFacade>; + let userProfileService: Mock<UserProfileService>; + + let service: UserSettingsService; + + beforeEach(() => { + facade = mock(UserSettingsFacade); + userProfileService = mock(UserProfileService); + + service = new UserSettingsService(useFromMock(facade), useFromMock(userProfileService)); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('get user settings', () => { + const userProfileResource: UserProfileResource = createUserProfileResource(); + const stateResourceCU = createStateResource(createUserSettingsResource()); + const emptyStateResourceCU = createEmptyStateResource<UserProfileResource>(true); + + beforeEach(() => { + const stateResourceUP: StateResource<UserProfileResource> = createStateResource(userProfileResource); + userProfileService.getCurrentUser.mockReturnValue(hot('a', { a: stateResourceUP })); + + facade.getUserSettings.mockReturnValue(hot('-a', { a: stateResourceCU })); + }) + + it('should call userProfileService.getCurrentUser', () => { + service.getUserSettings(); + + expect(userProfileService.getCurrentUser).toHaveBeenCalled(); + }) + + it.skip('should load user settings from facade', (done) => { + service.getUserSettings().subscribe(() => { + expect(facade.loadUserSettings).toHaveBeenCalledWith(userProfileResource); + done(); + }); + }) + + it('should return user settings', () => { + const userSettings$: Observable<StateResource<UserSettingsResource>> = service.getUserSettings(); + + expect(userSettings$).toBeObservable(cold('ab', { a: emptyStateResourceCU, b: stateResourceCU })); + }) + + }) + + describe('set UserSettings', () => { + it('should call set method in facade', () => { + const userSettings: UserSettings = createUserSettings(); + + service.setUserSettings(userSettings); + + expect(facade.setUserSettings).toHaveBeenCalledWith(userSettings); + }) + }) +}); diff --git a/goofy-client/libs/user-settings-shared/src/lib/user-settings.service.ts b/goofy-client/libs/user-settings-shared/src/lib/user-settings.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..29ddc9c17fdd4091ba6d76b1518abefac4a90705 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/lib/user-settings.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { createEmptyStateResource, doIfLoadingRequired, StateResource } from '@goofy-client/tech-shared'; +import { UserProfileService } from '@goofy-client/user-profile-shared'; +import { combineLatest, Observable } from 'rxjs'; +import { map, startWith, tap } from 'rxjs/operators'; +import { UserSettingsFacade } from './+state/user-settings.facade'; +import { UserSettings, UserSettingsResource } from './user-settings.model'; + +@Injectable({ providedIn: 'root' }) +export class UserSettingsService { + + constructor( + private userSettingsFacade: UserSettingsFacade, + private userProfileService: UserProfileService, + ) { } + + public getUserSettings(): Observable<StateResource<UserSettingsResource>> { + return combineLatest([this.userProfileService.getCurrentUser(), this.userSettingsFacade.getUserSettings()]).pipe( + tap(([userProfile, userSettings]) => doIfLoadingRequired(userSettings, () => { + if (userProfile.resource) { + this.userSettingsFacade.loadUserSettings(userProfile.resource); + } + })), + map(([, userSettings]) => userSettings), + startWith(createEmptyStateResource<UserSettingsResource>(true)) + ); + } + + public setUserSettings(userSettings: UserSettings): void { + this.userSettingsFacade.setUserSettings(userSettings); + } +} diff --git a/goofy-client/libs/user-settings-shared/src/test-setup.ts b/goofy-client/libs/user-settings-shared/src/test-setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..ffbe3a552ab7905a5e86fad93e87c7bc1d3abed7 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/src/test-setup.ts @@ -0,0 +1,14 @@ +import 'jest-preset-angular/setup-jest'; + +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +getTestBed().resetTestEnvironment(); +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), + { teardown: { destroyAfterEach: false } } +); diff --git a/goofy-client/libs/user-settings-shared/test/user-settings.ts b/goofy-client/libs/user-settings-shared/test/user-settings.ts new file mode 100644 index 0000000000000000000000000000000000000000..5e10134a7c4e119acf4627c25934a3904ea0edbc --- /dev/null +++ b/goofy-client/libs/user-settings-shared/test/user-settings.ts @@ -0,0 +1,13 @@ +import { toResource } from 'libs/tech-shared/test/resource'; +import { NotificationsSendFor, UserSettings, UserSettingsResource } from '../src/lib/user-settings.model'; + +export function createUserSettingsResource(linkRelations: string[] = []): UserSettingsResource { + return toResource(createUserSettings(), linkRelations); +} + +export function createUserSettings(): UserSettings { + return { + notificationsSendFor: NotificationsSendFor.ALL + } +} + diff --git a/goofy-client/libs/user-settings-shared/tsconfig.json b/goofy-client/libs/user-settings-shared/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..3d3a2a0498aafea3513d0d915e99d166d927fbe0 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "target": "es2020" + } +} diff --git a/goofy-client/libs/user-settings-shared/tsconfig.lib.json b/goofy-client/libs/user-settings-shared/tsconfig.lib.json new file mode 100644 index 0000000000000000000000000000000000000000..a2a53880f8274e73d8c264e347047cbd8c1f1cf8 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts"], + "include": ["**/*.ts"] +} diff --git a/goofy-client/libs/user-settings-shared/tsconfig.spec.json b/goofy-client/libs/user-settings-shared/tsconfig.spec.json new file mode 100644 index 0000000000000000000000000000000000000000..a6c95196f6ff5249f87bfce3a787b520a26d8a67 --- /dev/null +++ b/goofy-client/libs/user-settings-shared/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/goofy-client/libs/user-settings/.eslintrc.json b/goofy-client/libs/user-settings/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..28f19b630511beab4ad3ff61ffce5a45f7430728 --- /dev/null +++ b/goofy-client/libs/user-settings/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": [ + "plugin:@nrwl/nx/angular", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "goofyClient", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "goofy-client", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nrwl/nx/angular-template"], + "rules": {} + } + ] +} diff --git a/goofy-client/libs/user-settings/README.md b/goofy-client/libs/user-settings/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9c5e3d238e40e7a12d88bf3a608b8e8211bb8047 --- /dev/null +++ b/goofy-client/libs/user-settings/README.md @@ -0,0 +1,7 @@ +# user-settings + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test user-settings` to execute the unit tests. diff --git a/goofy-client/libs/user-settings/jest.config.ts b/goofy-client/libs/user-settings/jest.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..770771ca188c94a6c6ed7526893d002b9df67976 --- /dev/null +++ b/goofy-client/libs/user-settings/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'user-settings', + preset: '../../jest.preset.js', + setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], + globals: { + 'ts-jest': { + tsconfig: '<rootDir>/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + }, + coverageDirectory: '../../coverage/libs/user-settings', + transform: { + '^.+\\.(ts|mjs|js|html)$': 'jest-preset-angular', + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/goofy-client/libs/user-settings/src/index.ts b/goofy-client/libs/user-settings/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba9f3be48abca118db96c0fabea35ac298590866 --- /dev/null +++ b/goofy-client/libs/user-settings/src/index.ts @@ -0,0 +1,4 @@ +export * from './lib/user-settings-container/user-settings-container.component'; +export * from './lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component'; +export * from './lib/user-settings.module'; + diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.html b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..7cff27785933adb77a43e9155b796fd3efb9d9d8 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.html @@ -0,0 +1,16 @@ +<goofy-client-icon-button-with-spinner icon="settings" toolTip="Einstellungen" + [matMenuTriggerFor]="settingsMenu"> +</goofy-client-icon-button-with-spinner> + +<mat-menu #settingsMenu="matMenu"> + <div class="menu-container" (click)="$event.stopPropagation()"> + <goofy-client-user-settings-email-benachrichtigung + [userSettings$]="userSettings$" + (valueChanged)="changeNotificationsSendFor($event)" + ></goofy-client-user-settings-email-benachrichtigung> + <goofy-client-user-settings-darkmode + [darkMode$]="darkMode$" + (darkModeEmitter)="changeColorMode($event)" + ></goofy-client-user-settings-darkmode> + </div> +</mat-menu> diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.scss b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.scss similarity index 50% rename from goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.scss rename to goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.scss index 982d28ee457184d07da4cb2d8271bc6fd7a8f576..e15dab43b6e1199516db6bca6fe3c9ad2574d47e 100644 --- a/goofy-client/libs/navigation/src/lib/header-container/header/settings/settings.component.scss +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.scss @@ -2,6 +2,7 @@ margin: 12px 16px; } -goofy-client-icon-button-with-spinner { +goofy-client-icon-button-with-spinner, +mat-slide-toggle { display: block; } diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.spec.ts b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d11f9673e482b3b32a9c60b9f203382028210041 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.spec.ts @@ -0,0 +1,77 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatMenuModule } from '@angular/material/menu'; +import { AppService } from '@goofy-client/app-shared'; +import { mock } from '@goofy-client/test-utils'; +import { IconButtonWithSpinnerComponent } from '@goofy-client/ui'; +import { UserSettingsService } from '@goofy-client/user-settings-shared'; +import { NotificationsSendFor } from 'libs/user-settings-shared/src/lib/user-settings.model'; +import { MockComponent } from 'ng-mocks'; +import { BehaviorSubject } from 'rxjs'; +import { UserSettingsContainerComponent } from './user-settings-container.component'; +import { UserSettingsDarkmodeComponent } from './user-settings-darkmode/user-settings-darkmode.component'; +import { UserSettingsEmailBenachrichtigungComponent } from './user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component'; + +describe('UserSettingsContainerComponent', () => { + let component: UserSettingsContainerComponent; + let fixture: ComponentFixture<UserSettingsContainerComponent>; + + const darkModeSubj: BehaviorSubject<boolean> = new BehaviorSubject(false); + const appService = { ...mock(AppService), getDarkMode: () => darkModeSubj }; + const userSettingsService = { ...mock(UserSettingsService), setUserSettings: jest.fn() }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatMenuModule], + declarations: [ + UserSettingsContainerComponent, + MockComponent(UserSettingsDarkmodeComponent), + MockComponent(UserSettingsEmailBenachrichtigungComponent), + MockComponent(IconButtonWithSpinnerComponent) + ], + providers: [ + { + provide: AppService, + useValue: appService, + }, + { + provide: UserSettingsService, + useValue: userSettingsService + } + ], + }); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UserSettingsContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('change color mode', () => { + + it('should call app service', () => { + component.changeColorMode(true); + + expect(appService.setDarkMode).toHaveBeenCalledWith(true); + }) + }) + + describe('change notifications send for', () => { + + it('should call service with ALL', () => { + component.changeNotificationsSendFor(true); + + expect(userSettingsService.setUserSettings).toHaveBeenCalledWith({ notificationsSendFor: NotificationsSendFor.ALL }); + }) + + it('should call service with NONE', () => { + component.changeNotificationsSendFor(false); + + expect(userSettingsService.setUserSettings).toHaveBeenCalledWith({ notificationsSendFor: NotificationsSendFor.NONE }); + }) + }) +}); diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.ts b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ebe11daa5b45cdd973c475cd500f065bc3581d2 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-container.component.ts @@ -0,0 +1,61 @@ +import { DOCUMENT } from '@angular/common'; +import { Component, Inject, OnInit, Renderer2 } from '@angular/core'; +import { AppService, localStorageDark } from '@goofy-client/app-shared'; +import { StateResource } from '@goofy-client/tech-shared'; +import { UserSettingsService } from '@goofy-client/user-settings-shared'; +import { NotificationsSendFor, UserSettings, UserSettingsResource } from 'libs/user-settings-shared/src/lib/user-settings.model'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'goofy-client-user-settings-container', + templateUrl: './user-settings-container.component.html', + styleUrls: ['./user-settings-container.component.scss'], +}) +export class UserSettingsContainerComponent implements OnInit { + darkMode$: Observable<boolean> = of(true); + userSettings$: Observable<StateResource<UserSettingsResource>>; + + constructor( + private renderer: Renderer2, + private appService: AppService, + private userSettingsService: UserSettingsService, + @Inject(DOCUMENT) private document: Document + ) { + this.darkMode$ = this.appService.getDarkMode(); + + this.subscribeToDarkMode(); + } + + ngOnInit(): void { + this.userSettings$ = this.userSettingsService.getUserSettings(); + } + + private subscribeToDarkMode(): void { + this.darkMode$.subscribe((darkMode) => + darkMode + ? this.addClass(localStorageDark) + : this.removeClass(localStorageDark) + ); + } + + private addClass(styleClass: string): void { + this.renderer.addClass(this.document.body, styleClass); + } + + private removeClass(styleClass: string): void { + this.renderer.removeClass(this.document.body, styleClass); + } + + changeColorMode(darkMode: boolean): void { + this.appService.setDarkMode(darkMode); + } + + changeNotificationsSendFor(toggleValue: boolean): void { + this.userSettingsService.setUserSettings(this.createUserSettings(toggleValue)); + } + + private createUserSettings(toggleValue: boolean): UserSettings { + const notificationsSendFor: NotificationsSendFor = toggleValue ? NotificationsSendFor.ALL : NotificationsSendFor.NONE; + return { notificationsSendFor }; + } +} \ No newline at end of file diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.html b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.html new file mode 100644 index 0000000000000000000000000000000000000000..07a006c57442bef409a85437b10a68fde2f5c680 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.html @@ -0,0 +1,6 @@ +<mat-slide-toggle + color="primary" + [checked]="darkMode$ | async" + (change)="darkModeEmitter.emit($event.checked)" + >Dark Mode +</mat-slide-toggle> diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.scss b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.spec.ts b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f333224d4ba89c44239501605915654454dcfa27 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { UiModule } from '@goofy-client/ui'; +import { UserSettingsDarkmodeComponent } from './user-settings-darkmode.component'; + +describe('UserSettingsDarkmodeComponent', () => { + let component: UserSettingsDarkmodeComponent; + let fixture: ComponentFixture<UserSettingsDarkmodeComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UiModule], + declarations: [UserSettingsDarkmodeComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(UserSettingsDarkmodeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.ts b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..651132cf8620c19aa5b6375164247bf99ddb0384 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-darkmode/user-settings-darkmode.component.ts @@ -0,0 +1,14 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'goofy-client-user-settings-darkmode', + templateUrl: './user-settings-darkmode.component.html', + styleUrls: ['./user-settings-darkmode.component.scss'], +}) +export class UserSettingsDarkmodeComponent { + @Input() darkMode$: Observable<boolean> = of(true); + + @Output() public darkModeEmitter: EventEmitter<boolean> = + new EventEmitter(); +} diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.html b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.html new file mode 100644 index 0000000000000000000000000000000000000000..90823c5225e8680fc0f3c4e8f2d59ada0db9366e --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.html @@ -0,0 +1,8 @@ +<ng-container *ngIf="(userSettings$ | async)?.resource as userSettings"> + <mat-slide-toggle data-test-id="toggle-notifications-send-for" + color="primary" + matTooltip="Benachrichtigung per E-Mail bei Eingang eines Antrags" + [checked]="getToggleStatus(userSettings)" + (change)="valueChanged.emit($event.checked)">Benachrichtigung per E-Mail + </mat-slide-toggle> +</ng-container> \ No newline at end of file diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.scss b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..92276dfe14738302abcf74e348103b1dfb94c9fe --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.scss @@ -0,0 +1,3 @@ +mat-slide-toggle { + margin-bottom: 0.5rem; +} diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.spec.ts b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..84969ed2659f95fb7dd76e3fd5eb56be1719b2f2 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.spec.ts @@ -0,0 +1,70 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatRippleModule } from '@angular/material/core'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatSlideToggle } from '@angular/material/slide-toggle'; +import { createStateResource } from '@goofy-client/tech-shared'; + +import { dispatchEventFromFixture, mock } from '@goofy-client/test-utils'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { NotificationsSendFor } from 'libs/user-settings-shared/src/lib/user-settings.model'; +import { createUserSettings, createUserSettingsResource } from 'libs/user-settings-shared/test/user-settings'; +import { of } from 'rxjs'; +import { EventEmitter } from 'stream'; +import { UserSettingsEmailBenachrichtigungComponent } from './user-settings-email-benachrichtigung.component'; + +describe('UserSettingsEmailBenachrichtigungComponent', () => { + let component: UserSettingsEmailBenachrichtigungComponent; + let fixture: ComponentFixture<UserSettingsEmailBenachrichtigungComponent>; + + const toggle: string = getDataTestIdOf('toggle-notifications-send-for'); + const userSettings = createUserSettings(); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatMenuModule, MatRippleModule], + declarations: [UserSettingsEmailBenachrichtigungComponent, MatSlideToggle], + }).compileComponents(); + + fixture = TestBed.createComponent( + UserSettingsEmailBenachrichtigungComponent + ); + component = fixture.componentInstance; + component.userSettings$ = of(createStateResource(createUserSettingsResource())); + component.valueChanged = { ...<any>mock(EventEmitter), emit: jest.fn() }; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should show toggle', () => { + const element = fixture.nativeElement.querySelector(toggle); + + expect(element).toBeInstanceOf(HTMLElement); + }) + + describe('toggle status', () => { + it('should be set if userSettings.notificationsSendFor is ALL', () => { + userSettings.notificationsSendFor = NotificationsSendFor.ALL; + + const result: boolean = component.getToggleStatus(userSettings); + + expect(result).toBeTruthy(); + }) + + it('should not be set if userSettings.notificationsSendFor is NONE', () => { + userSettings.notificationsSendFor = NotificationsSendFor.NONE; + + const result: boolean = component.getToggleStatus(userSettings); + + expect(result).toBeFalsy(); + }) + + it('should emit value if toggled', () => { + dispatchEventFromFixture(fixture, toggle, 'change'); + + expect(component.valueChanged.emit).toHaveBeenCalled(); + }) + }) +}); diff --git a/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.ts b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..adb1df1f939759d85464ac9503323a94759f55c2 --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component.ts @@ -0,0 +1,18 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { StateResource } from '@goofy-client/tech-shared'; +import { NotificationsSendFor, UserSettings, UserSettingsResource } from '@goofy-client/user-settings-shared'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'goofy-client-user-settings-email-benachrichtigung', + templateUrl: './user-settings-email-benachrichtigung.component.html', + styleUrls: ['./user-settings-email-benachrichtigung.component.scss'], +}) +export class UserSettingsEmailBenachrichtigungComponent { + @Input() userSettings$: Observable<StateResource<UserSettingsResource>>; + @Output() valueChanged: EventEmitter<boolean> = new EventEmitter(); + + public getToggleStatus(userSettings: UserSettings): boolean { + return userSettings.notificationsSendFor === NotificationsSendFor.ALL; + } +} diff --git a/goofy-client/libs/user-settings/src/lib/user-settings.module.ts b/goofy-client/libs/user-settings/src/lib/user-settings.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..79c603fe449e56a1414bd9eec6ae65bc3aa9672e --- /dev/null +++ b/goofy-client/libs/user-settings/src/lib/user-settings.module.ts @@ -0,0 +1,25 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { UiModule } from '@goofy-client/ui'; +import { UserSettingsSharedModule } from '@goofy-client/user-settings-shared'; +import { UserSettingsContainerComponent } from './user-settings-container/user-settings-container.component'; +import { UserSettingsDarkmodeComponent } from './user-settings-container/user-settings-darkmode/user-settings-darkmode.component'; +import { UserSettingsEmailBenachrichtigungComponent } from './user-settings-container/user-settings-email-benachrichtigung/user-settings-email-benachrichtigung.component'; + +@NgModule({ + imports: [ + CommonModule, + UiModule, + UserSettingsSharedModule + ], + declarations: [ + UserSettingsContainerComponent, + UserSettingsEmailBenachrichtigungComponent, + UserSettingsDarkmodeComponent, + ], + exports: [ + UserSettingsContainerComponent, + UserSettingsEmailBenachrichtigungComponent, + ], +}) +export class UserSettingsModule {} diff --git a/goofy-client/libs/user-settings/src/test-setup.ts b/goofy-client/libs/user-settings/src/test-setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..08ef82b5d5efa87cb27257314075f6a16eb7a63e --- /dev/null +++ b/goofy-client/libs/user-settings/src/test-setup.ts @@ -0,0 +1,14 @@ +import 'jest-preset-angular/setup-jest'; + +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing'; + +getTestBed().resetTestEnvironment(); +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), + { teardown: { destroyAfterEach: false } } +); diff --git a/goofy-client/libs/user-settings/tsconfig.json b/goofy-client/libs/user-settings/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..3d3a2a0498aafea3513d0d915e99d166d927fbe0 --- /dev/null +++ b/goofy-client/libs/user-settings/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "target": "es2020" + } +} diff --git a/goofy-client/libs/user-settings/tsconfig.lib.json b/goofy-client/libs/user-settings/tsconfig.lib.json new file mode 100644 index 0000000000000000000000000000000000000000..a2a53880f8274e73d8c264e347047cbd8c1f1cf8 --- /dev/null +++ b/goofy-client/libs/user-settings/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts"], + "include": ["**/*.ts"] +} diff --git a/goofy-client/libs/user-settings/tsconfig.spec.json b/goofy-client/libs/user-settings/tsconfig.spec.json new file mode 100644 index 0000000000000000000000000000000000000000..a6c95196f6ff5249f87bfce3a787b520a26d8a67 --- /dev/null +++ b/goofy-client/libs/user-settings/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/goofy-client/libs/vorgang/src/lib/vorgang.module.ts b/goofy-client/libs/vorgang/src/lib/vorgang.module.ts index 97443528a9bf246bf26f24fceae2859787daf3ed..49f433b03c481402f1680915ed115f5170cd5c7b 100644 --- a/goofy-client/libs/vorgang/src/lib/vorgang.module.ts +++ b/goofy-client/libs/vorgang/src/lib/vorgang.module.ts @@ -20,7 +20,7 @@ const routes: Routes = [ { path: '', component: VorgangListPageContainerComponent, - title: 'Vorgang-Liste | Alfa' + title: 'Alle Vorgänge | Alfa' }, { path: 'search/:search', @@ -40,7 +40,7 @@ const routes: Routes = [ { path: 'vorgang/:vorgangWithEingangUrl', loadChildren: () => import('@goofy-client/vorgang-detail').then(m => m.VorgangDetailModule), - title: 'Vorgang-Detailseite | Alfa' + title: 'Details zum Vorgang | Alfa' }, ]; diff --git a/goofy-client/pom.xml b/goofy-client/pom.xml index 641ca104804688f78f5e3ab96b556a2dc59bedda..45e6b5587379e4660ece10d816b5561dd8bd22c4 100644 --- a/goofy-client/pom.xml +++ b/goofy-client/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>0.31.0-SNAPSHOT</version> + <version>0.32.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> diff --git a/goofy-client/proxy.conf.json b/goofy-client/proxy.conf.json index 4bc3d0b5262907841d1fcb13583bd2839e203860..34bc6ac9ced56d91af0347aeee18be29dae9ec09 100644 --- a/goofy-client/proxy.conf.json +++ b/goofy-client/proxy.conf.json @@ -7,5 +7,17 @@ }, "secure": false, "logLevel": "debug" + }, + "/usermanager/api": { + "target": { + "host": "localhost", + "port": 9092, + "protocol": "http:" + }, + "pathRewrite": { + "^/usermanager": "" + }, + "secure": false, + "logLevel": "debug" } } \ No newline at end of file diff --git a/goofy-client/tsconfig.base.json b/goofy-client/tsconfig.base.json index 873d101e70c97d2085574c4e33766f3275aac671..21cae37080c26111bd62cbc7c7f6c6314a54e33a 100644 --- a/goofy-client/tsconfig.base.json +++ b/goofy-client/tsconfig.base.json @@ -57,6 +57,10 @@ "@goofy-client/user-profile-shared": [ "libs/user-profile-shared/src/index.ts" ], + "@goofy-client/user-settings": ["libs/user-settings/src/index.ts"], + "@goofy-client/user-settings-shared": [ + "libs/user-settings-shared/src/index.ts" + ], "@goofy-client/vorgang": ["libs/vorgang/src/index.ts"], "@goofy-client/vorgang-detail": [ "libs/vorgang-detail/src/index.ts" diff --git a/goofy-server/pom.xml b/goofy-server/pom.xml index ba4e53191ef5670c93764305b9e4d0a8c80f00fe..24c74564ce454a3a77092d6d223c435fd550431e 100644 --- a/goofy-server/pom.xml +++ b/goofy-server/pom.xml @@ -7,7 +7,7 @@ <parent> <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>0.31.0-SNAPSHOT</version> + <version>0.32.0-SNAPSHOT</version> </parent> <artifactId>goofy-server</artifactId> diff --git a/pom.xml b/pom.xml index 6315b873457a7e59e27a0329eb9ad3ed0d470dc5..51643ab3571b1d4e1ee313fb5279b0915610573c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,14 +5,14 @@ <groupId>de.itvsh.ozg</groupId> <artifactId>goofy</artifactId> - <version>0.31.0-SNAPSHOT</version> + <version>0.32.0-SNAPSHOT</version> <name>Goofy Parent</name> <packaging>pom</packaging> <parent> <groupId>de.itvsh.kop.common</groupId> <artifactId>kop-common-parent</artifactId> - <version>1.1.5</version> + <version>1.2.1</version> </parent> <modules> @@ -24,7 +24,7 @@ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> - <pluto.version>0.31.0-SNAPSHOT</pluto.version> + <pluto.version>0.32.0-SNAPSHOT</pluto.version> <jsoup.version>1.15.1</jsoup.version> </properties>