Skip to content
Snippets Groups Projects
Commit b8ca7f8e authored by OZGCloud's avatar OZGCloud
Browse files

OZG-6170 Refactore getMessageForInvalidParam()

Same behaviour like getMessageForIssue() because label comes from HTML template only.
parent e81b3b95
No related branches found
No related tags found
No related merge requests found
Showing
with 169 additions and 70 deletions
......@@ -175,7 +175,7 @@ describe('PostfachFormComponent', () => {
function createProblemDetailForAbsenderName(): ProblemDetail {
return {
...createProblemDetail(),
'invalid-params': [{ ...createInvalidParam(), name: 'settingBody.absender.name' }],
invalidParams: [{ ...createInvalidParam(), name: 'settingBody.absender.name' }],
};
}
......
......@@ -21,7 +21,7 @@
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { Issue } from '@alfa-client/tech-shared';
import { InvalidParam } from '@alfa-client/tech-shared';
import { Component, OnDestroy, OnInit, Optional, Self } from '@angular/core';
import { ControlValueAccessor, NgControl, UntypedFormControl } from '@angular/forms';
import { isEmpty } from 'lodash-es';
......@@ -89,9 +89,11 @@ export abstract class FormControlEditorAbstractComponent
}
}
get issues(): Issue[] {
get invalidParams(): InvalidParam[] {
return this.fieldControl.errors ?
Object.keys(this.fieldControl.errors).map((key) => <Issue>this.fieldControl.errors[key])
Object.keys(this.fieldControl.errors).map(
(key) => <InvalidParam>this.fieldControl.errors[key],
)
: [];
}
}
......@@ -10,7 +10,7 @@
>
<ods-validation-error
error
[issues]="issues"
[invalidParams]="invalidParams"
[label]="label"
[attr.data-test-id]="(label | convertForDataTest) + '-text-editor-error'"
></ods-validation-error>
......
......@@ -26,6 +26,6 @@ export class TextEditorComponent extends FormControlEditorAbstractComponent {
@Input() focus: boolean = false;
get variant(): string {
return this.issues.length > 0 ? 'error' : 'default';
return this.invalidParams.length > 0 ? 'error' : 'default';
}
}
......@@ -10,7 +10,7 @@
>
<ods-validation-error
error
[issues]="issues"
[invalidParams]="invalidParams"
[label]="label"
[attr.data-test-id]="(label | convertForDataTest) + '-textarea-editor-error'"
></ods-validation-error>
......
......@@ -26,6 +26,6 @@ export class TextareaEditorComponent extends FormControlEditorAbstractComponent
@Input() focus: boolean = false;
get variant(): string {
return this.issues.length > 0 ? 'error' : 'default';
return this.invalidParams.length > 0 ? 'error' : 'default';
}
}
<ng-container *ngFor="let issue of issues"
><ods-error-message [text]="message(issue)"></ods-error-message
<ng-container *ngFor="let invalidParam of invalidParams"
><ods-error-message [text]="message(invalidParam)"></ods-error-message
></ng-container>
import { getMessageForIssue } from '@alfa-client/tech-shared';
import { getMessageForInvalidParam, InvalidParam } from '@alfa-client/tech-shared';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { createIssue } from 'libs/tech-shared/test/error';
import { ValidationMessageCode } from 'libs/tech-shared/src/lib/validation/tech.validation.messages';
import { createInvalidParam } from 'libs/tech-shared/test/error';
import { ValidationErrorComponent } from './validation-error.component';
describe('ValidationErrorComponent', () => {
......@@ -21,32 +22,30 @@ describe('ValidationErrorComponent', () => {
expect(component).toBeTruthy();
});
describe('get message for issue', () => {
describe('get message from invalidParam', () => {
const fieldLabel: string = 'Field Label';
const invalidParam: InvalidParam = {
...createInvalidParam(),
reason: ValidationMessageCode.VALIDATION_FIELD_SIZE,
messageCode: ValidationMessageCode.VALIDATION_FIELD_SIZE,
};
it('should return message', () => {
const msg: string = getMessageForIssue(fieldLabel, {
...createIssue(),
messageCode: 'validation_field_size',
});
it('should contain ', () => {
const msg: string = getMessageForInvalidParam(fieldLabel, invalidParam);
expect(msg).toContain('muss mindestens');
});
it('should set field label', () => {
const msg: string = getMessageForIssue(fieldLabel, {
...createIssue(),
messageCode: 'validation_field_size',
});
const msg: string = getMessageForInvalidParam(fieldLabel, invalidParam);
expect(msg).toContain(fieldLabel);
});
it('should replace min param', () => {
const msg: string = getMessageForIssue(fieldLabel, {
...createIssue(),
messageCode: 'validation_field_size',
parameters: [{ name: 'min', value: '3' }],
const msg: string = getMessageForInvalidParam(fieldLabel, {
...invalidParam,
constraintParameters: [{ name: 'min', value: '3' }],
});
expect(msg).toContain('3');
......
import { Issue, getMessageForIssue } from '@alfa-client/tech-shared';
import { InvalidParam, getMessageForInvalidParam } from '@alfa-client/tech-shared';
import { CommonModule } from '@angular/common';
import { Component, Input } from '@angular/core';
import { ErrorMessageComponent } from '@ods/system';
......@@ -11,9 +11,9 @@ import { ErrorMessageComponent } from '@ods/system';
})
export class ValidationErrorComponent {
@Input() label: string;
@Input() issues: Issue[];
@Input() invalidParams: InvalidParam[];
public message(issue: Issue): string {
return getMessageForIssue(this.label, issue);
public message(invalidParam: InvalidParam): string {
return getMessageForInvalidParam(this.label, invalidParam);
}
}
......@@ -30,14 +30,17 @@ import { map } from 'rxjs/operators';
import { hasStateResourceError, StateResource } from '../resource/resource.util';
import { ApiError, HttpError, InvalidParam, Issue, ProblemDetail } from '../tech.model';
import { isNotUndefined } from '../tech.util';
import { setInvalidParamValidationError, setIssueValidationError } from '../validation/tech.validation.util';
import {
setInvalidParamValidationError,
setIssueValidationError,
} from '../validation/tech.validation.util';
export abstract class AbstractFormService {
form: UntypedFormGroup;
pathPrefix: string;
source: any;
private readonly PROBLEM_DETAIL_INVALID_PARAMS_KEY: string = 'invalid-params';
private readonly PROBLEM_DETAIL_INVALID_PARAMS_KEY: string = 'invalidParams';
constructor(public formBuilder: UntypedFormBuilder) {
this.form = this.initForm();
......
......@@ -47,12 +47,20 @@ export interface ProblemDetail {
status: HttpStatusCode;
detail: string;
instance: string;
'invalid-params': InvalidParam[];
invalidParams: InvalidParam[];
}
export interface InvalidParam {
constraintParameters: ConstraintParameter[];
messageCode: ValidationMessageCode;
name: string;
reason: ValidationMessageCode;
value: string;
}
export interface ConstraintParameter {
name: string;
value: string;
}
export declare type HttpError = ProblemDetail | ApiError;
......
......@@ -24,6 +24,7 @@
export enum ValidationMessageCode {
VALIDATION_FIELD_FILE_SIZE_EXCEEDED = 'validation_field_file_size_exceeded',
VALIDATION_FIELD_EMPTY = 'validation_field_empty',
VALIDATION_FIELD_SIZE = 'validation_field_size',
VALIDATION_FIELD_FILE_CONTENT_TYPE_INVALID = 'validation_field_file_content_type_invalid',
}
......@@ -31,7 +32,8 @@ export const VALIDATION_MESSAGES: { [code: string]: string } = {
[ValidationMessageCode.VALIDATION_FIELD_EMPTY]: 'Bitte {field} ausfüllen',
validation_field_max_size: '{field} darf höchstens {max} Zeichen enthalten',
validation_field_min_size: '{field} muss aus mindestens {min} Zeichen bestehen',
validation_field_size: '{field} muss mindestens {min} und darf höchstens {max} Zeichen enthalten',
[ValidationMessageCode.VALIDATION_FIELD_SIZE]:
'{field} muss mindestens {min} und darf höchstens {max} Zeichen enthalten',
validation_field_date_past: 'Das Datum für {field} muss in der Zukunft liegen',
validation_field_invalid: 'Bitte {field} korrekt ausfüllen',
[ValidationMessageCode.VALIDATION_FIELD_FILE_SIZE_EXCEEDED]:
......
......@@ -28,13 +28,17 @@ 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,
getLastPart,
getMessageForInvalidParam,
getMessageForIssue,
getPartsAfterPrefix,
setInvalidParamValidationError,
setIssueValidationError,
} from './tech.validation.util';
......@@ -55,7 +59,7 @@ describe('ValidationUtils', () => {
describe('set issue validation error', () => {
describe('get control for issue', () => {
it('should return base field control', () => {
const issue: Issue = { ...createIssue(), field: 'baseField1' };
const issue: Issue = { ...createIssue(), field: 'class.resource.baseField1' };
const control: AbstractControl = getControlForIssue(form, issue);
......@@ -63,24 +67,24 @@ describe('ValidationUtils', () => {
});
it('should return sub group field', () => {
const issue: Issue = { ...createIssue(), field: 'subGroup.subGroupField1' };
const issue: Issue = { ...createIssue(), field: 'class.resource.subGroup.subGroupField1' };
const control: AbstractControl = getControlForIssue(form, issue);
const control: AbstractControl = getControlForIssue(form, issue, 'resource');
expect(control).toBe(subGroupFieldControl);
});
it('should ignore path prefix', () => {
const issue: Issue = { ...createIssue(), field: 'pathprefix.resource.baseField1' };
const issue: Issue = { ...createIssue(), field: 'class.resource.baseField1' };
const control: AbstractControl = getControlForIssue(form, issue, 'pathprefix.resource');
const control: AbstractControl = getControlForIssue(form, issue, 'resource');
expect(control).toBe(baseField1Control);
});
});
describe('in base field', () => {
const issue: Issue = { ...createIssue(), field: 'baseField1' };
const issue: Issue = { ...createIssue(), field: 'class.resource.baseField1' };
it('should set error in control', () => {
setIssueValidationError(form, issue);
......@@ -108,10 +112,10 @@ describe('ValidationUtils', () => {
});
describe('in subGroup Field', () => {
const issue: Issue = { ...createIssue(), field: 'subGroup.subGroupField1' };
const issue: Issue = { ...createIssue(), field: 'class.resource.subGroup.subGroupField1' };
it('should set error in control', () => {
setIssueValidationError(form, issue);
setIssueValidationError(form, issue, 'resource');
expect(subGroupFieldControl.errors).not.toBeNull();
});
......@@ -150,13 +154,14 @@ describe('ValidationUtils', () => {
});
});
describe('invalid param', () => {
describe.skip('invalid param', () => {
const formPrefixes: string[] = ['', 'some-prefix'];
const fieldNames: string[] = ['baseField1', 'baseField2', 'subGroup.subGroupField1'];
const prefixNameCombinations: string[][] = formPrefixes.flatMap((prefix) =>
fieldNames.map((name) => [prefix, name]),
);
const unknownName = 'unknown-field';
const fieldLabel: string = faker.lorem.word();
describe.each(prefixNameCombinations)(
'with prefix "%s" and fieldName "%s"',
......@@ -173,7 +178,7 @@ describe('ValidationUtils', () => {
describe('get message for invalid param', () => {
it('should return', () => {
const msg: string = getMessageForInvalidParam(invalidParam, prefix);
const msg: string = getMessageForInvalidParam(fieldLabel, invalidParam);
expect(msg).toEqual(`Bitte ${fieldName} ausfüllen`);
});
......@@ -189,7 +194,7 @@ describe('ValidationUtils', () => {
describe('set invalid param validation error', () => {
it('should assign invalidParam to form control error without prefix', () => {
const message: string = getMessageForInvalidParam(invalidParam, prefix);
const message: string = getMessageForInvalidParam(fieldLabel, invalidParam);
setInvalidParamValidationError(form, invalidParam, prefix);
......@@ -248,4 +253,50 @@ describe('ValidationUtils', () => {
},
);
});
describe('getFieldPath', () => {
const resourcePath: string = 'resource';
const backendClassName: string = 'class';
it('should return simple field path', () => {
const fieldPath: string = 'field1';
const fullPath: string = `${backendClassName}.${resourcePath}.${fieldPath}`;
const result: string = getFieldPath(fullPath);
expect(result).toBe(fieldPath);
});
it('should return hierarchical field path ', () => {
const fieldPath: string = 'fieldGroup.field1';
const fullPath: string = `${backendClassName}.${resourcePath}.${fieldPath}`;
const result: string = getFieldPath(fullPath, resourcePath);
expect(result).toBe(fieldPath);
});
});
describe('getLastPart', () => {
it('should get last term in string of terms', () => {
const lastPart: string = 'field';
const allParts: string = 'class.resource.' + lastPart;
const result: string = getLastPart(allParts);
expect(result).toBe(lastPart);
});
});
describe('getPartsAfterPrefix', () => {
it('should get all parts after the prefix', () => {
const lastParts: string = 'group.field';
const prefix: string = 'resource';
const allParts: string = 'class.' + prefix + '.' + lastParts;
const result: string = getPartsAfterPrefix(allParts, prefix);
expect(result).toBe(lastParts);
});
});
});
......@@ -22,9 +22,9 @@
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
import { isNil } from 'lodash-es';
import { isEmpty, isNil } from 'lodash-es';
import { ApiError, InvalidParam, Issue, IssueParam } from '../tech.model';
import { isNotNil, replacePlaceholder } from '../tech.util';
import { replacePlaceholder } from '../tech.util';
import { VALIDATION_MESSAGES, ValidationMessageCode } from './tech.validation.messages';
export function isValidationError(issue: Issue): boolean {
......@@ -47,7 +47,7 @@ export function getControlForIssue(
issue: Issue,
pathPrefix?: string,
): AbstractControl {
const fieldPath: string = getFieldPathWithoutPrefix(issue.field, pathPrefix);
const fieldPath: string = getFieldPath(issue.field, pathPrefix);
let curControl: AbstractControl = form;
fieldPath
......@@ -86,30 +86,44 @@ export function setInvalidParamValidationError(
pathPrefix?: string,
): void {
const control: AbstractControl = getControlForInvalidParam(form, invalidParam, pathPrefix);
if (isNotNil(control)) {
control.setErrors({
[invalidParam.reason]: getMessageForInvalidParam(invalidParam, pathPrefix),
});
control.setErrors({ [invalidParam.reason]: invalidParam });
control.markAsTouched();
}
}
export function getControlForInvalidParam(
form: UntypedFormGroup,
invalidParam: InvalidParam,
pathPrefix?: string,
): AbstractControl {
return form.get(getFieldPathWithoutPrefix(invalidParam.name, pathPrefix));
return form.get(getFieldPath(invalidParam.name, pathPrefix));
}
export function getMessageForInvalidParam(item: InvalidParam, pathPrefix: string): string {
return replacePlaceholder(
VALIDATION_MESSAGES[item.reason],
'field',
getFieldPathWithoutPrefix(item.name, pathPrefix),
export function getMessageForInvalidParam(label: string, invalidParam: InvalidParam): string {
let msg: string = VALIDATION_MESSAGES[invalidParam.reason];
if (isNil(msg)) {
console.warn('No message for code ' + invalidParam.reason + ' found.');
return invalidParam.reason;
}
msg = replacePlaceholder(msg, 'field', label);
invalidParam.constraintParameters.forEach(
(param: IssueParam) => (msg = replacePlaceholder(msg, param.name, param.value)),
);
return msg;
}
export function getFieldPath(name: string, pathPrefix?: string): string {
return isEmpty(pathPrefix) ? getLastPart(name) : getPartsAfterPrefix(name, pathPrefix);
}
export function getLastPart(name: string): string {
return name.split('.').pop();
}
function getFieldPathWithoutPrefix(name: string, pathPrefix?: string): string {
return pathPrefix ? name.substring(pathPrefix.length + 1) : name;
export function getPartsAfterPrefix(name: string, pathPrefix: string): string {
pathPrefix = `${pathPrefix}.`;
const indexField = name.lastIndexOf(pathPrefix) + pathPrefix.length;
return name.slice(indexField);
}
......@@ -23,7 +23,14 @@
*/
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { faker } from '@faker-js/faker';
import { ApiError, InvalidParam, Issue, IssueParam, ProblemDetail } from '../src/lib/tech.model';
import {
ApiError,
ConstraintParameter,
InvalidParam,
Issue,
IssueParam,
ProblemDetail,
} from '../src/lib/tech.model';
import { ValidationMessageCode } from '../src/lib/validation/tech.validation.messages';
export function createIssueParam(): IssueParam {
......@@ -63,10 +70,23 @@ export function createProblemDetail(
type: faker.random.word(),
instance: faker.internet.url(),
detail: faker.random.word(),
'invalid-params': invalidParams,
invalidParams: invalidParams,
};
}
export function createInvalidParam(): InvalidParam {
return { name: faker.random.word(), reason: ValidationMessageCode.VALIDATION_FIELD_EMPTY };
return {
name: faker.random.word(),
reason: ValidationMessageCode.VALIDATION_FIELD_EMPTY,
value: faker.random.words(10),
messageCode: ValidationMessageCode.VALIDATION_FIELD_EMPTY,
constraintParameters: [createInvalidParamConstraintParameter()],
};
}
export function createInvalidParamConstraintParameter(): ConstraintParameter {
return {
name: faker.random.word(),
value: faker.random.word(),
};
}
......@@ -54,7 +54,7 @@
<mat-error>
<ozgcloud-validation-error
[attr.data-test-id]="(label | convertForDataTest) + '-autocomplete-error'"
[issues]="issues"
[invalidParams]="invalidParams"
[label]="label"
>
</ozgcloud-validation-error>
......
......@@ -43,7 +43,7 @@
<mat-error>
<ozgcloud-validation-error
[attr.data-test-id]="(label | convertForDataTest) + '-date-error'"
[issues]="issues"
[invalidParams]="invalidParams"
[label]="label"
></ozgcloud-validation-error>
</mat-error>
......
......@@ -48,7 +48,7 @@
<mat-error>
<ozgcloud-validation-error
[attr.data-test-id]="(label | convertForDataTest) + '-file-upload-error'"
[issues]="issues"
[invalidParams]="invalidParams"
[label]="label"
>
</ozgcloud-validation-error>
......
......@@ -58,7 +58,7 @@
<mat-error>
<ozgcloud-validation-error
[attr.data-test-id]="(getPlaceholderLabel() | convertForDataTest) + '-text-error'"
[issues]="issues"
[invalidParams]="invalidParams"
[label]="getPlaceholderLabel()"
></ozgcloud-validation-error>
</mat-error>
......
......@@ -39,8 +39,8 @@
<mat-error>
<ozgcloud-validation-error
[issues]="issues"
[label]="label"
[invalidParams]="invalidParams"
[attr.data-test-id]="(label | convertForDataTest) + '-textarea-error'"
>
</ozgcloud-validation-error>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment