@contember/schema-utils 1.3.0-beta.4 → 1.3.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/type-schema/model.d.ts.map +1 -1
- package/dist/src/type-schema/model.js +1 -1
- package/dist/src/type-schema/model.js.map +1 -1
- package/dist/src/type-schema/settings.d.ts +11 -0
- package/dist/src/type-schema/settings.d.ts.map +1 -1
- package/dist/src/type-schema/settings.js +3 -0
- package/dist/src/type-schema/settings.js.map +1 -1
- package/dist/src/validation/ModelValidator.d.ts +5 -1
- package/dist/src/validation/ModelValidator.d.ts.map +1 -1
- package/dist/src/validation/ModelValidator.js +66 -12
- package/dist/src/validation/ModelValidator.js.map +1 -1
- package/dist/src/validation/errors.d.ts +1 -1
- package/dist/src/validation/errors.d.ts.map +1 -1
- package/dist/src/validation/errors.js.map +1 -1
- package/dist/tests/cases/unit/modelValidator.test.js +22 -0
- package/dist/tests/cases/unit/modelValidator.test.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/type-schema/model.ts +6 -1
- package/src/type-schema/settings.ts +3 -0
- package/src/validation/ModelValidator.ts +74 -13
- package/src/validation/errors.ts +1 -0
- package/tests/cases/unit/modelValidator.test.ts +20 -3
|
@@ -13,7 +13,7 @@ export class ModelValidator {
|
|
|
13
13
|
const errorBuilder = new ErrorBuilder([], [])
|
|
14
14
|
this.validateEnums(this.model.enums, errorBuilder.for('enums'))
|
|
15
15
|
this.validateEntities(this.model.entities, errorBuilder.for('entities'))
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
return errorBuilder.errors
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -34,10 +34,15 @@ export class ModelValidator {
|
|
|
34
34
|
}
|
|
35
35
|
this.validateEntity(entity, entityErrors)
|
|
36
36
|
}
|
|
37
|
+
const entitiesArr = Object.values(this.model.entities)
|
|
38
|
+
this.validateMetaSuffixCollisions(entitiesArr, errors)
|
|
39
|
+
this.validateTableNameCollisions(entitiesArr, errors)
|
|
40
|
+
this.validateAliasedTypesCollision(entitiesArr, errors)
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
private validateEntity(entity: Model.Entity, errors: ErrorBuilder): void {
|
|
40
44
|
this.validateIdentifier(entity.name, errors)
|
|
45
|
+
this.validateEntityName(entity.name, errors)
|
|
41
46
|
|
|
42
47
|
for (const [fieldName, field] of Object.entries(entity.fields)) {
|
|
43
48
|
const fieldErrors = errors.for(fieldName)
|
|
@@ -51,6 +56,7 @@ export class ModelValidator {
|
|
|
51
56
|
new Set(Object.keys(entity.fields)),
|
|
52
57
|
errors.for('unique'),
|
|
53
58
|
)
|
|
59
|
+
this.validateColumnNamesCollision(entity, errors)
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
private validateUniqueConstraints(uniqueConstraints: Model.Entity['unique'], fields: Set<string>, errors: ErrorBuilder): void {
|
|
@@ -63,6 +69,34 @@ export class ModelValidator {
|
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
private validateColumnNamesCollision(entity: Model.Entity, errors: ErrorBuilder): void {
|
|
73
|
+
const existingColumnNames = new Map<string, string>()
|
|
74
|
+
const addColumnName = (fieldName: string, columnName: string) => {
|
|
75
|
+
if (existingColumnNames.has(columnName)) {
|
|
76
|
+
const exitingName = existingColumnNames.get(columnName)
|
|
77
|
+
errors
|
|
78
|
+
.for(fieldName)
|
|
79
|
+
.add('MODEL_NAME_COLLISION', `Column name "${columnName}" on field "${fieldName}" collides with a column name on field "${exitingName}".`)
|
|
80
|
+
}
|
|
81
|
+
existingColumnNames.set(columnName, fieldName)
|
|
82
|
+
}
|
|
83
|
+
acceptEveryFieldVisitor(this.model, entity, {
|
|
84
|
+
visitColumn: ({ column }) => {
|
|
85
|
+
addColumnName(column.name, column.columnName)
|
|
86
|
+
},
|
|
87
|
+
visitOneHasOneOwning: ({ relation }) => {
|
|
88
|
+
addColumnName(relation.name, relation.joiningColumn.columnName)
|
|
89
|
+
},
|
|
90
|
+
visitManyHasOne: ({ relation }) => {
|
|
91
|
+
addColumnName(relation.name, relation.joiningColumn.columnName)
|
|
92
|
+
},
|
|
93
|
+
visitOneHasOneInverse: () => {},
|
|
94
|
+
visitOneHasMany: () => {},
|
|
95
|
+
visitManyHasManyInverse: () => {},
|
|
96
|
+
visitManyHasManyOwning: () => {},
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
66
100
|
private validateField(partialEntity: Model.Entity, field: Model.AnyField, errors: ErrorBuilder): void {
|
|
67
101
|
this.validateIdentifier(field.name, errors)
|
|
68
102
|
if (isRelation(field)) {
|
|
@@ -204,10 +238,29 @@ export class ModelValidator {
|
|
|
204
238
|
}
|
|
205
239
|
}
|
|
206
240
|
|
|
207
|
-
private
|
|
208
|
-
|
|
209
|
-
|
|
241
|
+
private validateEntityName(value: string, errorBuilder: ErrorBuilder) {
|
|
242
|
+
if (['Query', 'Mutation'].includes(value)) {
|
|
243
|
+
errorBuilder.add('MODEL_INVALID_ENTITY_NAME', `${value} is reserved word`)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
private validateMetaSuffixCollisions(entities: Model.Entity[], errorBuilder: ErrorBuilder) {
|
|
210
249
|
const entityNames = new Set(entities.map(it => it.name))
|
|
250
|
+
for (const entity of entities) {
|
|
251
|
+
if (entity.name.endsWith('Meta')) {
|
|
252
|
+
const baseName = entity.name.substring(0, entity.name.length - 4)
|
|
253
|
+
if (entityNames.has(baseName)) {
|
|
254
|
+
errorBuilder.for(entity.name)
|
|
255
|
+
.add('MODEL_NAME_COLLISION', `entity ${entity.name} collides with entity ${baseName}, because a GraphQL type with "Meta" suffix is created for every entity`)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
private validateTableNameCollisions(entities: Model.Entity[], errorBuilder: ErrorBuilder) {
|
|
263
|
+
const relationNames: Record<string, string> = {}
|
|
211
264
|
for (const entity of entities) {
|
|
212
265
|
const description = `table name ${entity.tableName} of entity ${entity.name}`
|
|
213
266
|
if (relationNames[entity.tableName]) {
|
|
@@ -217,14 +270,6 @@ export class ModelValidator {
|
|
|
217
270
|
} else {
|
|
218
271
|
relationNames[entity.tableName] = description
|
|
219
272
|
}
|
|
220
|
-
|
|
221
|
-
if (entity.name.endsWith('Meta')) {
|
|
222
|
-
const baseName = entity.name.substring(0, entity.name.length - 4)
|
|
223
|
-
if (entityNames.has(baseName)) {
|
|
224
|
-
errorBuilder.for(entity.name)
|
|
225
|
-
.add('MODEL_NAME_COLLISION', `entity ${entity.name} collides with entity ${baseName}, because a GraphQL type with "Meta" suffix is created for every entity`)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
273
|
}
|
|
229
274
|
for (const entity of entities) {
|
|
230
275
|
const entityErrorBuilder = errorBuilder.for(entity.name)
|
|
@@ -243,7 +288,22 @@ export class ModelValidator {
|
|
|
243
288
|
relationNames[joiningTable.tableName] = description
|
|
244
289
|
}
|
|
245
290
|
},
|
|
246
|
-
visitColumn: (
|
|
291
|
+
visitColumn: () => {},
|
|
292
|
+
visitManyHasManyInverse: () => { },
|
|
293
|
+
visitOneHasMany: () => { },
|
|
294
|
+
visitOneHasOneInverse: () => { },
|
|
295
|
+
visitOneHasOneOwning: () => { },
|
|
296
|
+
visitManyHasOne: () => { },
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
private validateAliasedTypesCollision(entities: Model.Entity[], errorBuilder: ErrorBuilder) {
|
|
303
|
+
const aliasedTypes = new Map<string, Model.ColumnType>()
|
|
304
|
+
for (const entity of entities) {
|
|
305
|
+
acceptEveryFieldVisitor(this.model, entity, {
|
|
306
|
+
visitColumn: ({ column }) => {
|
|
247
307
|
if (!column.typeAlias) {
|
|
248
308
|
return
|
|
249
309
|
}
|
|
@@ -254,6 +314,7 @@ export class ModelValidator {
|
|
|
254
314
|
}
|
|
255
315
|
aliasedTypes.set(column.typeAlias, column.type)
|
|
256
316
|
},
|
|
317
|
+
visitManyHasManyOwning: () => { },
|
|
257
318
|
visitManyHasManyInverse: () => { },
|
|
258
319
|
visitOneHasMany: () => { },
|
|
259
320
|
visitOneHasOneInverse: () => { },
|
package/src/validation/errors.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { assert, test } from 'vitest'
|
|
2
2
|
import { Model } from '@contember/schema'
|
|
3
3
|
import { ModelValidator } from '../../../src'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { c, createSchema } from '@contember/schema-definition'
|
|
7
5
|
|
|
8
6
|
test('"meta" collision', () => {
|
|
9
7
|
const model: Model.Schema = {
|
|
@@ -74,3 +72,22 @@ test('"meta" collision', () => {
|
|
|
74
72
|
},
|
|
75
73
|
])
|
|
76
74
|
})
|
|
75
|
+
|
|
76
|
+
namespace ColumnNameCollision {
|
|
77
|
+
export class Bar {
|
|
78
|
+
rel = c.oneHasOne(Bar)
|
|
79
|
+
relId = c.intColumn()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
test('column name collision', () => {
|
|
84
|
+
const schema = createSchema(ColumnNameCollision)
|
|
85
|
+
const validator = new ModelValidator(schema.model)
|
|
86
|
+
assert.deepStrictEqual(validator.validate(), [
|
|
87
|
+
{
|
|
88
|
+
code: 'MODEL_NAME_COLLISION',
|
|
89
|
+
message: 'Column name "rel_id" on field "relId" collides with a column name on field "rel".',
|
|
90
|
+
path: ['entities', 'Bar', 'relId'],
|
|
91
|
+
},
|
|
92
|
+
])
|
|
93
|
+
})
|