diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts index 4d314d740a260b8be5f21e709dca72093452b06f..328960de1f9b9a87f8442595ad8b609f339f9da8 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts @@ -288,10 +288,10 @@ describe('BinaryFileService', () => { return <HttpErrorResponse>{ status: 422, error: { - issues: [ + invalidParams: [ { - messageCode: validationMessageCode, - parameters: [], + reason: validationMessageCode, + constraintParameters: [], }, ], }, diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts index bb6592cb76931ff30edca949cc28a864686ecfce..57773cccbfa61d4dab2af4984a88d6d3f9cf7b14 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts @@ -29,7 +29,7 @@ import { createEmptyStateResource, createErrorStateResource, createStateResource, - getMessageForIssue, + getMessageForInvalidParam, isNotNil, isUnprocessableEntity, isValidationFieldFileSizeExceedError, @@ -90,7 +90,9 @@ export class BinaryFileService { handleSnackBar(error: HttpErrorResponse, showValidationErrorSnackBar: boolean) { if (showValidationErrorSnackBar && isValidationFieldFileSizeExceedError(error.error)) { - this.snackbarService.showError(getMessageForIssue(EMPTY_STRING, error.error.issues[0])); + this.snackbarService.showError( + getMessageForInvalidParam(EMPTY_STRING, error.error.invalidParams[0]), + ); } } diff --git a/alfa-client/libs/tech-shared/src/index.ts b/alfa-client/libs/tech-shared/src/index.ts index 64e34b9ffcb66244bb7687070f685ed6090b4950..6447da0179658e13795f1dbe8bfa8f0df2adeda3 100644 --- a/alfa-client/libs/tech-shared/src/index.ts +++ b/alfa-client/libs/tech-shared/src/index.ts @@ -33,6 +33,7 @@ export * from './lib/message-code'; export * from './lib/ngrx/actions'; export * from './lib/pipe/convert-api-error-to-error-messages.pipe'; export * from './lib/pipe/convert-for-data-test.pipe'; +export * from './lib/pipe/convert-problem-detail-to-error-messages.pipe'; export * from './lib/pipe/convert-to-boolean.pipe'; export * from './lib/pipe/enum-to-label.pipe'; export * from './lib/pipe/file-size.pipe'; diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe.spec.ts b/alfa-client/libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ffef78f2195217763693fbd09abf727915afac23 --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe.spec.ts @@ -0,0 +1,42 @@ +import { createInvalidParam, createProblemDetail } from '../../../test/error'; +import { InvalidParam, ProblemDetail } from '../tech.model'; +import { ValidationMessageCode } from '../validation/tech.validation.messages'; +import * as TechValidationUtil from '../validation/tech.validation.util'; +import { ConvertProblemDetailToErrorMessagesPipe } from './convert-problem-detail-to-error-messages.pipe'; + +describe('convertProblemDetailToErrorMessages', () => { + const pipe = new ConvertProblemDetailToErrorMessagesPipe(); + + it('create an instance', () => { + expect(pipe).toBeTruthy(); + }); + + describe('transform', () => { + const getMessageForInvalidParam = jest.spyOn(TechValidationUtil, 'getMessageForInvalidParam'); + + it('should not call getMessageForInvalidParam', () => { + pipe.transform(null); + + expect(getMessageForInvalidParam).not.toHaveBeenCalled(); + }); + + it('should call getMessageForInvalidParam', () => { + pipe.transform(createProblemDetail()); + + expect(getMessageForInvalidParam).toHaveBeenCalled(); + }); + + it('should return array of error messages', () => { + const expectedErrorMessage = 'Bitte ausfüllen'; + const invalidParam: InvalidParam = { + ...createInvalidParam(), + reason: ValidationMessageCode.FIELD_EMPTY, + }; + const problemDetail: ProblemDetail = createProblemDetail([invalidParam]); + + const errorMessages: string[] = pipe.transform(problemDetail); + + expect(errorMessages).toEqual([expectedErrorMessage]); + }); + }); +}); diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe.ts b/alfa-client/libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..437483a923c7f85353b96a745a2c24e1cc88e0fb --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe.ts @@ -0,0 +1,17 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { isNil } from 'lodash-es'; +import { InvalidParam, ProblemDetail } from '../tech.model'; +import { EMPTY_STRING } from '../tech.util'; +import { getMessageForInvalidParam } from '../validation/tech.validation.util'; + +@Pipe({ name: 'convertProblemDetailToErrorMessages' }) +export class ConvertProblemDetailToErrorMessagesPipe implements PipeTransform { + transform(value: ProblemDetail) { + if (isNil(value)) { + return []; + } + return value.invalidParams.map((invalidParam: InvalidParam) => + getMessageForInvalidParam(EMPTY_STRING, invalidParam), + ); + } +} diff --git a/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts b/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts index 331031b298487fcfa77df7c10041a33cfedec7bb..a91930ffa732c82e0be57db7f18516b24eb393c9 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts @@ -29,6 +29,7 @@ import { HttpXsrfInterceptor } from './interceptor/http-xsrf.interceptor'; import { XhrInterceptor } from './interceptor/xhr.interceptor'; import { ConvertApiErrorToErrorMessagesPipe } from './pipe/convert-api-error-to-error-messages.pipe'; import { ConvertForDataTestPipe } from './pipe/convert-for-data-test.pipe'; +import { ConvertProblemDetailToErrorMessagesPipe } from './pipe/convert-problem-detail-to-error-messages.pipe'; import { ConvertToBooleanPipe } from './pipe/convert-to-boolean.pipe'; import { EnumToLabelPipe } from './pipe/enum-to-label.pipe'; import { FileSizePlainPipe } from './pipe/file-size-plain.pipe'; @@ -69,6 +70,7 @@ import { ToTrafficLightPipe } from './pipe/to-traffic-light.pipe'; GetUrlPipe, ConvertToBooleanPipe, ConvertApiErrorToErrorMessagesPipe, + ConvertProblemDetailToErrorMessagesPipe, ], exports: [ FormatToPrettyDatePipe, @@ -90,6 +92,7 @@ import { ToTrafficLightPipe } from './pipe/to-traffic-light.pipe'; GetUrlPipe, ConvertToBooleanPipe, ConvertApiErrorToErrorMessagesPipe, + ConvertProblemDetailToErrorMessagesPipe, ], providers: [ { diff --git a/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.spec.ts b/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.spec.ts index e84d1d813df15f207b77c4965a0887b1baf5c1d7..140ac187955823972d474bcd90c568133b99bde7 100644 --- a/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.spec.ts @@ -28,14 +28,12 @@ import { UntypedFormControl, UntypedFormGroup, } from '@angular/forms'; -import { faker } from '@faker-js/faker'; import { createInvalidParam, createIssue } from '../../../test/error'; import { InvalidParam, Issue } from '../tech.model'; import { getControlForInvalidParam, getControlForIssue, getFieldPath, - getMessageForInvalidParam, getMessageForIssue, setInvalidParamValidationError, setIssueValidationError, @@ -152,68 +150,92 @@ describe('ValidationUtils', () => { }); }); - describe('invalid param', () => { - const formPrefixes: string[] = ['', 'somePrefix']; - const fieldNames: string[] = ['baseField1', 'baseField2', 'subGroup.subGroupField1']; - const prefixNameCombinations: string[][] = formPrefixes - .flatMap((prefix) => fieldNames.map((name) => [prefix, name])) - .filter((el) => existingPrefixAndFieldCombination(el[0], el[1])); - - const fieldLabel: string = faker.lorem.word(); - - describe.each(prefixNameCombinations)( - 'with prefix "%s" and fieldName "%s"', - (prefix, fieldName) => { - let invalidParam: InvalidParam; - - beforeEach(() => { - form.reset(); - invalidParam = { - ...createInvalidParam(), - name: prefix.length ? `${prefix}.${fieldName}` : fieldName, - }; - }); - - describe('get message for invalid param', () => { - it('should return', () => { - const msg: string = getMessageForInvalidParam(fieldLabel, invalidParam); - - expect(msg).toEqual(`Bitte ${fieldLabel} ausfüllen`); - }); - }); - - describe('get control for invalid param', () => { - it('should find', () => { - const control: AbstractControl = getControlForInvalidParam(form, invalidParam, prefix); - - expect(control).toBeTruthy(); - }); - }); - - describe('set invalid param validation error', () => { - it('should assign invalidParam to form control error without prefix', () => { - setInvalidParamValidationError(form, invalidParam, prefix); - - const result: InvalidParam = form.getError(invalidParam.reason, fieldName); - - expect(result).toBe(invalidParam); - }); - - it('should mark form as touched', () => { - setInvalidParamValidationError(form, invalidParam, prefix); - - expect(form.touched).toBeTruthy(); - }); - }); - }, - ); + describe('set invalidParam validation error', () => { + describe('get control for invalidParam', () => { + it('should return base field control', () => { + const invalidParam: InvalidParam = { + ...createInvalidParam(), + name: 'class.resource.baseField1', + }; + + const control: AbstractControl = getControlForInvalidParam(form, invalidParam); + + expect(control).toBe(baseField1Control); + }); + + it('should return sub group field', () => { + const invalidParam: InvalidParam = { + ...createInvalidParam(), + name: 'class.resource.subGroup.subGroupField1', + }; + + const control: AbstractControl = getControlForInvalidParam(form, invalidParam, 'resource'); + + expect(control).toBe(subGroupFieldControl); + }); + + it('should ignore path prefix', () => { + const invalidParam: InvalidParam = { + ...createInvalidParam(), + name: 'class.resource.baseField1', + }; + + const control: AbstractControl = getControlForInvalidParam(form, invalidParam, 'resource'); + + expect(control).toBe(baseField1Control); + }); + }); + + describe('in base field', () => { + const invalidParam: InvalidParam = { + ...createInvalidParam(), + name: 'class.resource.baseField1', + }; + + it('should set error in control', () => { + setInvalidParamValidationError(form, invalidParam); + + expect(baseField1Control.errors).not.toBeNull(); + }); + + it('should set message code in control', () => { + setInvalidParamValidationError(form, invalidParam); + + expect(baseField1Control.hasError(invalidParam.reason)).toBe(true); + }); + + it('should set control touched', () => { + setInvalidParamValidationError(form, invalidParam); + + expect(baseField1Control.touched).toBe(true); + }); + + it('should not set error in other control', () => { + setInvalidParamValidationError(form, invalidParam); + + expect(baseField2Control.errors).toBeNull(); + }); + }); + + describe('in subGroup Field', () => { + const invalidParam: InvalidParam = { + ...createInvalidParam(), + name: 'class.resource.subGroup.subGroupField1', + }; + + it('should set error in control', () => { + setInvalidParamValidationError(form, invalidParam, 'resource'); + + expect(subGroupFieldControl.errors).not.toBeNull(); + }); + }); }); describe('getFieldPath', () => { const resource: string = 'resource'; const backendClassName: string = 'class'; - it('should return field path ', () => { + it('should return field path', () => { const fieldPath: string = 'field1'; const fullPath: string = `${backendClassName}.${resource}.${fieldPath}`; @@ -230,9 +252,22 @@ describe('ValidationUtils', () => { expect(result).toBe(fieldPath); }); + + it('should return field from full path when resource is undefined', () => { + const fieldPath: string = 'field1'; + const fullPath: string = `${backendClassName}.${resource}.${fieldPath}`; + + const result: string = getFieldPath(fullPath, undefined); + + expect(result).toBe(fieldPath); + }); + + it('should return field from field when resource is undefined', () => { + const fieldPath: string = 'field1'; + + const result: string = getFieldPath(fieldPath, undefined); + + expect(result).toBe(fieldPath); + }); }); }); - -function existingPrefixAndFieldCombination(prefix: string, field: string): boolean { - return !(prefix === '' && field === 'subGroup.subGroupField1'); -} diff --git a/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.ts b/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.ts index 9a76e4b6f54deb9a0cef10a26406333283565690..4483d47bdef3f9be7afb00ad9a8c86f6c270be01 100644 --- a/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.ts +++ b/alfa-client/libs/tech-shared/src/lib/validation/tech.validation.util.ts @@ -22,8 +22,8 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { AbstractControl, UntypedFormGroup } from '@angular/forms'; -import { isNil } from 'lodash-es'; -import { ApiError, InvalidParam, Issue, IssueParam } from '../tech.model'; +import { isEmpty, isNil } from 'lodash-es'; +import { ApiError, InvalidParam, Issue, IssueParam, ProblemDetail } from '../tech.model'; import { replacePlaceholder } from '../tech.util'; import { VALIDATION_MESSAGES, ValidationMessageCode } from './tech.validation.messages'; @@ -73,7 +73,11 @@ export function getMessageForIssue(label: string, issue: Issue): string { } export function isValidationFieldFileSizeExceedError(error: any) { - return getMessageCode(error) === ValidationMessageCode.FIELD_FILE_SIZE_EXCEEDED; + return getMessageReason(error) === ValidationMessageCode.FIELD_FILE_SIZE_EXCEEDED; +} + +export function getMessageReason(problemDetail: ProblemDetail): string { + return problemDetail.invalidParams[0].reason ?? null; } export function getMessageCode(apiError: ApiError): string { @@ -115,7 +119,10 @@ export function getMessageForInvalidParam(label: string, invalidParam: InvalidPa } export function getFieldPath(name: string, pathPrefix: string): string { - pathPrefix = `${pathPrefix}.`; - const indexField = name.lastIndexOf(pathPrefix) + pathPrefix.length; - return name.slice(indexField); + if (isEmpty(pathPrefix)) { + return name.split('.').pop(); + } + + const indexOfField = name.lastIndexOf(pathPrefix) + pathPrefix.length + 1; + return name.slice(indexOfField); } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html index ff4e542e0d9181e9ee7bc98b468e62b2a1d23482..78e9fba0ee241ee0b39e7a9e4d771e48cb1b84fd 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html @@ -22,7 +22,7 @@ *ngIf="uploadFileInProgress.loading || uploadFileInProgress.error" [loadingCaption]="uploadFileInProgress.fileName" errorCaption="Fehler beim Hochladen" - [errorMessages]="uploadFileInProgress.error | convertApiErrorToErrorMessages" + [errorMessages]="uploadFileInProgress.error | convertProblemDetailToErrorMessages" description="Anhang wird hochgeladen" [isLoading]="uploadFileInProgress.loading" ></ods-attachment> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts index 63758c537759f45fbe750b6c286b6b2e5778864d..caed9958a4f9dc70ae170976ae44c01c77e8e235 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts @@ -2,7 +2,6 @@ import { BescheidService } from '@alfa-client/bescheid-shared'; import { BinaryFile2ContainerComponent } from '@alfa-client/binary-file'; import { BinaryFileResource } from '@alfa-client/binary-file-shared'; import { - ConvertApiErrorToErrorMessagesPipe, convertForDataTest, ConvertForDataTestPipe, createErrorStateResource, @@ -15,6 +14,7 @@ import { OzgcloudSvgIconComponent, SpinnerComponent } from '@alfa-client/ui'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; import { AttachmentComponent, AttachmentWrapperComponent, SpinnerIconComponent } from '@ods/system'; +import { ConvertProblemDetailToErrorMessagesPipe } from 'libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe'; import { MockComponent, MockPipe } from 'ng-mocks'; import { BehaviorSubject, EMPTY, Observable, of, Subscription } from 'rxjs'; import { createUploadFileInProgress } from '../../../../../../../bescheid-shared/src/test/bescheid'; @@ -51,7 +51,7 @@ describe('VorgangDetailBescheidenResultAttachmentsComponent', () => { ConvertForDataTestPipe, MatIcon, MockPipe(FileSizePipe), - MockPipe(ConvertApiErrorToErrorMessagesPipe), + MockPipe(ConvertProblemDetailToErrorMessagesPipe), MockComponent(OzgcloudSvgIconComponent), MockComponent(SpinnerComponent), MockComponent(AttachmentWrapperComponent), diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html index bb762af88d7b449d222804be6eccd2be60690f5e..d94edab530d6fb8fcdce754bee1d5083f0e7e6b7 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html @@ -27,7 +27,7 @@ 'upload-bescheid-document-error-' + !!uploadBescheidDocumentInProgress.error " [isLoading]="uploadBescheidDocumentInProgress.loading" - [errorMessages]="uploadBescheidDocumentInProgress.error | convertApiErrorToErrorMessages" + [errorMessages]="uploadBescheidDocumentInProgress.error | convertProblemDetailToErrorMessages" description="Bescheiddokument wird hochgeladen" ></ods-attachment> <ods-attachment diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts index c61b90e368bb525ef644dfd2e95d156c06338d22..7dceb5ce5ba5e75c88ddb80fdd529a89b3e5ed41 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts @@ -2,7 +2,6 @@ import { BescheidLinkRel, BescheidResource, BescheidService } from '@alfa-client import { BinaryFile2ContainerComponent } from '@alfa-client/binary-file'; import { CommandResource } from '@alfa-client/command-shared'; import { - ConvertApiErrorToErrorMessagesPipe, StateResource, createEmptyStateResource, createStateResource, @@ -13,6 +12,7 @@ import { getUrl } from '@ngxp/rest'; import { AttachmentComponent, AttachmentWrapperComponent } from '@ods/system'; import { createBescheidResource } from 'libs/bescheid-shared/src/test/bescheid'; import { createBinaryFileResource } from 'libs/binary-file-shared/test/binary-file'; +import { ConvertProblemDetailToErrorMessagesPipe } from 'libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { createApiError } from 'libs/tech-shared/test/error'; import { MockComponent, MockPipe } from 'ng-mocks'; @@ -48,7 +48,7 @@ describe('VorgangDetailBescheidenResultDokumentComponent', () => { MockComponent(BinaryFile2ContainerComponent), MockComponent(AttachmentComponent), MockComponent(AttachmentWrapperComponent), - MockPipe(ConvertApiErrorToErrorMessagesPipe), + MockPipe(ConvertProblemDetailToErrorMessagesPipe), ], providers: [ {