@contember/schema-definition 1.3.6 → 1.4.0-alpha.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/createSchema.d.ts +4 -1
- package/dist/src/createSchema.d.ts.map +1 -1
- package/dist/src/createSchema.js +8 -2
- package/dist/src/createSchema.js.map +1 -1
- package/dist/src/model/definition/UniqueDefinition.d.ts +4 -2
- package/dist/src/model/definition/UniqueDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/UniqueDefinition.js +4 -2
- package/dist/src/model/definition/UniqueDefinition.js.map +1 -1
- package/dist/src/model/definition/context.d.ts +10 -0
- package/dist/src/model/definition/context.d.ts.map +1 -0
- package/dist/src/model/definition/context.js +3 -0
- package/dist/src/model/definition/context.js.map +1 -0
- package/dist/src/model/definition/extensions.d.ts +4 -8
- package/dist/src/model/definition/extensions.d.ts.map +1 -1
- package/dist/src/model/definition/extensions.js.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/ColumnDefinition.d.ts +6 -2
- package/dist/src/model/definition/fieldDefinitions/ColumnDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/ColumnDefinition.js +2 -2
- package/dist/src/model/definition/fieldDefinitions/ColumnDefinition.js.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/FieldDefinition.d.ts +3 -7
- package/dist/src/model/definition/fieldDefinitions/FieldDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/FieldDefinition.js.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/ManyHasManyDefinition.d.ts +1 -1
- package/dist/src/model/definition/fieldDefinitions/ManyHasManyDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/ManyHasManyDefinition.js +2 -1
- package/dist/src/model/definition/fieldDefinitions/ManyHasManyDefinition.js.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/ManyHasOneDefinition.d.ts +3 -3
- package/dist/src/model/definition/fieldDefinitions/ManyHasOneDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/ManyHasOneDefinition.js +6 -1
- package/dist/src/model/definition/fieldDefinitions/ManyHasOneDefinition.js.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/OneHasOneDefinition.d.ts +2 -1
- package/dist/src/model/definition/fieldDefinitions/OneHasOneDefinition.d.ts.map +1 -1
- package/dist/src/model/definition/fieldDefinitions/OneHasOneDefinition.js +6 -1
- package/dist/src/model/definition/fieldDefinitions/OneHasOneDefinition.js.map +1 -1
- package/dist/src/model/definition/index.d.ts +4 -1
- package/dist/src/model/definition/index.d.ts.map +1 -1
- package/dist/src/model/definition/index.js +2 -2
- package/dist/src/model/definition/index.js.map +1 -1
- package/dist/src/model/definition/internal/SchemaBuilder.d.ts +3 -1
- package/dist/src/model/definition/internal/SchemaBuilder.d.ts.map +1 -1
- package/dist/src/model/definition/internal/SchemaBuilder.js +6 -2
- package/dist/src/model/definition/internal/SchemaBuilder.js.map +1 -1
- package/dist/src/presets.d.ts +1 -0
- package/dist/src/presets.d.ts.map +1 -1
- package/dist/src/presets.js +10 -0
- package/dist/src/presets.js.map +1 -1
- package/dist/src/strict.d.ts +22 -0
- package/dist/src/strict.d.ts.map +1 -0
- package/dist/src/strict.js +28 -0
- package/dist/src/strict.js.map +1 -0
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/cases/unit/createSchema.test.js +25 -0
- package/dist/tests/cases/unit/createSchema.test.js.map +1 -1
- package/dist/tests/cases/unit/extendEntity.test.js +2 -4
- package/dist/tests/cases/unit/extendEntity.test.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/createSchema.ts +13 -2
- package/src/model/definition/UniqueDefinition.ts +11 -5
- package/src/model/definition/context.ts +10 -0
- package/src/model/definition/extensions.ts +10 -12
- package/src/model/definition/fieldDefinitions/ColumnDefinition.ts +3 -3
- package/src/model/definition/fieldDefinitions/FieldDefinition.ts +8 -9
- package/src/model/definition/fieldDefinitions/ManyHasManyDefinition.ts +3 -1
- package/src/model/definition/fieldDefinitions/ManyHasOneDefinition.ts +9 -3
- package/src/model/definition/fieldDefinitions/OneHasOneDefinition.ts +8 -1
- package/src/model/definition/index.ts +8 -2
- package/src/model/definition/internal/SchemaBuilder.ts +8 -2
- package/src/presets.ts +13 -0
- package/src/strict.ts +40 -0
- package/tests/cases/unit/createSchema.test.ts +23 -0
- package/tests/cases/unit/extendEntity.test.ts +2 -4
|
@@ -21,7 +21,7 @@ export class ManyHasManyDefinitionImpl extends FieldDefinition<ManyHasManyDefini
|
|
|
21
21
|
return this.withOption('orderBy', [...(this.options.orderBy || []), { path, direction: direction as Model.OrderDirection }])
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
createField({ name, conventions, entityName, entityRegistry }: CreateFieldContext): Model.AnyField {
|
|
24
|
+
createField({ name, conventions, entityName, entityRegistry, strictDefinitionValidator }: CreateFieldContext): Model.AnyField {
|
|
25
25
|
const options = this.options
|
|
26
26
|
const columnNames = conventions.getJoiningTableColumnNames(
|
|
27
27
|
entityName,
|
|
@@ -37,6 +37,8 @@ export class ManyHasManyDefinitionImpl extends FieldDefinition<ManyHasManyDefini
|
|
|
37
37
|
...options.joiningTable,
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
strictDefinitionValidator.validateInverseSide(entityName, name, options)
|
|
41
|
+
|
|
40
42
|
return {
|
|
41
43
|
type: Model.RelationType.ManyHasMany,
|
|
42
44
|
name,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { EntityConstructor, Interface } from '../types'
|
|
1
|
+
import { EntityConstructor, Interface, RelationTarget } from '../types'
|
|
2
2
|
import { Model } from '@contember/schema'
|
|
3
3
|
import { CreateFieldContext, FieldDefinition } from './FieldDefinition'
|
|
4
|
-
import { RelationTarget } from '../types'
|
|
5
4
|
|
|
6
5
|
export class ManyHasOneDefinitionImpl extends FieldDefinition<ManyHasOneDefinitionOptions> {
|
|
7
6
|
type = 'ManyHasOneDefinition' as const
|
|
@@ -26,13 +25,20 @@ export class ManyHasOneDefinitionImpl extends FieldDefinition<ManyHasOneDefiniti
|
|
|
26
25
|
return this.withOption('joiningColumn', { ...this.options.joiningColumn, onDelete: Model.OnDelete.setNull })
|
|
27
26
|
}
|
|
28
27
|
|
|
28
|
+
restrictOnDelete(): Interface<ManyHasOneDefinition> {
|
|
29
|
+
return this.withOption('joiningColumn', { ...this.options.joiningColumn, onDelete: Model.OnDelete.restrict })
|
|
30
|
+
}
|
|
31
|
+
|
|
29
32
|
notNull(): Interface<ManyHasOneDefinition> {
|
|
30
33
|
return this.withOption('nullable', false)
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
createField({ name, conventions, entityName, entityRegistry }: CreateFieldContext): Model.AnyField {
|
|
36
|
+
createField({ name, conventions, entityName, entityRegistry, strictDefinitionValidator }: CreateFieldContext): Model.AnyField {
|
|
34
37
|
const options = this.options
|
|
35
38
|
const joiningColumn = options.joiningColumn || {}
|
|
39
|
+
strictDefinitionValidator.validateInverseSide(entityName, name, options)
|
|
40
|
+
strictDefinitionValidator.validateOnCascade(entityName, name, joiningColumn)
|
|
41
|
+
|
|
36
42
|
return {
|
|
37
43
|
name: name,
|
|
38
44
|
...(typeof options.inversedBy === 'undefined' ? {} : { inversedBy: options.inversedBy }),
|
|
@@ -25,6 +25,10 @@ export class OneHasOneDefinitionImpl extends FieldDefinition<OneHasOneDefinition
|
|
|
25
25
|
return this.withOption('joiningColumn', { ...this.options.joiningColumn, onDelete: Model.OnDelete.setNull })
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
restrictOnDelete(): Interface<OneHasOneDefinition> {
|
|
29
|
+
return this.withOption('joiningColumn', { ...this.options.joiningColumn, onDelete: Model.OnDelete.restrict })
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
notNull(): Interface<OneHasOneDefinition> {
|
|
29
33
|
return this.withOption('nullable', false)
|
|
30
34
|
}
|
|
@@ -33,10 +37,13 @@ export class OneHasOneDefinitionImpl extends FieldDefinition<OneHasOneDefinition
|
|
|
33
37
|
return this.withOption('orphanRemoval', true)
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
createField({ name, conventions, entityRegistry }: CreateFieldContext): Model.AnyField {
|
|
40
|
+
createField({ name, conventions, entityRegistry, strictDefinitionValidator, entityName }: CreateFieldContext): Model.AnyField {
|
|
37
41
|
const options = this.options
|
|
38
42
|
const joiningColumn: Partial<Model.JoiningColumn> = options.joiningColumn || {}
|
|
39
43
|
|
|
44
|
+
strictDefinitionValidator.validateInverseSide(entityName, name, options)
|
|
45
|
+
strictDefinitionValidator.validateOnCascade(entityName, name, joiningColumn)
|
|
46
|
+
|
|
40
47
|
return {
|
|
41
48
|
name: name,
|
|
42
49
|
...(typeof options.inversedBy === 'undefined' ? {} : { inversedBy: options.inversedBy }),
|
|
@@ -6,6 +6,7 @@ import 'reflect-metadata'
|
|
|
6
6
|
import { FieldDefinition } from './fieldDefinitions'
|
|
7
7
|
import { isEntityConstructor } from '../../utils'
|
|
8
8
|
import { DefaultNamingConventions } from '@contember/schema-utils'
|
|
9
|
+
import { StrictOptions, StrictDefinitionValidator } from '../../strict'
|
|
9
10
|
|
|
10
11
|
export * from './fieldDefinitions'
|
|
11
12
|
export * from './EventLogDefinition'
|
|
@@ -24,8 +25,13 @@ export type ModelDefinition<M> = {
|
|
|
24
25
|
[K in keyof M]: unknown
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export function createModel<M extends ModelDefinition<M>>(definitions: M
|
|
28
|
-
|
|
28
|
+
export function createModel<M extends ModelDefinition<M>>(definitions: M, options: {
|
|
29
|
+
strictDefinitionValidator?: StrictDefinitionValidator
|
|
30
|
+
} = {}): Model.Schema {
|
|
31
|
+
const schemaBuilder = new SchemaBuilder(
|
|
32
|
+
new DefaultNamingConventions(),
|
|
33
|
+
options.strictDefinitionValidator,
|
|
34
|
+
)
|
|
29
35
|
for (const [name, definition] of Object.entries(definitions)) {
|
|
30
36
|
if (definition instanceof EnumDefinition) {
|
|
31
37
|
schemaBuilder.addEnum(name, definition)
|
|
@@ -8,13 +8,17 @@ import { EntityRegistry } from './EntityRegistry'
|
|
|
8
8
|
import { EnumRegistry } from './EnumRegistry'
|
|
9
9
|
import { ColumnDefinition } from '../fieldDefinitions'
|
|
10
10
|
import { applyEntityExtensions } from '../extensions'
|
|
11
|
+
import { StrictOptions, StrictDefinitionValidator } from '../../../strict'
|
|
11
12
|
|
|
12
13
|
export class SchemaBuilder {
|
|
13
14
|
private entityRegistry = new EntityRegistry()
|
|
14
15
|
|
|
15
16
|
private enumRegistry = new EnumRegistry()
|
|
16
17
|
|
|
17
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly conventions: NamingConventions,
|
|
20
|
+
private readonly strictDefinitionValidator: StrictDefinitionValidator = new StrictDefinitionValidator({}),
|
|
21
|
+
) {}
|
|
18
22
|
|
|
19
23
|
public addEntity(name: string, entity: EntityConstructor): void {
|
|
20
24
|
this.entityRegistry.register(name, entity)
|
|
@@ -47,6 +51,7 @@ export class SchemaBuilder {
|
|
|
47
51
|
conventions: this.conventions,
|
|
48
52
|
enumRegistry: this.enumRegistry,
|
|
49
53
|
entityRegistry: this.entityRegistry,
|
|
54
|
+
strictDefinitionValidator: this.strictDefinitionValidator,
|
|
50
55
|
})
|
|
51
56
|
})
|
|
52
57
|
.reduce<Model.Entity['fields']>((acc, field) => {
|
|
@@ -67,6 +72,7 @@ export class SchemaBuilder {
|
|
|
67
72
|
entityRegistry: this.entityRegistry,
|
|
68
73
|
conventions: this.conventions,
|
|
69
74
|
enumRegistry: this.enumRegistry,
|
|
75
|
+
strictDefinitionValidator: this.strictDefinitionValidator,
|
|
70
76
|
})
|
|
71
77
|
})
|
|
72
78
|
|
|
@@ -87,7 +93,7 @@ export class SchemaBuilder {
|
|
|
87
93
|
const unique: Model.UniqueConstraint[] = []
|
|
88
94
|
for (const [fieldName, definition] of Object.entries(fieldDefinitions)) {
|
|
89
95
|
if (definition.options.unique) {
|
|
90
|
-
unique.push({ fields: [fieldName] })
|
|
96
|
+
unique.push({ fields: [fieldName], ...definition.options.unique })
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
99
|
return unique
|
package/src/presets.ts
CHANGED
|
@@ -6,6 +6,19 @@ const v1_3preset: Settings.Schema = {
|
|
|
6
6
|
inviteExpirationMinutes: 60 * 24 * 7, // 7 days
|
|
7
7
|
},
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const v1_4preset: Settings.Schema = {
|
|
12
|
+
tenant: {
|
|
13
|
+
inviteExpirationMinutes: 60 * 24 * 7, // 7 days
|
|
14
|
+
},
|
|
15
|
+
content: {
|
|
16
|
+
shortDateResponse: true,
|
|
17
|
+
useExistsInHasManyFilter: true,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
export const settingsPresets = {
|
|
10
22
|
'v1.3': v1_3preset,
|
|
23
|
+
'v1.4': v1_4preset,
|
|
11
24
|
}
|
package/src/strict.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Model } from '@contember/schema'
|
|
2
|
+
|
|
3
|
+
export type StrictOptions = {
|
|
4
|
+
requireOnDelete?: boolean
|
|
5
|
+
requireInverseSide?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const allStrict: StrictOptions = {
|
|
9
|
+
requireOnDelete: true,
|
|
10
|
+
requireInverseSide: true,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export type Warning = { message: string }
|
|
15
|
+
|
|
16
|
+
export class StrictDefinitionValidator {
|
|
17
|
+
public readonly warnings: Warning[] = []
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
private readonly options: StrictOptions,
|
|
21
|
+
) {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public validateInverseSide(entityName: string, field: string, definition: { inversedBy?: string }): void {
|
|
25
|
+
if (!definition.inversedBy && this.options.requireInverseSide) {
|
|
26
|
+
this.registerWarning(`${entityName}.${field}: inverse side of the relation is not defined.`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
public validateOnCascade(entityName: string, field: string, definition: { onDelete?: Model.OnDelete}): void {
|
|
32
|
+
if (!definition.onDelete && this.options.requireOnDelete) {
|
|
33
|
+
this.registerWarning(`${entityName}.${field}: onDelete behaviour is not set. Use one of cascadeOnDelete(), setNullOnDelete() or restrictOnDelete().`)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public registerWarning(message: string): void {
|
|
38
|
+
this.warnings.push({ message })
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -97,3 +97,26 @@ test('basic createSchema test', () => {
|
|
|
97
97
|
}
|
|
98
98
|
`)
|
|
99
99
|
})
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
namespace StrictModel {
|
|
103
|
+
export class Genre {
|
|
104
|
+
name = c.stringColumn()
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
export class Book {
|
|
108
|
+
title = c.stringColumn()
|
|
109
|
+
genre = c.manyHasOne(Genre)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
test('strict test', () => {
|
|
114
|
+
const cb = () => createSchema(StrictModel, schema => ({
|
|
115
|
+
...schema,
|
|
116
|
+
settings: settingsPresets['v1.3'],
|
|
117
|
+
}), { strict: true })
|
|
118
|
+
|
|
119
|
+
expect(cb).toThrow(`Strict schema validation failed:
|
|
120
|
+
- Book.genre: inverse side of the relation is not defined.
|
|
121
|
+
- Book.genre: onDelete behaviour is not set. Use one of cascadeOnDelete(), setNullOnDelete() or restrictOnDelete().`)
|
|
122
|
+
})
|
|
@@ -10,16 +10,14 @@ namespace ExtendedModel {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const addField = (name: string, field: FieldDefinition<any>): DecoratorFunction<any> => {
|
|
13
|
-
return extendEntity(({ entity,
|
|
13
|
+
return extendEntity(({ entity, ...context }) => ({
|
|
14
14
|
...entity,
|
|
15
15
|
fields: {
|
|
16
16
|
...entity.fields,
|
|
17
17
|
[name]: field.createField({
|
|
18
18
|
name,
|
|
19
|
-
enumRegistry,
|
|
20
|
-
entityRegistry,
|
|
21
|
-
conventions,
|
|
22
19
|
entityName: entity.name,
|
|
20
|
+
...context,
|
|
23
21
|
}),
|
|
24
22
|
},
|
|
25
23
|
}))
|