@contember/engine-system-api 1.3.2 → 1.3.3

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/engine-system-api",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -13,22 +13,22 @@
13
13
  "test": "vitest --dir ./tests/cases"
14
14
  },
15
15
  "dependencies": {
16
- "@contember/authorization": "1.3.2",
17
- "@contember/database": "1.3.2",
18
- "@contember/database-migrations": "1.3.2",
19
- "@contember/dic": "1.3.2",
20
- "@contember/graphql-utils": "1.3.2",
21
- "@contember/logger": "1.3.2",
22
- "@contember/queryable": "1.3.2",
23
- "@contember/schema": "1.3.2",
24
- "@contember/schema-migrations": "1.3.2",
25
- "@contember/schema-utils": "1.3.2",
16
+ "@contember/authorization": "1.3.3",
17
+ "@contember/database": "1.3.3",
18
+ "@contember/database-migrations": "1.3.3",
19
+ "@contember/dic": "1.3.3",
20
+ "@contember/graphql-utils": "1.3.3",
21
+ "@contember/logger": "1.3.3",
22
+ "@contember/queryable": "1.3.3",
23
+ "@contember/schema": "1.3.3",
24
+ "@contember/schema-migrations": "1.3.3",
25
+ "@contember/schema-utils": "1.3.3",
26
26
  "@graphql-tools/utils": "^8.6.13",
27
27
  "graphql-tag": "^2.12.5"
28
28
  },
