diff --git a/goofy-client/apps/goofy-e2e/src/integration/wiedervorlage/wiedervorlage.erledigen.e2e-spec.ts b/goofy-client/apps/goofy-e2e/src/integration/wiedervorlage/wiedervorlage.erledigen.e2e-spec.ts index 6cd04391f3fc2307dad154fabf0a32fbc8f4887f..2f4e76762ebeed4706f4adeeaaff664fac0822ee 100644 --- a/goofy-client/apps/goofy-e2e/src/integration/wiedervorlage/wiedervorlage.erledigen.e2e-spec.ts +++ b/goofy-client/apps/goofy-e2e/src/integration/wiedervorlage/wiedervorlage.erledigen.e2e-spec.ts @@ -50,6 +50,7 @@ describe('Wiedervorlage', () => { exist(wiedervorlagePage.getSubnavigation()); }) + //TODO Anforderung unklärt - in OZG-296 gibt es dazu kein AC it('should navigate to vorgang detail after click on erledigen', () => { wiedervorlagePage.erledigen(); @@ -60,6 +61,7 @@ describe('Wiedervorlage', () => { containClass(wiedervorlage.getStatusDot(), 'erledigt'); }) }) + describe('wiedereroeffnen', () => { let wiedervorlage: WiedervorlageInVorgangE2EComponent; @@ -74,6 +76,7 @@ describe('Wiedervorlage', () => { exist(wiedervorlagePage.getSubnavigation()); }) + //TODO Anforderung unklärt - in OZG-296 gibt es dazu kein AC it('should navigate to vorgang detail after click on wiedereroeffnen', () => { wiedervorlagePage.wiedereroeffnen(); diff --git a/goofy-client/libs/command-shared/src/lib/command.repository.spec.ts b/goofy-client/libs/command-shared/src/lib/command.repository.spec.ts index 9a24dcd6a48d40479d1d9821aa80dc93ec5b4bd9..f5d8c55f9833902dff0921effa35006c36f3e69a 100644 --- a/goofy-client/libs/command-shared/src/lib/command.repository.spec.ts +++ b/goofy-client/libs/command-shared/src/lib/command.repository.spec.ts @@ -7,7 +7,7 @@ import { cold, hot } from 'jest-marbles'; import { createCommand, createCommandResource } from 'libs/command-shared/test/command'; import { createApiError } from 'libs/tech-shared/test/error'; import { toResource } from "libs/tech-shared/test/resource"; -import { throwError } from 'rxjs'; +import { of, throwError } from 'rxjs'; import { CommandLinkRel } from './command.linkrel'; import { Command, CommandResource, CommandResourceWithError, CommandStatus } from './command.model'; import { CommandRepository } from './command.repository'; @@ -106,7 +106,6 @@ describe('CommandRepository', () => { }) }) - //TODO: Prüfen, ob man sich der warning entledigen kann/oder anders testen kann describe('createCommand expecting apiError', () => { const apiError: ApiError = createApiError(); @@ -115,10 +114,11 @@ describe('CommandRepository', () => { beforeEach(() => { (<any>resourceWrapper).post.mockReturnValue(error); repository.getError = jest.fn(); + (<jest.Mock>repository.getError).mockReturnValue(of(null)); }) it('should return apiError', () => { - repository.createCommand(commandResource, faker.internet.url(), command).subscribe(); + var res = repository.createCommand(commandResource, faker.internet.url(), command).subscribe(); expect(repository.getError).toHaveBeenCalledWith(apiError); }) diff --git a/goofy-client/libs/command-shared/src/lib/command.repository.ts b/goofy-client/libs/command-shared/src/lib/command.repository.ts index 7ecd40ded609f6d40b7d964abbe479720189134d..78843996300dc717e45bedffad1456377a1911a5 100644 --- a/goofy-client/libs/command-shared/src/lib/command.repository.ts +++ b/goofy-client/libs/command-shared/src/lib/command.repository.ts @@ -8,13 +8,14 @@ import { Command, CommandResource, CommandResourceWithError, CommandStatus } fro @Injectable({ providedIn: 'root' }) export class CommandRepository { - constructor(private resourceFacotry: ResourceFactory) { } + constructor(private resourceFactory: ResourceFactory) { } public createCommand(resource: Resource, linkrel: string, command: Command): Observable<CommandResourceWithError> { - return this.resourceFacotry.from(resource).post(linkrel, command).pipe( - map(commandResource => <CommandResourceWithError>{ commandResource, apiError: null }), - catchError(apiError => this.getError(apiError)) - ); + return this.resourceFactory.from(resource).post(linkrel, command) + .pipe( + map(commandResource => <CommandResourceWithError>{ commandResource, apiError: null }), + catchError(apiError => this.getError(apiError)) + ); } getError(apiError: any): Observable<CommandResourceWithError> { @@ -22,10 +23,10 @@ export class CommandRepository { } public getCommand(resource: CommandResource): Observable<CommandResource> { - return this.resourceFacotry.from(resource).get(getUrl(resource)); + return this.resourceFactory.from(resource).get(getUrl(resource)); } public revokeCommand(resource: CommandResource): Observable<CommandResource> { - return this.resourceFacotry.from(resource).patch(CommandLinkRel.REVOKE, { status: CommandStatus.REVOKED }); + return this.resourceFactory.from(resource).patch(CommandLinkRel.REVOKE, { status: CommandStatus.REVOKED }); } } \ No newline at end of file diff --git a/goofy-client/libs/tech-shared/src/index.ts b/goofy-client/libs/tech-shared/src/index.ts index ddcaa36f4410991cff270dc7cf35a2e5d705711e..fed2c8eb6bd758c56ca71b5c248c3a3bd5e27a61 100644 --- a/goofy-client/libs/tech-shared/src/index.ts +++ b/goofy-client/libs/tech-shared/src/index.ts @@ -6,7 +6,7 @@ export * from './lib/pipes/to-traffic-light.pipe'; export * from './lib/resource/resource.util'; export * from './lib/services/navigation.service'; export * from './lib/tech-shared.module'; -export * from './lib/tech.error.util'; export * from './lib/tech.model'; export * from './lib/tech.util'; +export * from './lib/tech.validation.util'; diff --git a/goofy-client/libs/tech-shared/src/lib/tech.error.util.ts b/goofy-client/libs/tech-shared/src/lib/tech.error.util.ts deleted file mode 100644 index 4352ea4a1edbfc3d4d197dda74344a179473b03b..0000000000000000000000000000000000000000 --- a/goofy-client/libs/tech-shared/src/lib/tech.error.util.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Issue } from './tech.model'; - -export function isValidationError(issue: Issue): boolean { - return issue.messageCode.includes('javax.validation.constraints') -} \ No newline at end of file diff --git a/goofy-client/libs/tech-shared/src/lib/tech.model.ts b/goofy-client/libs/tech-shared/src/lib/tech.model.ts index 25bbecb28d23bce45858eb35fcb26d32b5820631..63409e9bf568e503521c68140fb98846b8111d08 100644 --- a/goofy-client/libs/tech-shared/src/lib/tech.model.ts +++ b/goofy-client/libs/tech-shared/src/lib/tech.model.ts @@ -1,6 +1,5 @@ -export interface IssueParam { - name: string, - value: string; +export interface ApiError { + issues: Issue[] } export interface Issue { @@ -10,6 +9,11 @@ export interface Issue { parameters: IssueParam[] } -export interface ApiError { - issues: Issue[] +export interface IssueParam { + name: string, + value: string; +} + +export const VALIDATION_MESSAGES: { [code: string]: string } = { + validation_field_size: '{field} muss aus mindestens {min} Buchstaben bestehen' } \ No newline at end of file diff --git a/goofy-client/libs/tech-shared/src/lib/tech.validation.util.spec.ts b/goofy-client/libs/tech-shared/src/lib/tech.validation.util.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7eeb933b3befb687c49fe3988ef32b4c5b8e028 --- /dev/null +++ b/goofy-client/libs/tech-shared/src/lib/tech.validation.util.spec.ts @@ -0,0 +1,105 @@ +import { FormControl, FormGroup } from "@angular/forms"; +import { Issue } from '..'; +import { createIssue } from "../../test/error"; +import { getControlForIssue, getMessageForIssue, setValidationError } from "./tech.validation.util"; + +describe('tech.validation.utils', () => { + describe('setValidationError', () => { + + const baseField1Control = new FormControl(); + const baseField2Control = new FormControl(); + const subGroupFieldControl = new FormControl(); + + const form = new FormGroup({ + baseField1: baseField1Control, + baseField2: baseField2Control, + subGroup: new FormGroup({ + subGroupField1: subGroupFieldControl + }) + }); + + describe('get control for issue', () => { + it('should return base field control', () => { + const issue: Issue = { ...createIssue(), field: 'baseField1' }; + + const control = getControlForIssue(form, issue); + + expect(control).toBe(baseField1Control); + }); + + it('should reeturn sub group field', () => { + const issue: Issue = { ...createIssue(), field: 'subGroup.subGroupField1' }; + + const control = getControlForIssue(form, issue); + + expect(control).toBe(subGroupFieldControl); + }) + + it('should ignore path prefix', () => { + const issue: Issue = { ...createIssue(), field: 'command.wiedervorlage.baseField1' }; + + const control = getControlForIssue(form, issue, 'command.wiedervorlage'); + + expect(control).toBe(baseField1Control); + }) + }) + + describe('in base field', () => { + const issue: Issue = { ...createIssue(), field: 'baseField1' }; + it('should set error in control', () => { + setValidationError(form, issue); + + expect(baseField1Control.errors).not.toBeNull(); + }); + + it('should set message code in control', () => { + setValidationError(form, issue); + + expect(baseField1Control.hasError(issue.messageCode)).toBe(true); + }); + + it('should set control touched', () => { + setValidationError(form, issue); + + expect(baseField1Control.touched).toBe(true); + }) + + it('should not set error in other control', () => { + setValidationError(form, issue); + + expect(baseField2Control.errors).toBeNull(); + }); + }); + + describe('in subGroup Field', () => { + const issue: Issue = { ...createIssue(), field: 'subGroup.subGroupField1' }; + + it('should set error in control', () => { + setValidationError(form, issue); + + expect(subGroupFieldControl.errors).not.toBeNull(); + }); + }); + }); + + describe('get message for issue', () => { + const fieldLabel = "Field Label"; + it('should return message', () => { + const msg = getMessageForIssue(fieldLabel, { ...createIssue(), messageCode: 'validation_field_size' }); + + expect(msg).toContain('muss aus mindestens'); + }); + + it('should set field label', () => { + const msg = getMessageForIssue(fieldLabel, { ...createIssue(), messageCode: 'validation_field_size' }); + + expect(msg).toContain(fieldLabel); + }); + + it('should replace min param', () => { + const msg = getMessageForIssue(fieldLabel, { ...createIssue(), messageCode: 'validation_field_size', parameters: [{ name: 'min', value: '3' }] }); + + expect(msg).toContain(3); + }); + }) +}) \ No newline at end of file diff --git a/goofy-client/libs/tech-shared/src/lib/tech.validation.util.ts b/goofy-client/libs/tech-shared/src/lib/tech.validation.util.ts new file mode 100644 index 0000000000000000000000000000000000000000..160b3d98c00d636c0813ac6eb4130fc355353c5d --- /dev/null +++ b/goofy-client/libs/tech-shared/src/lib/tech.validation.util.ts @@ -0,0 +1,43 @@ +import { AbstractControl, FormGroup } from '@angular/forms'; +import { isNil } from 'lodash-es'; +import { Issue, VALIDATION_MESSAGES } from './tech.model'; + +export function isValidationError(issue: Issue): boolean { + return issue.messageCode.includes('javax.validation.constraints') +} + +export function setValidationError(form: FormGroup, issue: Issue, pathPrefix?: String) { + const control = getControlForIssue(form, issue, pathPrefix); + + control.setErrors({ [issue.messageCode]: issue }); + control.markAsTouched(); +} + +export function getControlForIssue(form: FormGroup, issue: Issue, pathPrefix?: String): AbstractControl { + const fieldPath = pathPrefix ? issue.field.substr(pathPrefix.length + 1) : issue.field; + + let curControl: AbstractControl = form; + fieldPath.split(".").forEach( + field => curControl = (<FormGroup>curControl).controls[field]); + + return curControl; +} + +export function getMessageForIssue(label: string, issue: Issue) { + let msg = VALIDATION_MESSAGES[issue.messageCode]; + + if (isNil(msg)) { + console.warn('No message for code ' + issue.messageCode + ' found.'); + return issue.messageCode; + } + + msg = replace(msg, 'field', label); + issue.parameters.forEach(param => + msg = replace(msg, param.name, param.value) + ); + return msg; +} + +function replace(text: string, placeholder: string, value: string): string { + return text.replace('{' + placeholder + '}', value); +} \ No newline at end of file diff --git a/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.html b/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.html index 57ec03a8545eec4c91d24085bc3601a3c89a194f..fd29b0f0ea15bd5862b5deb35bd4f4ccf1519506 100644 --- a/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.html +++ b/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.html @@ -1,5 +1,6 @@ <mat-form-field appearance="fill"> <mat-label>{{ label }}</mat-label> - <input matInput [formControl]="fieldControl" autocomplete="off" [attr.data-test-id]="(label | convertForDataTest) + '-text-input'"/> + <input matInput [formControl]="fieldControl" [maxlength]="maxlength" autocomplete="off" (blur)="touch()" + [attr.data-test-id]="(label | convertForDataTest) + '-text-input'" /> <mat-error [attr.data-test-id]="(label | convertForDataTest) + '-text-error'">{{errorMessage}}</mat-error> </mat-form-field> \ No newline at end of file diff --git a/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.spec.ts b/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.spec.ts index 5aa54cabf4279494774a7dc8bd55681ed18d8693..d883fe8c647d1a3a26cdcd20ad939f895c7d312b 100644 --- a/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.spec.ts +++ b/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInput } from '@angular/material/input'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -28,7 +28,7 @@ describe('TextEditorComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(TextEditorComponent); component = fixture.componentInstance; - component.fieldControl = new FormControl(); + component.label = 'Ein Label'; fixture.detectChanges(); }); @@ -43,5 +43,25 @@ describe('TextEditorComponent', () => { const element: HTMLElement = fixture.nativeElement.querySelector('[data-test-id="Ein_Label-text-input"]'); expect(element).toBeInstanceOf(HTMLElement); + }); + + describe('max length attribute', () => { + let input: HTMLElement; + + beforeEach(() => { + component.label = 'Ein Label'; + fixture.detectChanges(); + input = fixture.nativeElement.querySelector('[data-test-id="Ein_Label-text-input"]'); + }) + it('should not be present if no length given', () => { + expect(input).not.toHaveAttribute('maxlength'); + }); + + it('should be present if length is given', () => { + component.maxlength = 5; + fixture.detectChanges(); + + expect(input).toHaveAttribute('maxlength', "5"); + }) }) }); \ No newline at end of file diff --git a/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.ts b/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.ts index 16303a3fe413f394dde156d3d44d467beadab297..8854a74685a67227d9f1d90311be932abc7a66a3 100644 --- a/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.ts +++ b/goofy-client/libs/ui/src/lib/ui/text-editor/text-editor.component.ts @@ -1,14 +1,64 @@ -import { Component, Input } from '@angular/core'; -import { FormControl } from '@angular/forms'; +import { Component, Input, OnDestroy, Optional, Self } from '@angular/core'; +import { ControlValueAccessor, FormControl, NgControl } from '@angular/forms'; +import { Subscription } from 'rxjs'; @Component({ selector: 'goofy-client-text-editor', templateUrl: './text-editor.component.html', styleUrls: ['./text-editor.component.scss'] }) -export class TextEditorComponent { +export class TextEditorComponent implements ControlValueAccessor, OnDestroy { + + readonly fieldControl: FormControl = new FormControl(); + private onChange = (text: string) => { }; + public onTouched = () => { }; + + private changesSubscr: Subscription; - @Input() fieldControl: FormControl; @Input() label: string; + @Input() disabled: boolean = false; @Input() errorMessage: string; + @Input() maxlength: number; + + constructor(@Self() @Optional() public control: NgControl) { + if (this.control) this.control.valueAccessor = this; + + this.changesSubscr = this.fieldControl.valueChanges.subscribe(val => { + this.onChange(val); + this.setErrors(); + }); + } + + touch(): void { + this.onTouched(); + + if (this.control) this.control.statusChanges.subscribe(status => { + this.setErrors(); + }); + } + + writeValue(text: string): void { + this.fieldControl.setValue(text); + this.setErrors(); + } + registerOnChange(fn: (text: string) => {}): void { + this.onChange = fn; + } + registerOnTouched(fn: () => {}): void { + this.onTouched = fn; + } + setDisabledState?(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + ngOnDestroy(): void { + if (this.changesSubscr) this.changesSubscr.unsubscribe(); + } + + private setErrors() { + if (this.control) { + this.fieldControl.setErrors(this.control.errors); + if (this.control.invalid) this.fieldControl.markAsTouched(); + } + } } \ No newline at end of file diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.spec.ts b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.spec.ts index 1779fd3cd6dfd5c992864dea1fbefcd802b4cc72..9f22e48385041917019daba4a0c2b0faae02ae4e 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.spec.ts +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.spec.ts @@ -17,14 +17,14 @@ import { WiedervorlagePageContainerComponent } from './wiedervorlage-page-contai import { BreadcrumbComponent } from './wiedervorlage-page/breadcrumb/breadcrumb.component'; import { WiedervorlageActionButtonsComponent } from './wiedervorlage-page/wiedervorlage-action-buttons/wiedervorlage-action-buttons.component'; import { WiedervorlageFormComponent } from './wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component'; -import { WiedervorlageFormservice } from './wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice'; +import { WiedervorlageFormService } from './wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice'; import { WiedervorlagePageComponent } from './wiedervorlage-page/wiedervorlage-page.component'; describe('WiedervorlagePageContainerComponent', () => { let component: WiedervorlagePageContainerComponent; let fixture: ComponentFixture<WiedervorlagePageContainerComponent>; - const wiedervorlageFormservice = mock(WiedervorlageFormservice); + const wiedervorlageFormservice = mock(WiedervorlageFormService); const wiedervorlageService = mock(WiedervorlageService); const navigationService = mock(NavigationService); const vorgangService = mock(VorgangService); @@ -56,7 +56,7 @@ describe('WiedervorlagePageContainerComponent', () => { useValue: wiedervorlageService }, { - provide: WiedervorlageFormservice, + provide: WiedervorlageFormService, useValue: wiedervorlageFormservice }, { diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.ts b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.ts index b82902cb9e849ef145c828ba501409adff3a529b..1d76ccf3882079910547ffb4643457d05cef751e 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.ts +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page-container.component.ts @@ -6,13 +6,13 @@ import { VorgangResource, VorgangService, VorgangWithEingangResource } from '@go import { WiedervorlageListResource, WiedervorlageResource, WiedervorlageService } from '@goofy-client/wiedervorlage-shared'; import { Observable } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; -import { WiedervorlageFormservice } from './wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice'; +import { WiedervorlageFormService } from './wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice'; @Component({ selector: 'goofy-client-wiedervorlage-page-container', templateUrl: './wiedervorlage-page-container.component.html', styleUrls: ['./wiedervorlage-page-container.component.scss'], - providers: [WiedervorlageFormservice] + providers: [WiedervorlageFormService] }) export class WiedervorlagePageContainerComponent implements AfterContentInit { @@ -27,7 +27,7 @@ export class WiedervorlagePageContainerComponent implements AfterContentInit { constructor( private wiedervorlageService: WiedervorlageService, private vorgangService: VorgangService, - private formService: WiedervorlageFormservice, + private formService: WiedervorlageFormService, private navigationService: NavigationService, private route: ActivatedRoute ) { } diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-action-buttons/wiedervorlage-action-buttons.component.spec.ts b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-action-buttons/wiedervorlage-action-buttons.component.spec.ts index fb9d68d3b6ae9da8a7e3e9f2c2e22b42b1f6cf84..7892fa4bf01b474125a00881a96453249a432fc4 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-action-buttons/wiedervorlage-action-buttons.component.spec.ts +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-action-buttons/wiedervorlage-action-buttons.component.spec.ts @@ -6,14 +6,14 @@ import { IconButtonWithSpinnerComponent } from '@goofy-client/ui'; import { WiedervorlageLinkRel } from '@goofy-client/wiedervorlage-shared'; import { createWiedervorlageResource } from 'libs/wiedervorlage-shared/test/wiedervorlage'; import { MockComponent } from 'ng-mocks'; -import { WiedervorlageFormservice } from '../wiedervorlage-form/wiedervorlage.formservice'; +import { WiedervorlageFormService } from '../wiedervorlage-form/wiedervorlage.formservice'; import { WiedervorlageActionButtonsComponent } from './wiedervorlage-action-buttons.component'; describe('WiedervorlageActionButtonsComponent', () => { let component: WiedervorlageActionButtonsComponent; let fixture: ComponentFixture<WiedervorlageActionButtonsComponent>; - const wiedervorlageFormService = mock(WiedervorlageFormservice); + const wiedervorlageFormService = mock(WiedervorlageFormService); const wiedereroeffnenButtonSelector = '[data-test-id="wiedereroeffnen-icon-button"]' const erledigenButtonSelector = '[data-test-id="erledigen-icon-button"]' @@ -28,7 +28,7 @@ describe('WiedervorlageActionButtonsComponent', () => { ], providers: [ { - provide: WiedervorlageFormservice, + provide: WiedervorlageFormService, useValue: wiedervorlageFormService } ] diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.html b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.html index 3a9884c7a38fe1e4991155b6524a3e38f4a25d03..e6c9b01b428f8b30596e798768b1b3dcc1dab555 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.html +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.html @@ -1,24 +1,23 @@ <form class="form" [formGroup]="formService.form"> - - <goofy-client-text-editor label="Betreff" - [fieldControl]="formService.form.controls[formServiceClass.fieldBetreff]" + <!-- [fieldControl]="formService.form.controls[formServiceClass.fieldBetreff]" --> + <goofy-client-text-editor label="Betreff" [formControlName]="formServiceClass.FIELD_BETREFF" [errorMessage]="wiedervorlageValidationMessages.BETREFF_INVALID"> </goofy-client-text-editor> <goofy-client-textarea-editor label="Beschreibung" - [fieldControl]="formService.form.controls[formServiceClass.fieldBeschreibung]"> + [fieldControl]="formService.form.controls[formServiceClass.FIELD_BESCHREIBUNG]"> </goofy-client-textarea-editor> <goofy-client-date-editor class="date" label="Frist" - [fieldControl]="formService.form.controls[formServiceClass.fieldFrist]" + [fieldControl]="formService.form.controls[formServiceClass.FIELD_FRIST]" [errorMessage]="wiedervorlageValidationMessages.DATUM_INVALID"> </goofy-client-date-editor> <!--goofy-client-file-upload></goofy-client-file-upload--> - <goofy-client-button-with-spinner text="Speichern" icon="save_alt" (click)="formularSubmit.emit()" class="submit-button" - [stateResource]="submitInProgress" data-test-id="speichern-button"> + <goofy-client-button-with-spinner text="Speichern" icon="save_alt" (click)="formularSubmit.emit()" + class="submit-button" [stateResource]="submitInProgress" data-test-id="speichern-button"> </goofy-client-button-with-spinner> <!--goofy-client-button-with-spinner text="Löschen" icon="delete" color="warn"></goofy-client-button-with-spinner--> -</form> +</form> \ No newline at end of file diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.spec.ts b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.spec.ts index 66eb2f324edd51904364ef3375b6d92ea1eae05d..b29e8b73b884475ae652828b511eccf10e451dbc 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.spec.ts +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.spec.ts @@ -1,23 +1,23 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { mock } from '@goofy-client/test-utils'; +import { mock, useFromMock } from '@goofy-client/test-utils'; import { ButtonWithSpinnerComponent } from '@goofy-client/ui'; -import { WiedervorlageResource } from '@goofy-client/wiedervorlage-shared'; +import { WiedervorlageResource, WiedervorlageService } from '@goofy-client/wiedervorlage-shared'; import { DateEditorComponent } from 'libs/ui/src/lib/ui/date-editor/date-editor.component'; import { TextEditorComponent } from 'libs/ui/src/lib/ui/text-editor/text-editor.component'; import { TextAreaEditorComponent } from 'libs/ui/src/lib/ui/textarea-editor/textarea-editor.component'; import { createWiedervorlageResource } from 'libs/wiedervorlage-shared/test/wiedervorlage'; import { MockComponent } from 'ng-mocks'; import { WiedervorlageFormComponent } from './wiedervorlage-form.component'; -import { WiedervorlageFormservice } from './wiedervorlage.formservice'; +import { WiedervorlageFormService } from './wiedervorlage.formservice'; describe('WiedervorlageFormComponent', () => { let component: WiedervorlageFormComponent; let fixture: ComponentFixture<WiedervorlageFormComponent>; - const wiedervorlageFormservice = { ...mock(WiedervorlageFormservice), form: new FormGroup({}) }; + const wiedervorlageFormService = new WiedervorlageFormService(new FormBuilder(), useFromMock(mock(WiedervorlageService))); const wiedervorlage: WiedervorlageResource = createWiedervorlageResource(); beforeEach(async () => { @@ -36,14 +36,16 @@ describe('WiedervorlageFormComponent', () => { ], providers: [ { - provide: WiedervorlageFormservice, - useValue: wiedervorlageFormservice + provide: WiedervorlageFormService, + useValue: wiedervorlageFormService } ] }).compileComponents(); }); beforeEach(() => { + wiedervorlageFormService.patch = jest.fn(); + fixture = TestBed.createComponent(WiedervorlageFormComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -60,7 +62,7 @@ describe('WiedervorlageFormComponent', () => { component.patchWiedervorlage(); - expect(wiedervorlageFormservice.patch).not.toHaveBeenCalled(); + expect(wiedervorlageFormService.patch).not.toHaveBeenCalled(); }) it('should patch by formservice', () => { @@ -68,7 +70,7 @@ describe('WiedervorlageFormComponent', () => { component.patchWiedervorlage(); - expect(wiedervorlageFormservice.patch).toHaveBeenCalledWith(wiedervorlage); + expect(wiedervorlageFormService.patch).toHaveBeenCalledWith(wiedervorlage); }) }) }); diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.ts b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.ts index 5d02ac944bb10de6f5b10ef68c7f5d421b1e513b..bb4fc06f09209e135e59f3ce7046cec6e4d9e6fc 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.ts +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage-form.component.ts @@ -3,7 +3,7 @@ import { CommandResourceWithError } from '@goofy-client/command-shared'; import { StateResource } from '@goofy-client/tech-shared'; import { WiedervorlageResource, WiedervorlageValidationMessages } from '@goofy-client/wiedervorlage-shared'; import { isNull } from 'lodash'; -import { WiedervorlageFormservice } from './wiedervorlage.formservice'; +import { WiedervorlageFormService } from './wiedervorlage.formservice'; @Component({ selector: 'goofy-client-wiedervorlage-form', @@ -19,9 +19,9 @@ export class WiedervorlageFormComponent implements OnChanges { @Output() formularSubmit: EventEmitter<void> = new EventEmitter(); readonly wiedervorlageValidationMessages = WiedervorlageValidationMessages; - readonly formServiceClass = WiedervorlageFormservice; + readonly formServiceClass = WiedervorlageFormService; - constructor(public formService: WiedervorlageFormservice) { } + constructor(public formService: WiedervorlageFormService) { } ngOnChanges(changes: SimpleChanges): void { if (changes.wiedervorlage) { diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.spec.ts b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.spec.ts index e57323767a92f80c64172f7326c851951582f4c5..74cfdd52b4dec9d23a2c570738ba1d77d4034bd3 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.spec.ts +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.spec.ts @@ -6,10 +6,10 @@ import { Wiedervorlage, WiedervorlageResource, WiedervorlageService } from '@goo import { createApiError } from 'libs/tech-shared/test/error'; import { createWiedervorlage, createWiedervorlageResource } from 'libs/wiedervorlage-shared/test/wiedervorlage'; import { of } from 'rxjs'; -import { WiedervorlageFormservice } from './wiedervorlage.formservice'; +import { WiedervorlageFormService } from './wiedervorlage.formservice'; describe('WiedervorlageFormService', () => { - let formService: WiedervorlageFormservice; + let formService: WiedervorlageFormService; let wiedervorlageService: Mock<WiedervorlageService>; const formBuilder: FormBuilder = new FormBuilder(); @@ -19,7 +19,7 @@ describe('WiedervorlageFormService', () => { beforeEach(() => { wiedervorlageService = mock(WiedervorlageService); - formService = new WiedervorlageFormservice(formBuilder, useFromMock(wiedervorlageService)); + formService = new WiedervorlageFormService(formBuilder, useFromMock(wiedervorlageService)); }) it('should create', () => { @@ -30,7 +30,7 @@ describe('WiedervorlageFormService', () => { const date = new Date(); date.setDate(date.getDate() + 14); - expect(formService.form.controls[WiedervorlageFormservice.fieldFrist].value.getDate()).toBe(date.getDate()); + expect(formService.form.controls[WiedervorlageFormService.FIELD_FRIST].value.getDate()).toBe(date.getDate()); }) it('should patch wiedervorlage', () => { diff --git a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.ts b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.ts index 69884409285972af7e2364a87663c1256b95dde9..fcb5ec72a4aebd42b29252a19aa14aae63c4c3f3 100644 --- a/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.ts +++ b/goofy-client/libs/wiedervorlage/src/lib/wiedervorlage-page-container/wiedervorlage-page/wiedervorlage-form/wiedervorlage.formservice.ts @@ -1,18 +1,19 @@ import { Injectable } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { CommandResourceWithError } from '@goofy-client/command-shared'; -import { ApiError, format, Issue, isValidationError, markAsTouched, StateResource } from '@goofy-client/tech-shared'; +import { ApiError, format, setValidationError, StateResource } from '@goofy-client/tech-shared'; import { Wiedervorlage, WiedervorlageResource, WiedervorlageService } from '@goofy-client/wiedervorlage-shared'; import { isNull, isUndefined } from 'lodash-es'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable() -export class WiedervorlageFormservice { +export class WiedervorlageFormService { - static readonly fieldBetreff = 'betreff'; - static readonly fieldBeschreibung = 'beschreibung'; - static readonly fieldFrist = 'frist'; + static readonly FIELD_BETREFF = 'betreff'; + static readonly FIELD_BESCHREIBUNG = 'beschreibung'; + static readonly FIELD_FRIST = 'frist'; + static readonly FIELD_PATH_PREFIX = "command.wiedervorlage"; form: FormGroup; wiedervorlageResource: WiedervorlageResource = undefined @@ -26,9 +27,9 @@ export class WiedervorlageFormservice { private initForm(): void { this.form = this.formBuilder.group({ - [WiedervorlageFormservice.fieldBetreff]: new FormControl(null), - [WiedervorlageFormservice.fieldFrist]: new FormControl(this.createDefaultDate()), - [WiedervorlageFormservice.fieldBeschreibung]: new FormControl() + [WiedervorlageFormService.FIELD_BETREFF]: new FormControl(null), + [WiedervorlageFormService.FIELD_FRIST]: new FormControl(this.createDefaultDate()), + [WiedervorlageFormService.FIELD_BESCHREIBUNG]: new FormControl() }) } @@ -47,7 +48,7 @@ export class WiedervorlageFormservice { } submit(): Observable<StateResource<CommandResourceWithError>> { - this.submitForm(); + // this.submitForm(); if (!isUndefined(this.wiedervorlageResource)) { return this.wiedervorlageService.saveWiedervorlage(this.wiedervorlageResource, this.getFormValue()).pipe( @@ -62,10 +63,6 @@ export class WiedervorlageFormservice { return { ...this.form.value, frist: format(this.form.value.frist) }; } - private submitForm(): void { - markAsTouched(this.form); - } - handleWiedervorlageResponse(result: StateResource<CommandResourceWithError>): StateResource<CommandResourceWithError> { if (result.loading) { return result; @@ -77,18 +74,10 @@ export class WiedervorlageFormservice { } hasValidationError(resource: CommandResourceWithError): boolean { - return !isNull(resource.apiError) && isValidationError(resource.apiError.issues[0]); + return !isNull(resource.apiError); } setError(apiError: ApiError): void { - apiError.issues.forEach(issue => this.setControlError(this.getControlFieldName(issue))); - } - - private getControlFieldName(issue: Issue): string { - return issue.field.substr(issue.field.lastIndexOf('.') + 1, issue.field.length); - } - - private setControlError(field: string): void { - this.form.controls[field].setErrors({ notValid: true }); + apiError.issues.forEach(issue => setValidationError(this.form, issue, WiedervorlageFormService.FIELD_PATH_PREFIX)) } } diff --git a/goofy-server/Dockerfile b/goofy-server/Dockerfile index 35a3f9f2c88a8b34dcde171b970e90750dfc1f11..b9653d2dedcc23e2f782568c42d9b775c4bdefaf 100644 --- a/goofy-server/Dockerfile +++ b/goofy-server/Dockerfile @@ -1,7 +1,8 @@ -FROM 172.30.35.192:8082/openjdk:11-slim +#FROM 172.30.35.192:8082/openjdk:11-slim +FROM openjdk:11-slim #COPY ${JAR_FILE} /opt/goofy.jar COPY /target/goofy-server.jar /opt/goofy.jar WORKDIR /opt -CMD java -jar goofy.jar \ No newline at end of file +CMD java -jar goofy.jar diff --git a/goofy-server/pom.xml b/goofy-server/pom.xml index fa72a2e13480332153d1041097b487f5995000a5..18dfb891ca0e90e937af8381ded391eb3eb157fd 100644 --- a/goofy-server/pom.xml +++ b/goofy-server/pom.xml @@ -280,7 +280,8 @@ <configuration> <username>admin</username> <password>admin</password> - <repository>default-route-openshift-image-registry.apps.lab.okd.local/sh-kiel-dev/goofy</repository> + <!-- <repository>default-route-openshift-image-registry.apps.lab.okd.local/sh-kiel-dev/goofy</repository> --> + <repository>registry.ozg-sh.de/sh-land/goofy</repository> <tag>${git.branch}-${project.version}</tag> <useMavenSettingsForAuth>true</useMavenSettingsForAuth> <buildArgs> diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java b/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java new file mode 100644 index 0000000000000000000000000000000000000000..853459b1f0465024ed5e01790e772c2e245d44ff --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java @@ -0,0 +1,7 @@ +package de.itvsh.goofy.common; + +public class ValidationMessageCodes { + + public static final String FIELD_IS_NULL = "validation-field-empty"; + public static final String FIELD_SIZE = "validation-field-size"; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandModelAssembler.java index 3cd2c06ebd0c37d8daf3e1429eda6a44e66bb4f4..8650a51f0065e4dfc818e4fbd070a53a4155681b 100644 --- a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandModelAssembler.java +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandModelAssembler.java @@ -3,7 +3,6 @@ package de.itvsh.goofy.common.command; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; import java.util.Optional; -import java.util.Set; import java.util.function.Predicate; import org.springframework.hateoas.EntityModel; @@ -23,12 +22,6 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent private static final Predicate<Command> IS_DONE = command -> command.getStatus() == CommandStatus.FINISHED || command.getStatus() == CommandStatus.REVOKED; - private static final Set<CommandOrder> NOT_ALLOW_TO_REVOKE = Set.of( - CommandOrder.CREATE_WIEDERVORLAGE, - CommandOrder.EDIT_WIEDERVORLAGE, - CommandOrder.WIEDERVORLAGE_ERLEDIGEN); - private static final Predicate<Command> ALLOW_REVOKE = command -> !NOT_ALLOW_TO_REVOKE.contains(command.getOrder()); - @Override public EntityModel<Command> toModel(Command entity) { Link selfLink = linkTo(CommandController.class).slash(entity.getId()).withSelfRel(); @@ -39,7 +32,7 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent var resultModel = EntityModel.of(entity, selfLink); resultModel = addIf(IS_DONE, resultModel, effectedResourceLink); - resultModel = addIf(ALLOW_REVOKE, resultModel, revokeLink); + resultModel = addIf(command -> command.getOrder().isRevokeable(), resultModel, revokeLink); return resultModel; } diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandOrder.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandOrder.java index 49aeb0516f314ee300ec7f96a9b6651078a643a1..63b3e300bd2893ad66aab135ccbd2cb8479bd45f 100644 --- a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandOrder.java +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandOrder.java @@ -1,14 +1,22 @@ package de.itvsh.goofy.common.command; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter public enum CommandOrder { - VORGANG_ANNEHMEN, - VORGANG_VERWERFEN, - VORGANG_ZURUECKHOLEN, - VORGANG_BEARBEITEN, - VORGANG_BESCHEIDEN, - VORGANG_ZURUECKSTELLEN, - VORGANG_ABSCHLIESSEN, - VORGANG_WIEDEREROEFFNEN, + VORGANG_ANNEHMEN(true), + VORGANG_VERWERFEN(true), + VORGANG_ZURUECKHOLEN(true), + VORGANG_BEARBEITEN(true), + VORGANG_BESCHEIDEN(true), + VORGANG_ZURUECKSTELLEN(true), + VORGANG_ABSCHLIESSEN(true), + VORGANG_WIEDEREROEFFNEN(true), CREATE_WIEDERVORLAGE, EDIT_WIEDERVORLAGE, @@ -17,5 +25,7 @@ public enum CommandOrder { CREATE_KOMMENTAR, - UNRECOGNIZED + UNRECOGNIZED; + + private boolean revokeable; } \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java index 56ead9ecc5206517248148afab291938e275d4a3..f3c58410f5043a06ae8411936a4e94fd8fae90ad 100644 --- a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java @@ -1,10 +1,16 @@ package de.itvsh.goofy.common.errorhandling; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.Path; +import javax.validation.metadata.ConstraintDescriptor; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; @@ -20,6 +26,8 @@ import lombok.extern.log4j.Log4j2; @Order(99) public class ExceptionController { + private static final Set<String> IGNORABLE_CONSTRAINT_VIOLATION_ATTRIBUTES = new HashSet<>(Arrays.asList("groups", "payload", "message")); + @ExceptionHandler(ResourceNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) @ResponseBody @@ -51,11 +59,20 @@ public class ExceptionController { private Issue buildIssue(ConstraintViolation<?> violation) { return Issue.builder()// .field(buildFieldPath(violation.getPropertyPath()))// - .messageCode(violation.getMessageTemplate().replaceAll("\\{", "").replaceAll("\\}", ""))// + .messageCode(violation.getMessageTemplate().replace("{", "").replace("}", ""))// .message(violation.getMessage())// + .parameters(buildParameters(violation).collect(Collectors.toList())) .build(); } + private Stream<IssueParam> buildParameters(ConstraintViolation<?> violation) { + return Optional.ofNullable(violation.getConstraintDescriptor()).map(ConstraintDescriptor::getAttributes) + .map(descr -> descr.entrySet().stream() + .filter(entry -> !IGNORABLE_CONSTRAINT_VIOLATION_ATTRIBUTES.contains(entry.getKey())) + .map(entry -> IssueParam.builder().name(entry.getKey()).value(entry.getValue().toString()).build())) + .orElse(Stream.empty()); + } + private String buildFieldPath(Path propertyPath) { return propertyPath.toString().substring(propertyPath.toString().indexOf('.') + 1); } diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/Wiedervorlage.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/Wiedervorlage.java index 5b8a43428660c0be43db9721fece576730c61e38..1fa0ddff6bda8924c55ae25b1229de41cc3eddd1 100644 --- a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/Wiedervorlage.java +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/Wiedervorlage.java @@ -1,6 +1,9 @@ package de.itvsh.goofy.wiedervorlage; +import static de.itvsh.goofy.common.ValidationMessageCodes.*; + import java.time.LocalDate; +import java.time.ZonedDateTime; import javax.validation.constraints.FutureOrPresent; import javax.validation.constraints.NotNull; @@ -16,7 +19,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import java.time.ZonedDateTime; @Getter @Builder(toBuilder = true) @@ -37,8 +39,8 @@ public class Wiedervorlage { @JsonProperty(access = Access.READ_ONLY) private ZonedDateTime createdAt; - @NotNull - @Size(min = 3) + @NotNull(message = FIELD_IS_NULL) + @Size(min = 3, max = 40, message = FIELD_SIZE) private String betreff; private String beschreibung; diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandControllerTest.java index 5e030bfcc189ae3c632f49a3b04a475f88eed828..a36049d62d023ec0abfd402d1183976807eba473 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandControllerTest.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandControllerTest.java @@ -21,13 +21,6 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import de.itvsh.goofy.TestUtils; -import de.itvsh.goofy.common.command.Command; -import de.itvsh.goofy.common.command.CommandController; -import de.itvsh.goofy.common.command.CommandModelAssembler; -import de.itvsh.goofy.common.command.CommandOrder; -import de.itvsh.goofy.common.command.CommandService; -import de.itvsh.goofy.common.command.CommandStatus; -import de.itvsh.goofy.common.command.CreateCommand; import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; import de.itvsh.goofy.common.errorhandling.ExceptionController; import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandModelAssemblerTest.java index 3cf683147e4a751c7f9151cd407167f11115e781..89622b1e689f1d27241cd2d349e47c1d95beae4d 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandModelAssemblerTest.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandModelAssemblerTest.java @@ -15,11 +15,6 @@ import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; -import de.itvsh.goofy.common.command.Command; -import de.itvsh.goofy.common.command.CommandModelAssembler; -import de.itvsh.goofy.common.command.CommandOrder; -import de.itvsh.goofy.common.command.CommandStatus; - class CommandModelAssemblerTest { @InjectMocks @@ -80,7 +75,8 @@ class CommandModelAssemblerTest { private final String COMMAND_URL = "/api/commands/" + CommandTestFactory.ID; @ParameterizedTest - @EnumSource(mode = Mode.EXCLUDE, names = { "CREATE_WIEDERVORLAGE", "WIEDERVORLAGE_ERLEDIGEN", "EDIT_WIEDERVORLAGE" }) + @EnumSource(mode = Mode.EXCLUDE, names = { "CREATE_WIEDERVORLAGE", "WIEDERVORLAGE_ERLEDIGEN", "EDIT_WIEDERVORLAGE", + "WIEDERVORLAGE_WIEDEREROEFFNEN", "CREATE_KOMMENTAR", "UNRECOGNIZED" }) void shoulHaveLink(CommandOrder order) { var model = modelAssembler.toModel(CommandTestFactory.createBuilder().order(order).build()); @@ -88,7 +84,8 @@ class CommandModelAssemblerTest { } @ParameterizedTest - @EnumSource(names = { "CREATE_WIEDERVORLAGE", "WIEDERVORLAGE_ERLEDIGEN", "EDIT_WIEDERVORLAGE" }) + @EnumSource(names = { "CREATE_WIEDERVORLAGE", "WIEDERVORLAGE_ERLEDIGEN", "EDIT_WIEDERVORLAGE", + "WIEDERVORLAGE_WIEDEREROEFFNEN", "CREATE_KOMMENTAR", "UNRECOGNIZED" }) void shouldNotHaveLink(CommandOrder order) { var model = modelAssembler.toModel(CommandTestFactory.createBuilder().order(order).build()); diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandRemoteServiceTest.java index b43024bc4670e018eac7eff285f46d4f07fd9533..9c830994ead06c8f1ac89c489c752f0dc4fa641f 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandRemoteServiceTest.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandRemoteServiceTest.java @@ -16,11 +16,6 @@ import org.mockito.Mock; import org.mockito.Spy; import de.itvsh.goofy.common.ContextService; -import de.itvsh.goofy.common.command.Command; -import de.itvsh.goofy.common.command.CommandMapper; -import de.itvsh.goofy.common.command.CommandOrder; -import de.itvsh.goofy.common.command.CommandRemoteService; -import de.itvsh.goofy.common.command.CreateCommand; import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc.CommandServiceBlockingStub; import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandServiceTest.java index ff58ade45514d287247ef370575e7df06fc3a555..4f14b946a86818b0db79c7ab8e62e628ab87b55d 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandServiceTest.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandServiceTest.java @@ -13,10 +13,6 @@ import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import de.itvsh.goofy.common.command.CommandRemoteService; -import de.itvsh.goofy.common.command.CommandService; -import de.itvsh.goofy.common.command.CreateCommand; - class CommandServiceTest { @InjectMocks @@ -27,7 +23,6 @@ class CommandServiceTest { @Captor private ArgumentCaptor<CreateCommand> vorgangCommandCaptor; - @Captor private ArgumentCaptor<Long> versionCaptor; diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandTestFactory.java index c2a2a80298ee7bac5da1995342b9c97fa09dc5e1..c863376c619b767680ba4a4c240b56df3c3b8e07 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandTestFactory.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandTestFactory.java @@ -2,9 +2,6 @@ package de.itvsh.goofy.common.command; import java.util.UUID; -import de.itvsh.goofy.common.command.Command; -import de.itvsh.goofy.common.command.CommandOrder; -import de.itvsh.goofy.common.command.CreateCommand; import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; public class CommandTestFactory { diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionControllerTest.java index dc47af9d037f36cd9dcc6221139e57b77f00bc97..9fac49cfc24c8a2b79649e8f4de8c80f58a994ba 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionControllerTest.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionControllerTest.java @@ -14,7 +14,7 @@ import org.mockito.InjectMocks; class ExceptionControllerTest { - @InjectMocks // NOSONAR + @InjectMocks private ExceptionController exceptionController; @Test diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandITCase.java index 371b93e04e01f0179ad6d034bb8edd46eca05eb0..a31aaa361ea28fca37b16aa847da73007ab68d39 100644 --- a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandITCase.java +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandITCase.java @@ -4,10 +4,12 @@ import static de.itvsh.goofy.wiedervorlage.WiedervorlageCommandTestFactory.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.time.LocalDate; +import org.assertj.core.internal.bytebuddy.utility.RandomString; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -21,6 +23,8 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; +import de.itvsh.goofy.common.ValidationMessageCodes; + @AutoConfigureMockMvc @SpringBootTest @WithMockUser @@ -56,22 +60,44 @@ class WiedervorlageCommandITCase { @DisplayName("for null Betreff") @Test void createCommandWithInvalidBetreff() throws Exception { - String content = createRequestContent(createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().betreff(null).build())); + String content = buildContentWithBetreff(null); doRequest(content).andExpect(status().isUnprocessableEntity()) .andExpect(jsonPath("$.issues.length()").value(1)) .andExpect(jsonPath("$.issues.[0].field").value("command.wiedervorlage.betreff")) - .andExpect(jsonPath("$.issues.[0].messageCode").value("javax.validation.constraints.NotNull.message")) - .andExpect(jsonPath("$.issues.[0].message").value("must not be null")); + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_NULL)) + .andDo(print()); } - @DisplayName("for only 1 charachter in Betreff") + @DisplayName("for only 1 character in Betreff") @Test void createcommandWithShortBetreff() throws Exception { - String content = createRequestContent(createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().betreff("a").build())); + String content = buildContentWithBetreff("a"); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.[0].field").value("command.wiedervorlage.betreff")); + + } + + @DisplayName("for invalid betreff should have paramter") + @Test + void minMaxParameter() throws Exception { + String content = buildContentWithBetreff("a"); + + doRequest(content).andExpect(status().isUnprocessableEntity()).andDo(print()) + .andExpect(jsonPath("$.issues[0].parameters.length()").value(2)); + } + + @DisplayName("for 41 character in Betreff") + @Test + void createCommandWithLongBetreff() throws Exception { + var content = buildContentWithBetreff(RandomString.make(41)); doRequest(content).andExpect(status().isUnprocessableEntity()); + } + private String buildContentWithBetreff(String betreff) { + return createRequestContent(createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().betreff(betreff).build())); } @DisplayName("for date in past") @@ -83,9 +109,11 @@ class WiedervorlageCommandITCase { doRequest(content).andExpect(status().isUnprocessableEntity()) .andExpect(jsonPath("$.issues.length()").value(1)) .andExpect(jsonPath("$.issues.[0].field").value("command.wiedervorlage.frist")) - .andExpect(jsonPath("$.issues.[0].messageCode").value("javax.validation.constraints.FutureOrPresent.message")) - .andExpect(jsonPath("$.issues.[0].message").value("must be a date in the present or in the future")); + .andExpect(jsonPath("$.issues.[0].messageCode").value("javax.validation.constraints.FutureOrPresent.message")); } + +// @DisplayName("for empty date") + } private ResultActions doRequest(String content) throws Exception {