@contember/schema-utils 2.0.2 → 2.1.0-alpha.10
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/development/index.cjs +0 -1
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +1 -2
- package/dist/development/src/dataUtils.cjs +9 -9
- package/dist/development/src/dataUtils.cjs.map +1 -1
- package/dist/development/src/dataUtils.js +10 -10
- package/dist/development/src/dataUtils.js.map +1 -1
- package/dist/development/src/definition-generator/DefinitionCodeGenerator.cjs +1 -0
- package/dist/development/src/definition-generator/DefinitionCodeGenerator.cjs.map +1 -1
- package/dist/development/src/definition-generator/DefinitionCodeGenerator.js +1 -0
- package/dist/development/src/definition-generator/DefinitionCodeGenerator.js.map +1 -1
- package/dist/development/src/model/modelUtils.cjs +1 -1
- package/dist/development/src/model/modelUtils.cjs.map +1 -1
- package/dist/development/src/model/modelUtils.js +1 -1
- package/dist/development/src/model/modelUtils.js.map +1 -1
- package/dist/development/src/model/resolveDefaultColumnType.cjs +2 -0
- package/dist/development/src/model/resolveDefaultColumnType.cjs.map +1 -1
- package/dist/development/src/model/resolveDefaultColumnType.js +2 -0
- package/dist/development/src/model/resolveDefaultColumnType.js.map +1 -1
- package/dist/development/src/schemaNormalizer.cjs +2 -0
- package/dist/development/src/schemaNormalizer.cjs.map +1 -1
- package/dist/development/src/schemaNormalizer.js +2 -0
- package/dist/development/src/schemaNormalizer.js.map +1 -1
- package/dist/development/src/type-schema/acl.cjs +3 -1
- package/dist/development/src/type-schema/acl.cjs.map +1 -1
- package/dist/development/src/type-schema/acl.js +3 -1
- package/dist/development/src/type-schema/acl.js.map +1 -1
- package/dist/development/src/type-schema/condition.cjs +2 -0
- package/dist/development/src/type-schema/condition.cjs.map +1 -1
- package/dist/development/src/type-schema/condition.js +2 -0
- package/dist/development/src/type-schema/condition.js.map +1 -1
- package/dist/development/src/type-schema/model.cjs +25 -8
- package/dist/development/src/type-schema/model.cjs.map +1 -1
- package/dist/development/src/type-schema/model.js +25 -8
- package/dist/development/src/type-schema/model.js.map +1 -1
- package/dist/development/src/type-schema/settings.cjs +2 -1
- package/dist/development/src/type-schema/settings.cjs.map +1 -1
- package/dist/development/src/type-schema/settings.js +2 -1
- package/dist/development/src/type-schema/settings.js.map +1 -1
- package/dist/development/src/validation/ModelValidator.cjs +10 -10
- package/dist/development/src/validation/ModelValidator.cjs.map +1 -1
- package/dist/development/src/validation/ModelValidator.js +11 -11
- package/dist/development/src/validation/ModelValidator.js.map +1 -1
- package/dist/development/src/validation/errors.cjs.map +1 -1
- package/dist/development/src/validation/errors.js.map +1 -1
- package/dist/production/index.cjs +0 -1
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.js +1 -2
- package/dist/production/src/dataUtils.cjs +9 -9
- package/dist/production/src/dataUtils.cjs.map +1 -1
- package/dist/production/src/dataUtils.js +10 -10
- package/dist/production/src/dataUtils.js.map +1 -1
- package/dist/production/src/definition-generator/DefinitionCodeGenerator.cjs +1 -0
- package/dist/production/src/definition-generator/DefinitionCodeGenerator.cjs.map +1 -1
- package/dist/production/src/definition-generator/DefinitionCodeGenerator.js +1 -0
- package/dist/production/src/definition-generator/DefinitionCodeGenerator.js.map +1 -1
- package/dist/production/src/model/modelUtils.cjs +1 -1
- package/dist/production/src/model/modelUtils.cjs.map +1 -1
- package/dist/production/src/model/modelUtils.js +1 -1
- package/dist/production/src/model/modelUtils.js.map +1 -1
- package/dist/production/src/model/resolveDefaultColumnType.cjs +2 -0
- package/dist/production/src/model/resolveDefaultColumnType.cjs.map +1 -1
- package/dist/production/src/model/resolveDefaultColumnType.js +2 -0
- package/dist/production/src/model/resolveDefaultColumnType.js.map +1 -1
- package/dist/production/src/schemaNormalizer.cjs +2 -0
- package/dist/production/src/schemaNormalizer.cjs.map +1 -1
- package/dist/production/src/schemaNormalizer.js +2 -0
- package/dist/production/src/schemaNormalizer.js.map +1 -1
- package/dist/production/src/type-schema/acl.cjs +3 -1
- package/dist/production/src/type-schema/acl.cjs.map +1 -1
- package/dist/production/src/type-schema/acl.js +3 -1
- package/dist/production/src/type-schema/acl.js.map +1 -1
- package/dist/production/src/type-schema/condition.cjs +2 -0
- package/dist/production/src/type-schema/condition.cjs.map +1 -1
- package/dist/production/src/type-schema/condition.js +2 -0
- package/dist/production/src/type-schema/condition.js.map +1 -1
- package/dist/production/src/type-schema/model.cjs +25 -8
- package/dist/production/src/type-schema/model.cjs.map +1 -1
- package/dist/production/src/type-schema/model.js +25 -8
- package/dist/production/src/type-schema/model.js.map +1 -1
- package/dist/production/src/type-schema/settings.cjs +2 -1
- package/dist/production/src/type-schema/settings.cjs.map +1 -1
- package/dist/production/src/type-schema/settings.js +2 -1
- package/dist/production/src/type-schema/settings.js.map +1 -1
- package/dist/production/src/validation/ModelValidator.cjs +10 -10
- package/dist/production/src/validation/ModelValidator.cjs.map +1 -1
- package/dist/production/src/validation/ModelValidator.js +11 -11
- package/dist/production/src/validation/ModelValidator.js.map +1 -1
- package/dist/production/src/validation/errors.cjs.map +1 -1
- package/dist/production/src/validation/errors.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/acl/schemaUtils.d.ts.map +1 -1
- package/dist/types/dataUtils.d.ts +7 -4
- package/dist/types/dataUtils.d.ts.map +1 -1
- package/dist/types/model/modelUtils.d.ts.map +1 -1
- package/dist/types/model/resolveDefaultColumnType.d.ts.map +1 -1
- package/dist/types/schemaChecksum.d.ts.map +1 -1
- package/dist/types/schemaFilter.d.ts.map +1 -1
- package/dist/types/schemaNormalizer.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dist/types/type-schema/acl.d.ts.map +1 -1
- package/dist/types/type-schema/condition.d.ts.map +1 -1
- package/dist/types/type-schema/model.d.ts +1 -1
- package/dist/types/type-schema/model.d.ts.map +1 -1
- package/dist/types/type-schema/settings.d.ts +3 -0
- package/dist/types/type-schema/settings.d.ts.map +1 -1
- package/dist/types/type-schema/where.d.ts.map +1 -1
- package/dist/types/utils/deepCompare.d.ts.map +1 -1
- package/dist/types/utils/isIt.d.ts.map +1 -1
- package/dist/types/utils/printJsValue.d.ts.map +1 -1
- package/dist/types/validation/ModelValidator.d.ts.map +1 -1
- package/dist/types/validation/errors.d.ts +1 -1
- package/dist/types/validation/errors.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/dataUtils.ts +12 -9
- package/src/definition-generator/DefinitionCodeGenerator.ts +2 -1
- package/src/model/modelUtils.ts +1 -1
- package/src/model/resolveDefaultColumnType.ts +2 -0
- package/src/schemaNormalizer.ts +2 -0
- package/src/type-schema/acl.ts +2 -0
- package/src/type-schema/condition.ts +1 -0
- package/src/type-schema/model.ts +25 -6
- package/src/type-schema/settings.ts +1 -0
- package/src/validation/ModelValidator.ts +11 -11
- package/src/validation/errors.ts +2 -0
- package/tests/cases/unit/modelValidator.test.ts +19 -0
package/src/dataUtils.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Input, Model, Value } from '@contember/schema'
|
|
2
2
|
|
|
3
3
|
export interface Providers {
|
|
4
|
-
uuid: () => string
|
|
4
|
+
uuid: (args?: { version?: 4 | 7 }) => string
|
|
5
5
|
now: () => Date
|
|
6
6
|
}
|
|
7
7
|
|
|
@@ -25,9 +25,13 @@ export const resolveDefaultValue = (column: Model.AnyColumn, providers: Pick<Pro
|
|
|
25
25
|
break
|
|
26
26
|
case Model.ColumnType.DateTime:
|
|
27
27
|
case Model.ColumnType.Date:
|
|
28
|
+
case Model.ColumnType.Time:
|
|
28
29
|
if (column.default === 'now') {
|
|
29
30
|
return providers.now().toISOString()
|
|
30
31
|
}
|
|
32
|
+
if (typeof column.default !== 'undefined') {
|
|
33
|
+
return column.default
|
|
34
|
+
}
|
|
31
35
|
break
|
|
32
36
|
case Model.ColumnType.Uuid:
|
|
33
37
|
break
|
|
@@ -42,13 +46,6 @@ export const resolveDefaultValue = (column: Model.AnyColumn, providers: Pick<Pro
|
|
|
42
46
|
throw new NoDataError(`No data for column ${column.name}`)
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
export const resolvePrimaryGenerator = (column: Model.AnyColumn, providers: Providers): (() => Input.PrimaryValue | undefined) => {
|
|
46
|
-
if (column.type === Model.ColumnType.Uuid) {
|
|
47
|
-
return providers.uuid
|
|
48
|
-
}
|
|
49
|
-
return () => undefined
|
|
50
|
-
}
|
|
51
|
-
|
|
52
49
|
export const resolveColumnValue = (
|
|
53
50
|
{
|
|
54
51
|
entity,
|
|
@@ -60,12 +57,18 @@ export const resolveColumnValue = (
|
|
|
60
57
|
input: Input.ColumnValue | undefined
|
|
61
58
|
},
|
|
62
59
|
providers: Providers,
|
|
60
|
+
options: {
|
|
61
|
+
uuidVersion?: 4 | 7
|
|
62
|
+
},
|
|
63
63
|
): Value.FieldValue | undefined => {
|
|
64
64
|
if (input !== undefined) {
|
|
65
65
|
return input
|
|
66
66
|
}
|
|
67
67
|
if (entity.primary === column.name) {
|
|
68
|
-
|
|
68
|
+
if (column.type === Model.ColumnType.Uuid) {
|
|
69
|
+
return providers.uuid({ version: options.uuidVersion })
|
|
70
|
+
}
|
|
71
|
+
return undefined
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
return resolveDefaultValue(column, providers)
|
|
@@ -45,7 +45,7 @@ ${Object.values(entity.fields).map(field => this.generateField({ field, entity,
|
|
|
45
45
|
}\n`
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
private generateUniqueConstraint({ entity, constraint }: { entity: Model.Entity; constraint: Model.UniqueConstraint }): string {
|
|
48
|
+
private generateUniqueConstraint({ entity, constraint }: { entity: Model.Entity; constraint: Model.UniqueConstraint | Model.UniqueIndex }): string {
|
|
49
49
|
const fieldsList = `${constraint.fields.map(it => printJsValue(it)).join(', ')}`
|
|
50
50
|
|
|
51
51
|
return `@c.Unique(${fieldsList})`
|
|
@@ -234,6 +234,7 @@ const ColumnToMethodMapping: {
|
|
|
234
234
|
[Model.ColumnType.Bool]: 'boolColumn',
|
|
235
235
|
[Model.ColumnType.Date]: 'dateColumn',
|
|
236
236
|
[Model.ColumnType.DateTime]: 'dateTimeColumn',
|
|
237
|
+
[Model.ColumnType.Time]: 'timeColumn',
|
|
237
238
|
[Model.ColumnType.Json]: 'jsonColumn',
|
|
238
239
|
[Model.ColumnType.Double]: 'doubleColumn',
|
|
239
240
|
[Model.ColumnType.Uuid]: 'uuidColumn',
|
package/src/model/modelUtils.ts
CHANGED
|
@@ -65,7 +65,7 @@ export const tryGetColumnName = (schema: Model.Schema, entity: Model.Entity, fie
|
|
|
65
65
|
export const getColumnType = (schema: Model.Schema, entity: Model.Entity, fieldName: string): string => {
|
|
66
66
|
return acceptFieldVisitor(schema, entity, fieldName, {
|
|
67
67
|
// TODO solve enum handling properly maybe we should distinguish between domain and column type
|
|
68
|
-
visitColumn: ({ column }) => (column.type === Model.ColumnType.Enum ? 'text' : column.columnType),
|
|
68
|
+
visitColumn: ({ column }) => (column.type === Model.ColumnType.Enum ? 'text' : column.columnType) + (column.list ? '[]' : ''),
|
|
69
69
|
visitRelation: ({ entity, relation, targetEntity }) => {
|
|
70
70
|
if (isIt<Model.JoiningColumnRelation>(relation, 'joiningColumn')) {
|
|
71
71
|
return getColumnType(schema, targetEntity, targetEntity.primary)
|
|
@@ -17,6 +17,8 @@ export const resolveDefaultColumnType = (type: Exclude<Model.ColumnType, Model.C
|
|
|
17
17
|
return 'timestamptz'
|
|
18
18
|
case Model.ColumnType.Date:
|
|
19
19
|
return 'date'
|
|
20
|
+
case Model.ColumnType.Time:
|
|
21
|
+
return 'time'
|
|
20
22
|
case Model.ColumnType.Json:
|
|
21
23
|
return 'jsonb'
|
|
22
24
|
default:
|
package/src/schemaNormalizer.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const normalizeSchema = <S extends Schema>(schema: S): S => {
|
|
|
24
24
|
content: {
|
|
25
25
|
export: true,
|
|
26
26
|
import: true,
|
|
27
|
+
refreshMaterializedView: true,
|
|
27
28
|
...adminRoleDefinition.content,
|
|
28
29
|
},
|
|
29
30
|
system: {
|
|
@@ -47,6 +48,7 @@ export const normalizeSchema = <S extends Schema>(schema: S): S => {
|
|
|
47
48
|
content: {
|
|
48
49
|
export: true,
|
|
49
50
|
import: true,
|
|
51
|
+
refreshMaterializedView: true,
|
|
50
52
|
...contentAdminRoleDefinition.content,
|
|
51
53
|
},
|
|
52
54
|
system: {
|
package/src/type-schema/acl.ts
CHANGED
|
@@ -39,6 +39,7 @@ const contentPermissionsSchema = Typesafe.partial({
|
|
|
39
39
|
assumeMembership: membershipMatchRuleSchema,
|
|
40
40
|
export: Typesafe.boolean,
|
|
41
41
|
import: Typesafe.boolean,
|
|
42
|
+
refreshMaterializedView: Typesafe.boolean,
|
|
42
43
|
})
|
|
43
44
|
const contentSchemaCheck: Typesafe.Equals<Acl.ContentPermissions, ReturnType<typeof contentPermissionsSchema>> = true
|
|
44
45
|
|
|
@@ -95,6 +96,7 @@ const entityOperationsSchema = Typesafe.partial({
|
|
|
95
96
|
update: fieldPermissionsSchema,
|
|
96
97
|
delete: Typesafe.union(Typesafe.string, Typesafe.boolean),
|
|
97
98
|
customPrimary: Typesafe.boolean,
|
|
99
|
+
refreshMaterializedView: Typesafe.boolean,
|
|
98
100
|
noRoot: Typesafe.array(Typesafe.enumeration('create', 'read', 'update', 'delete')),
|
|
99
101
|
})
|
|
100
102
|
|
|
@@ -67,6 +67,7 @@ const resolveColumnTypeSchema = (type: Model.ColumnType) => {
|
|
|
67
67
|
// todo: validate format of string-like inputs uuid, date, enum
|
|
68
68
|
case Model.ColumnType.Date:
|
|
69
69
|
case Model.ColumnType.DateTime:
|
|
70
|
+
case Model.ColumnType.Time:
|
|
70
71
|
case Model.ColumnType.Uuid:
|
|
71
72
|
case Model.ColumnType.Enum:
|
|
72
73
|
case Model.ColumnType.String:
|
package/src/type-schema/model.ts
CHANGED
|
@@ -130,8 +130,10 @@ const columnSchema = Typesafe.intersection(
|
|
|
130
130
|
}),
|
|
131
131
|
Typesafe.partial({
|
|
132
132
|
typeAlias: Typesafe.string,
|
|
133
|
-
|
|
133
|
+
list: Typesafe.boolean,
|
|
134
|
+
default: Typesafe.anyJson,
|
|
134
135
|
sequence: intersectionSchema as Typesafe.Type<Model.AnyColumn['sequence']>,
|
|
136
|
+
collation: Typesafe.string as Typesafe.Type<Model.AnyColumn['collation']>,
|
|
135
137
|
}),
|
|
136
138
|
)
|
|
137
139
|
const columnSchemaCheck: Typesafe.Equals<Model.AnyColumn, ReturnType<typeof columnSchema>> = true
|
|
@@ -153,13 +155,15 @@ const viewSchemaInner = Typesafe.intersection(
|
|
|
153
155
|
}),
|
|
154
156
|
Typesafe.partial({
|
|
155
157
|
dependencies: Typesafe.array(Typesafe.string),
|
|
158
|
+
idSource: Typesafe.array(Typesafe.string),
|
|
159
|
+
materialized: Typesafe.boolean,
|
|
156
160
|
}),
|
|
157
161
|
)
|
|
158
162
|
const viewSchemaCheck: Typesafe.Equals<Model.View, ReturnType<typeof viewSchemaInner>> = true
|
|
159
163
|
const viewSchema: Typesafe.Type<Model.View> = viewSchemaInner
|
|
160
164
|
|
|
161
165
|
|
|
162
|
-
const
|
|
166
|
+
const indexSchemaBase = Typesafe.intersection(
|
|
163
167
|
Typesafe.object({
|
|
164
168
|
fields: Typesafe.array(Typesafe.string),
|
|
165
169
|
}),
|
|
@@ -167,22 +171,37 @@ const indexLike: Typesafe.Type<{readonly fields: readonly string[]; readonly nam
|
|
|
167
171
|
name: Typesafe.string,
|
|
168
172
|
}),
|
|
169
173
|
)
|
|
174
|
+
const indexCheck: Typesafe.Equals<Model.Index, ReturnType<typeof indexSchemaBase>> = true
|
|
170
175
|
|
|
171
176
|
const indexesSchema = Typesafe.coalesce<Model.Indexes, Model.Indexes>(
|
|
172
177
|
Typesafe.preprocess(
|
|
173
|
-
Typesafe.array(
|
|
178
|
+
Typesafe.array(indexSchemaBase),
|
|
174
179
|
it => it?.constructor === Object ? Object.values(it) : it,
|
|
175
180
|
),
|
|
176
181
|
[],
|
|
177
182
|
)
|
|
178
183
|
|
|
179
184
|
const uniqueConstraint = Typesafe.intersection(
|
|
180
|
-
|
|
185
|
+
Typesafe.object({
|
|
186
|
+
fields: Typesafe.array(Typesafe.string),
|
|
187
|
+
}),
|
|
181
188
|
Typesafe.partial({
|
|
182
189
|
timing: Typesafe.enumeration('deferrable', 'deferred'),
|
|
190
|
+
name: Typesafe.string,
|
|
191
|
+
index: Typesafe.literal(false),
|
|
192
|
+
}),
|
|
193
|
+
)
|
|
194
|
+
const uniqueIndex = Typesafe.intersection(
|
|
195
|
+
Typesafe.object({
|
|
196
|
+
fields: Typesafe.array(Typesafe.string),
|
|
197
|
+
index: Typesafe.literal(true),
|
|
198
|
+
}),
|
|
199
|
+
Typesafe.partial({
|
|
200
|
+
nulls: Typesafe.enumeration('distinct', 'not distinct'),
|
|
183
201
|
}),
|
|
184
202
|
)
|
|
185
203
|
const uniqueConstraintCheck: Typesafe.Equals<Model.UniqueConstraint, ReturnType<typeof uniqueConstraint>> = true
|
|
204
|
+
const uniqueIndexCheck: Typesafe.Equals<Model.UniqueIndex, ReturnType<typeof uniqueIndex>> = true
|
|
186
205
|
|
|
187
206
|
const entitySchema = Typesafe.intersection(
|
|
188
207
|
Typesafe.object({
|
|
@@ -191,8 +210,8 @@ const entitySchema = Typesafe.intersection(
|
|
|
191
210
|
primaryColumn: Typesafe.string,
|
|
192
211
|
tableName: Typesafe.string,
|
|
193
212
|
fields: Typesafe.record(Typesafe.string, fieldSchema),
|
|
194
|
-
unique: Typesafe.preprocess<Model.
|
|
195
|
-
Typesafe.array(uniqueConstraint),
|
|
213
|
+
unique: Typesafe.preprocess<Model.Uniques>(
|
|
214
|
+
Typesafe.array(Typesafe.union(uniqueConstraint, uniqueIndex)),
|
|
196
215
|
it => it?.constructor === Object ? Object.values(it) : it,
|
|
197
216
|
),
|
|
198
217
|
indexes: indexesSchema,
|
|
@@ -11,6 +11,7 @@ export const settingsSchema = Typesafe.partial({
|
|
|
11
11
|
useExistsInHasManyFilter: Typesafe.boolean,
|
|
12
12
|
fullDateTimeResponse: Typesafe.boolean,
|
|
13
13
|
shortDateResponse: Typesafe.boolean,
|
|
14
|
+
uuidVersion: Typesafe.union(Typesafe.literal(4), Typesafe.literal(7)),
|
|
14
15
|
}),
|
|
15
16
|
})
|
|
16
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Model } from '@contember/schema'
|
|
2
2
|
import { ErrorBuilder, ValidationError } from './errors'
|
|
3
|
-
import { acceptEveryFieldVisitor, getTargetEntity, isInverseRelation, isOwningRelation } from '../model'
|
|
3
|
+
import { acceptEveryFieldVisitor, getTargetEntity, isColumn, isInverseRelation, isOwningRelation } from '../model'
|
|
4
4
|
|
|
5
5
|
const IDENTIFIER_PATTERN = /^[_a-zA-Z][_a-zA-Z0-9]*$/
|
|
6
6
|
const RESERVED_WORDS = ['and', 'or', 'not']
|
|
@@ -52,18 +52,23 @@ export class ModelValidator {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
this.validateUniqueConstraints(
|
|
55
|
-
entity
|
|
56
|
-
new Set(Object.keys(entity.fields)),
|
|
55
|
+
entity,
|
|
57
56
|
errors.for('unique'),
|
|
58
57
|
)
|
|
59
58
|
this.validateColumnNamesCollision(entity, errors)
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
private validateUniqueConstraints(
|
|
63
|
-
for (const constraint of
|
|
61
|
+
private validateUniqueConstraints(entity: Model.Entity, errors: ErrorBuilder): void {
|
|
62
|
+
for (const constraint of entity.unique) {
|
|
63
|
+
if (entity.view?.materialized && !constraint.index) {
|
|
64
|
+
errors.add('MODEL_INVALID_CONSTRAINT', 'For materialized views, Unique must be defined as an index. Please set index: true.')
|
|
65
|
+
}
|
|
64
66
|
for (const field of constraint.fields) {
|
|
65
|
-
|
|
67
|
+
const fieldDef = entity.fields[field]
|
|
68
|
+
if (!fieldDef) {
|
|
66
69
|
errors.add('MODEL_UNDEFINED_FIELD', `Referenced field ${field} in a constraint does not exists`)
|
|
70
|
+
} else if (isColumn(fieldDef) && fieldDef.list) {
|
|
71
|
+
errors.add('MODEL_INVALID_FIELD', `Field ${field} in a unique constraint cannot be an array`)
|
|
67
72
|
}
|
|
68
73
|
}
|
|
69
74
|
}
|
|
@@ -190,11 +195,6 @@ export class ModelValidator {
|
|
|
190
195
|
}
|
|
191
196
|
} else {
|
|
192
197
|
const inversedBy = field.inversedBy
|
|
193
|
-
if (targetEntity.view) {
|
|
194
|
-
if ('joiningColumn' in field) {
|
|
195
|
-
return errors.add('MODEL_INVALID_VIEW_USAGE', `View entity ${targetEntity.name} cannot be referenced from an owning relation. Try switching the owning side.`)
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
198
|
if (inversedBy) {
|
|
199
199
|
const targetField = targetEntity.fields[inversedBy]
|
|
200
200
|
const relationDescription = `Target relation ${targetEntityName}::${inversedBy}:`
|
package/src/validation/errors.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type ValidationErrorCode =
|
|
2
2
|
| 'MODEL_NAME_MISMATCH'
|
|
3
3
|
| 'MODEL_UNDEFINED_FIELD'
|
|
4
|
+
| 'MODEL_INVALID_FIELD'
|
|
4
5
|
| 'MODEL_UNDEFINED_ENTITY'
|
|
5
6
|
| 'MODEL_RELATION_REQUIRED'
|
|
6
7
|
| 'MODEL_INVALID_RELATION_DEFINITION'
|
|
@@ -8,6 +9,7 @@ export type ValidationErrorCode =
|
|
|
8
9
|
| 'MODEL_INVALID_VIEW_USAGE'
|
|
9
10
|
| 'MODEL_INVALID_IDENTIFIER'
|
|
10
11
|
| 'MODEL_INVALID_ENTITY_NAME'
|
|
12
|
+
| 'MODEL_INVALID_CONSTRAINT'
|
|
11
13
|
| 'MODEL_NAME_COLLISION'
|
|
12
14
|
|
|
13
15
|
| 'ACL_INVALID_CONDITION'
|
|
@@ -91,3 +91,22 @@ test('column name collision', () => {
|
|
|
91
91
|
},
|
|
92
92
|
])
|
|
93
93
|
})
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
namespace ViewRelations {
|
|
97
|
+
@c.View('SELECT 1')
|
|
98
|
+
export class Foo {
|
|
99
|
+
bars = c.oneHasMany(Bar, 'foo')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@c.View('SELECT 1')
|
|
103
|
+
export class Bar {
|
|
104
|
+
foo = c.manyHasOne(Foo, 'bars')
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
test('view relations', () => {
|
|
109
|
+
const schema = createSchema(ViewRelations)
|
|
110
|
+
const validator = new ModelValidator(schema.model)
|
|
111
|
+
expect(validator.validate()).toStrictEqual([])
|
|
112
|
+
})
|