29
29
  "devDependencies": {
30
- "@contember/engine-api-tester": "1.3.2",
31
- "@contember/schema-definition": "1.3.2",
30
+ "@contember/engine-api-tester": "1.3.3",
31
+ "@contember/schema-definition": "1.3.3",
32
32
  "@graphql-codegen/cli": "^2.6.2",
33
33
  "@graphql-codegen/typescript": "^2.5.1",
34
34
  "@graphql-codegen/typescript-operations": "^2.4.2",
@@ -0,0 +1,77 @@
1
+ import { MigrationArgs, MigrationBuilder } from '@contember/database-migrations'
2
+ import { wrapIdentifier } from '@contember/database'
3
+ import { SystemMigrationArgs } from './types'
4
+ import { acceptFieldVisitor, ForeignKeyDeleteAction, SchemaDatabaseMetadata } from '@contember/schema-utils'
5
+ import { Model } from '@contember/schema'
6
+
7
+ export default async function (builder: MigrationBuilder, args: MigrationArgs<SystemMigrationArgs>) {
8
+ const schema = await args.schemaResolver(args.connection)
9
+ const stages = (await args.connection.query<{schema: string}>('SELECT schema FROM stage')).rows
10
+ const metadataByStage: Record<string, SchemaDatabaseMetadata> = {}
11
+
12
+ for (const entity of Object.values(schema.model.entities)) {
13
+ for (const field of Object.values(entity.fields)) {
14
+ const result = acceptFieldVisitor<null | {
15
+ relation: Model.OneHasOneOwningRelation | Model.ManyHasOneRelation
16
+ targetEntity: Model.Entity
17
+ }>(schema.model, entity, field, {
18
+ visitManyHasOne: ({ entity, relation, targetEntity }) => {
19
+ return { relation, entity, targetEntity }
20
+ },
21
+ visitOneHasOneOwning: ({ entity, relation, targetEntity }) => {
22
+ return { relation, entity, targetEntity }
23
+ },
24
+ visitOneHasOneInverse: () => null,
25
+ visitManyHasManyInverse: () => null,
26
+ visitManyHasManyOwning: () => null,
27
+ visitOneHasMany: () => null,
28
+ visitColumn: () => null,
29
+ })
30
+ if (!result) {
31
+ continue
32
+ }
33
+ const { targetEntity, relation } = result
34
+ if (entity.view || targetEntity.view) {
35
+ continue
36
+ }
37
+ for (const stage of stages) {
38
+ const databaseMetadata = metadataByStage[stage.schema] ??= await args.databaseMetadataResolver(args.connection, stage.schema)
39
+
40
+ const fkNames = databaseMetadata.getForeignKeyConstraintNames({
41
+ fromTable: entity.tableName,
42
+ fromColumn: relation.joiningColumn.columnName,
43
+ toTable: targetEntity.tableName,
44
+ toColumn: targetEntity.primaryColumn,
45
+ })
46
+
47
+ if (fkNames.every(it => {
48
+ const constraint = databaseMetadata.getForeignKeyConstraint(entity.tableName, it)
49
+ const dbConstraintFlag = ({
50
+ [Model.OnDelete.setNull]: ForeignKeyDeleteAction.setnull,
51
+ [Model.OnDelete.cascade]: ForeignKeyDeleteAction.cascade,
52
+ [Model.OnDelete.restrict]: ForeignKeyDeleteAction.restrict,
53
+ } as const)[relation.joiningColumn.onDelete]
54
+
55
+ return constraint && constraint.deleteAction === dbConstraintFlag
56
+ })) {
57
+ continue
58
+ }
59
+
60
+ for (const name of fkNames) {
61
+ builder.sql(`ALTER TABLE ${wrapIdentifier(stage.schema)}.${wrapIdentifier(entity.tableName)} DROP CONSTRAINT ${wrapIdentifier(name)}`)
62
+ }
63
+
64
+ const onDelete = ({
65
+ [Model.OnDelete.setNull]: 'SET NULL',
66
+ [Model.OnDelete.cascade]: 'CASCADE',
67
+ [Model.OnDelete.restrict]: 'RESTRICT',
68
+ } as const)[relation.joiningColumn.onDelete]
69
+
70
+ builder.sql(`ALTER TABLE ${wrapIdentifier(stage.schema)}.${wrapIdentifier(entity.tableName)}
71
+ ADD FOREIGN KEY (${wrapIdentifier(relation.joiningColumn.columnName)})
72
+ REFERENCES ${wrapIdentifier(stage.schema)}.${wrapIdentifier(targetEntity.tableName)} (${wrapIdentifier(targetEntity.primaryColumn)}) ON DELETE ${onDelete} DEFERRABLE INITIALLY IMMEDIATE
73
+ `)
74
+ }
75
+ }
76
+ }
77
+ }
@@ -17,6 +17,7 @@ import _20220208132600fnsearchpath from './2022-02-08-132600-fn-search-path'
17
17
  import _20220208140500dropdeadcode from './2022-02-08-140500-drop-deadcode'
18
18
  import _20220208144400dynamicstageschema from './2022-02-08-144400-dynamic-stage-schema'
19
19
  import _20221003110000tableondelete from './2022-10-03-110000-table-on-delete'
20
+ import _20230911174000fixondelete from './2023-09-11-174000-fix-on-delete'
20
21
  import snapshot from './snapshot'
21
22
 
22
23
  import { Client, Connection, createDatabaseIfNotExists, DatabaseConfig } from '@contember/database'
@@ -46,6 +47,7 @@ const migrations = {
46
47
  '2022-02-08-140500-drop-deadcode': _20220208140500dropdeadcode,
47
48
  '2022-02-08-144400-dynamic-stage-schema': _20220208144400dynamicstageschema,
48
49
  '2022-10-03-110000-table-on-delete': _20221003110000tableondelete,
50
+ '2023-09-11-174000-fix-on-delete': _20230911174000fixondelete,
49
51
  }
50
52
 
51
53
 
@@ -3,6 +3,7 @@ import {
3
3
  IndexMetadata,
4
4
  SchemaDatabaseMetadata,
5
5
  UniqueConstraintMetadata,
6
+ ForeignKeyDeleteAction,
6
7
  } from '@contember/schema-utils'
7
8
  import { DatabaseContext } from '../database'
8
9
  import { Connection } from '@contember/database'
@@ -21,6 +22,7 @@ export class SchemaDatabaseMetadataResolver {
21
22
  toColumn: it.target_columns[0],
22
23
  fromTable: it.table_name,
23
24
  toTable: it.target_table,
25
+ deleteAction: it.delete_action ?? ForeignKeyDeleteAction.noaction,
24
26
  }))
25
27
  const uniqueConstraints = constraintRows
26
28
  .filter((it): it is UniqueConstraintsRow => it.type === ConstraintTypes.unique)
@@ -54,7 +56,8 @@ export class SchemaDatabaseMetadataResolver {
54
56
  JSONB_AGG(DISTINCT pg_attribute_target.attname)
55
57
  FILTER ( WHERE pg_attribute_target.attname IS NOT NULL) AS target_columns,
56
58
  BOOL_OR(condeferrable),
57
- BOOL_OR(condeferred)
59
+ BOOL_OR(condeferred),
60
+ MAX(confdeltype) AS delete_action
58
61
  FROM pg_constraint
59
62
  JOIN pg_class
60
63
  ON pg_constraint.conrelid = pg_class.oid
@@ -116,6 +119,7 @@ type ForeignKeyConstraintsRow = {
116
119
  type: ConstraintTypes.foreignKey
117
120
  deferrable: boolean
118
121
  deferred: boolean
122
+ delete_action: ForeignKeyDeleteAction | null
119
123
  }
120
124
  type UniqueConstraintsRow = {
121
125
  constraint_name: string