|
1
|
|
|
import type { Entity } from '@shopware-ag/admin-extension-sdk/es/data/_internals/Entity'; |
|
2
|
|
|
import type ChangesetGenerator from 'src/core/data/changeset-generator.data'; |
|
3
|
|
|
import type EntityDefinition from 'src/core/data/entity-definition.data'; |
|
4
|
|
|
import type ErrorResolver from 'src/core/data/error-resolver.data'; |
|
5
|
|
|
import type EntityDefinitionFactory from 'src/core/factory/entity-definition.factory'; |
|
6
|
|
|
|
|
7
|
|
|
/** |
|
8
|
|
|
* @module app/entity-validation-service |
|
9
|
|
|
*/ |
|
10
|
|
|
|
|
11
|
|
|
/** |
|
12
|
|
|
* @private |
|
13
|
|
|
*/ |
|
14
|
|
|
export type ValidationError = { |
|
15
|
|
|
code: string, |
|
16
|
|
|
source: { |
|
17
|
|
|
pointer: string |
|
18
|
|
|
} |
|
19
|
|
|
} |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* @private |
|
23
|
|
|
*/ |
|
24
|
|
|
export type CustomValidator = (errors: ValidationError[], entity: Entity, definition: EntityDefinition) => ValidationError[]; |
|
25
|
|
|
|
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* A service for client side validation of entities |
|
29
|
|
|
* @private |
|
30
|
|
|
*/ |
|
31
|
|
|
export default class EntityValidationService { |
|
32
|
|
|
private entityDefinitionFactory: typeof EntityDefinitionFactory; |
|
33
|
|
|
|
|
34
|
|
|
private changesetGenerator: ChangesetGenerator; |
|
35
|
|
|
|
|
36
|
|
|
private errorResolver: ErrorResolver; |
|
37
|
|
|
|
|
38
|
|
|
public static readonly ERROR_CODE_REQUIRED = 'c1051bb4-d103-4f74-8988-acbcafc7fdc3'; |
|
39
|
|
|
|
|
40
|
|
|
constructor( |
|
41
|
|
|
entityDefinitionFactory: typeof EntityDefinitionFactory, |
|
42
|
|
|
changesetGenerator: ChangesetGenerator, |
|
43
|
|
|
errorResolver: ErrorResolver, |
|
44
|
|
|
) { |
|
45
|
|
|
this.entityDefinitionFactory = entityDefinitionFactory; |
|
46
|
|
|
this.changesetGenerator = changesetGenerator; |
|
47
|
|
|
this.errorResolver = errorResolver; |
|
48
|
|
|
} |
|
49
|
|
|
|
|
50
|
|
|
public static createRequiredError(fieldPointer: string): ValidationError { |
|
51
|
|
|
return { |
|
52
|
|
|
code: EntityValidationService.ERROR_CODE_REQUIRED, |
|
53
|
|
|
source: { |
|
54
|
|
|
pointer: fieldPointer, |
|
55
|
|
|
}, |
|
56
|
|
|
}; |
|
57
|
|
|
} |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* Validates an entity and returns true if it is valid. |
|
61
|
|
|
* Found errors are reported to the internal error resolver and |
|
62
|
|
|
* displayed by looking into the error Store (is done automatically for most sw-fields). |
|
63
|
|
|
* |
|
64
|
|
|
* A CustomValidator callback can be provided to modify or add to the found errors. |
|
65
|
|
|
*/ |
|
66
|
|
|
public validate(entity: Entity, customValidator: CustomValidator | undefined): boolean { |
|
67
|
|
|
const entityName = entity.getEntityName(); |
|
68
|
|
|
const definition = this.entityDefinitionFactory.get(entityName); |
|
69
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
|
70
|
|
|
const { changes } = this.changesetGenerator.generate(entity); |
|
71
|
|
|
let errors: ValidationError[] = []; |
|
72
|
|
|
|
|
73
|
|
|
// check for required fields |
|
74
|
|
|
const requiredFields = definition.getRequiredFields() as Record<string, never>; |
|
75
|
|
|
errors.push(...this.getRequiredErrors(entity, requiredFields)); |
|
76
|
|
|
|
|
77
|
|
|
// run custom validator |
|
78
|
|
|
if (customValidator !== undefined) { |
|
79
|
|
|
errors = customValidator(errors, entity, definition); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
// report errors |
|
83
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
|
84
|
|
|
this.errorResolver.handleWriteErrors({ errors }, [{ entity, changes }]); |
|
85
|
|
|
return errors.length < 1; |
|
86
|
|
|
} |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* Tries to find all the required fields which are not set in the given entity. |
|
90
|
|
|
* TODO: This implementation may only find required fields on the top level and may needs further improvement |
|
91
|
|
|
* for other use cases. |
|
92
|
|
|
* |
|
93
|
|
|
* @private |
|
94
|
|
|
*/ |
|
95
|
|
|
private getRequiredErrors(entity: Entity, requiredFields: Record<string, never>): ValidationError[] { |
|
96
|
|
|
const errors: ValidationError[] = []; |
|
97
|
|
|
|
|
98
|
|
|
Object.keys(requiredFields).forEach((field) => { |
|
99
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment |
|
100
|
|
|
const fieldDefinition = requiredFields[field] as any; |
|
101
|
|
|
// @ts-expect-error |
|
102
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment |
|
103
|
|
|
const value = entity[field] as any; |
|
104
|
|
|
const fieldFilterRegex = new RegExp('version|createdAt|translations', 'i'); |
|
105
|
|
|
|
|
106
|
|
|
if (fieldFilterRegex.test(field)) { |
|
107
|
|
|
return; |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
|
111
|
|
|
if (field.includes('price') && fieldDefinition.type === 'json_object' && Array.isArray(value)) { |
|
112
|
|
|
// detected price field -> custom handling of price fields |
|
113
|
|
|
value.forEach((price, index) => { |
|
114
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
|
115
|
|
|
if (price.net === undefined || price.net === null) { |
|
116
|
|
|
errors.push(EntityValidationService.createRequiredError(`/0/${field}/${index}/net`)); |
|
117
|
|
|
} |
|
118
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
|
119
|
|
|
if (price.gross === undefined || price.gross === null) { |
|
120
|
|
|
errors.push(EntityValidationService.createRequiredError(`/0/${field}/${index}/gross`)); |
|
121
|
|
|
} |
|
122
|
|
|
}); |
|
123
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access |
|
124
|
|
|
} else if (value === undefined || (fieldDefinition.type === 'string' && value === '')) { |
|
125
|
|
|
// any other field |
|
126
|
|
|
errors.push(EntityValidationService.createRequiredError(`/0/${field}`)); |
|
127
|
|
|
} |
|
128
|
|
|
}); |
|
129
|
|
|
|
|
130
|
|
|
return errors; |
|
131
|
|
|
} |
|
132
|
|
|
} |
|
133
|
|
|
|