@contember/schema-definition 2.1.0-beta.2 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/schema-definition",
3
- "version": "2.1.0-beta.2",
3
+ "version": "2.1.0-rc.2",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/production/index.js",
6
6
  "typings": "./dist/types/index.d.ts",
@@ -17,13 +17,14 @@ export const createSchema = (definitions: Record<string, any>, modifyCallback?:
17
17
  strictDefinitionValidator: strictDefinitionValidator,
18
18
  defaultCollation: options?.defaultCollation,
19
19
  })
20
+ strictDefinitionValidator.validateModel(model)
20
21
  const validation = InputValidation.parseDefinition(definitions)
21
22
  const acl = AclDefinition.createAcl(definitions, model)
22
23
  const actions = ActionsDefinition.createActions(definitions)
23
24
  const schema = { ...emptySchema, model, validation, acl, actions }
24
25
 
25
26
  if (strictDefinitionValidator.warnings.length > 0) {
26
- throw `Strict schema validation failed: \n${strictDefinitionValidator.warnings.map(it => `- ${it.message}`).join('\n')}`
27
+ throw `Strict schema validation failed:\n${strictDefinitionValidator.warnings.map(it => `- ${it.message}`).join('\n')}`
27
28
  }
28
29
 
29
30
  return modifyCallback ? modifyCallback(schema) : schema
package/src/strict.ts CHANGED
@@ -12,9 +12,17 @@ export const allStrict: StrictOptions = {
12
12
 
13
13
  export type Warning = { message: string }
14
14
 
15
+ type OnDeleteValidation = {
16
+ entityName: string
17
+ field: string
18
+ hasOnDelete: boolean
19
+ }
20
+
15
21
  export class StrictDefinitionValidator {
16
22
  public readonly warnings: Warning[] = []
17
23
 
24
+ private readonly onDeleteValidations: OnDeleteValidation[] = []
25
+
18
26
  constructor(
19
27
  private readonly options: StrictOptions,
20
28
  ) {
@@ -27,10 +35,27 @@ export class StrictDefinitionValidator {
27
35
  }
28
36
 
29
37
  public validateOnCascade(entityName: string, field: string, definition: { onDelete?: Model.OnDelete }): void {
30
- if (!definition.onDelete && this.options.requireOnDelete) {
31
- this.registerWarning(
32
- `${entityName}.${field}: onDelete behaviour is not set. Use one of cascadeOnDelete(), setNullOnDelete() or restrictOnDelete().`,
33
- )
38
+ // View entities are read-only and have no real delete semantics, so onDelete validation is
39
+ // deferred to validateModel() where we know which entities are views.
40
+ this.onDeleteValidations.push({ entityName, field, hasOnDelete: definition.onDelete !== undefined })
41
+ }
42
+
43
+ public validateModel(model: Model.Schema): void {
44
+ for (const { entityName, field, hasOnDelete } of this.onDeleteValidations) {
45
+ const isView = model.entities[entityName]?.view !== undefined
46
+ if (isView) {
47
+ if (hasOnDelete) {
48
+ this.registerWarning(
49
+ `${entityName}.${field}: onDelete behaviour must not be set on a relation of a view entity. Views are read-only and have no delete semantics.`,
50
+ )
51
+ }
52
+ continue
53
+ }
54
+ if (!hasOnDelete && this.options.requireOnDelete) {
55
+ this.registerWarning(
56
+ `${entityName}.${field}: onDelete behaviour is not set. Use one of cascadeOnDelete(), setNullOnDelete() or restrictOnDelete().`,
57
+ )
58
+ }
34
59
  }
35
60
  }
36
61
 
@@ -113,7 +113,56 @@ test('strict test', () => {
113
113
  settings: settingsPresets['v1.3'],
114
114
  }), { strict: true })
115
115
 
116
- expect(cb).toThrow(`Strict schema validation failed:
116
+ expect(cb).toThrow(`Strict schema validation failed:
117
117
  - Book.genre: inverse side of the relation is not defined.
118
118
  - Book.genre: onDelete behaviour is not set. Use one of cascadeOnDelete(), setNullOnDelete() or restrictOnDelete().`)
119
119
  })
120
+
121
+ namespace StrictViewModel {
122
+ export class Author {
123
+ name = c.stringColumn()
124
+ stats = c.oneHasOneInverse(AuthorStats, 'author')
125
+ }
126
+
127
+ @c.View('SELECT 1')
128
+ export class AuthorStats {
129
+ author = c.oneHasOne(Author, 'stats')
130
+ postCount = c.intColumn().notNull()
131
+ }
132
+ }
133
+
134
+ test('strict test: onDelete is not required on a view entity relation', () => {
135
+ const cb = () =>
136
+ createSchema(StrictViewModel, schema => ({
137
+ ...schema,
138
+ settings: settingsPresets['v1.3'],
139
+ }), { strict: true })
140
+
141
+ // AuthorStats.author has an inverse side defined and is a view relation, so neither the
142
+ // inverse-side nor the onDelete strict checks should fire.
143
+ expect(cb).not.toThrow()
144
+ })
145
+
146
+ namespace StrictViewWithOnDeleteModel {
147
+ export class Author {
148
+ name = c.stringColumn()
149
+ stats = c.oneHasOneInverse(AuthorStats, 'author')
150
+ }
151
+
152
+ @c.View('SELECT 1')
153
+ export class AuthorStats {
154
+ author = c.oneHasOne(Author, 'stats').cascadeOnDelete()
155
+ postCount = c.intColumn().notNull()
156
+ }
157
+ }
158
+
159
+ test('strict test: onDelete must not be set on a view entity relation', () => {
160
+ const cb = () =>
161
+ createSchema(StrictViewWithOnDeleteModel, schema => ({
162
+ ...schema,
163
+ settings: settingsPresets['v1.3'],
164
+ }), { strict: true })
165
+
166
+ expect(cb).toThrow(`Strict schema validation failed:
167
+ - AuthorStats.author: onDelete behaviour must not be set on a relation of a view entity. Views are read-only and have no delete semantics.`)
168
+ })