@contember/engine-system-api 1.4.0-alpha.1 → 1.4.0-alpha.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/dist/src/SystemContainer.d.ts +3 -2
- package/dist/src/SystemContainer.d.ts.map +1 -1
- package/dist/src/SystemContainer.js +3 -2
- package/dist/src/SystemContainer.js.map +1 -1
- package/dist/src/bin/runMigrations.js +1 -1
- package/dist/src/bin/runMigrations.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/migrations/2023-10-24-140000-schema-nullable-checksum.d.ts +3 -0
- package/dist/src/migrations/2023-10-24-140000-schema-nullable-checksum.d.ts.map +1 -0
- package/dist/src/migrations/2023-10-24-140000-schema-nullable-checksum.js +12 -0
- package/dist/src/migrations/2023-10-24-140000-schema-nullable-checksum.js.map +1 -0
- package/dist/src/migrations/runner.d.ts +1 -2
- package/dist/src/migrations/runner.d.ts.map +1 -1
- package/dist/src/migrations/runner.js +6 -7
- package/dist/src/migrations/runner.js.map +1 -1
- package/dist/src/migrations/snapshot.d.ts.map +1 -1
- package/dist/src/migrations/snapshot.js +7 -2
- package/dist/src/migrations/snapshot.js.map +1 -1
- package/dist/src/model/authorization/AuthorizationActions.d.ts +5 -0
- package/dist/src/model/authorization/AuthorizationActions.d.ts.map +1 -1
- package/dist/src/model/authorization/AuthorizationActions.js +1 -0
- package/dist/src/model/authorization/AuthorizationActions.js.map +1 -1
- package/dist/src/model/authorization/PermissionsFactory.d.ts.map +1 -1
- package/dist/src/model/authorization/PermissionsFactory.js +1 -0
- package/dist/src/model/authorization/PermissionsFactory.js.map +1 -1
- package/dist/src/model/commands/migrations/SaveMigrationCommand.d.ts +4 -4
- package/dist/src/model/commands/migrations/SaveMigrationCommand.d.ts.map +1 -1
- package/dist/src/model/commands/migrations/SaveMigrationCommand.js +11 -3
- package/dist/src/model/commands/migrations/SaveMigrationCommand.js.map +1 -1
- package/dist/src/model/dependencies/content/ContentQueryExecutor.d.ts +35 -0
- package/dist/src/model/dependencies/content/ContentQueryExecutor.d.ts.map +1 -0
- package/dist/src/model/dependencies/content/ContentQueryExecutor.js +3 -0
- package/dist/src/model/dependencies/content/ContentQueryExecutor.js.map +1 -0
- package/dist/src/model/dependencies/index.d.ts +1 -0
- package/dist/src/model/dependencies/index.d.ts.map +1 -1
- package/dist/src/model/dependencies/index.js +1 -0
- package/dist/src/model/dependencies/index.js.map +1 -1
- package/dist/src/model/dtos/ExecutedMigration.d.ts +3 -2
- package/dist/src/model/dtos/ExecutedMigration.d.ts.map +1 -1
- package/dist/src/model/dtos/ExecutedMigration.js +2 -1
- package/dist/src/model/dtos/ExecutedMigration.js.map +1 -1
- package/dist/src/model/migrations/MigrationInput.d.ts +18 -0
- package/dist/src/model/migrations/MigrationInput.d.ts.map +1 -0
- package/dist/src/model/migrations/MigrationInput.js +3 -0
- package/dist/src/model/migrations/MigrationInput.js.map +1 -0
- package/dist/src/model/migrations/ProjectMigrator.d.ts +28 -6
- package/dist/src/model/migrations/ProjectMigrator.d.ts.map +1 -1
- package/dist/src/model/migrations/ProjectMigrator.js +80 -22
- package/dist/src/model/migrations/ProjectMigrator.js.map +1 -1
- package/dist/src/model/migrations/SchemaVersionBuilder.d.ts.map +1 -1
- package/dist/src/model/migrations/SchemaVersionBuilder.js +10 -5
- package/dist/src/model/migrations/SchemaVersionBuilder.js.map +1 -1
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.d.ts +2 -1
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.d.ts.map +1 -1
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.js +2 -1
- package/dist/src/model/queries/migrations/ExecutedMigrationsQueryHelper.js.map +1 -1
- package/dist/src/resolvers/SystemResolverContext.d.ts +2 -2
- package/dist/src/resolvers/SystemResolverContext.d.ts.map +1 -1
- package/dist/src/resolvers/SystemResolverContext.js.map +1 -1
- package/dist/src/resolvers/mutation/MigrateMutationResolver.d.ts +1 -0
- package/dist/src/resolvers/mutation/MigrateMutationResolver.d.ts.map +1 -1
- package/dist/src/resolvers/mutation/MigrateMutationResolver.js +62 -4
- package/dist/src/resolvers/mutation/MigrateMutationResolver.js.map +1 -1
- package/dist/src/resolvers/query/ExecutedMigrationsQueryResolver.d.ts.map +1 -1
- package/dist/src/resolvers/query/ExecutedMigrationsQueryResolver.js +4 -0
- package/dist/src/resolvers/query/ExecutedMigrationsQueryResolver.js.map +1 -1
- package/dist/src/schema/system.graphql.d.ts.map +1 -1
- package/dist/src/schema/system.graphql.js +30 -4
- package/dist/src/schema/system.graphql.js.map +1 -1
- package/dist/src/schema/types.d.ts +170 -106
- package/dist/src/schema/types.d.ts.map +1 -1
- package/dist/src/schema/types.js +8 -1
- package/dist/src/schema/types.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/types.d.ts +1 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tests/cases/migrations/2020-05-06-150000-composed-primary.test.js +1 -0
- package/dist/tests/cases/migrations/2020-05-06-150000-composed-primary.test.js.map +1 -1
- package/dist/tests/cases/migrations/2020-06-01-103000-event-trigger-perf.test.js +1 -0
- package/dist/tests/cases/migrations/2020-06-01-103000-event-trigger-perf.test.js.map +1 -1
- package/dist/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.js +1 -0
- package/dist/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.js.map +1 -1
- package/dist/tests/cases/migrations/2023-09-11-174000-fix-on-delete.test.js +2 -0
- package/dist/tests/cases/migrations/2023-09-11-174000-fix-on-delete.test.js.map +1 -1
- package/dist/tests/cases/migrations/2023-10-19-173000-fix-unique.test.js +1 -0
- package/dist/tests/cases/migrations/2023-10-19-173000-fix-unique.test.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -22
- package/src/SystemContainer.ts +3 -1
- package/src/bin/runMigrations.ts +1 -2
- package/src/index.ts +5 -1
- package/src/migrations/2023-10-24-140000-schema-nullable-checksum.ts +11 -0
- package/src/migrations/runner.ts +10 -6
- package/src/migrations/snapshot.ts +7 -2
- package/src/model/authorization/AuthorizationActions.ts +1 -0
- package/src/model/authorization/PermissionsFactory.ts +1 -0
- package/src/model/commands/migrations/SaveMigrationCommand.ts +20 -7
- package/src/model/dependencies/content/ContentQueryExecutor.ts +27 -0
- package/src/model/dependencies/index.ts +1 -0
- package/src/model/dtos/ExecutedMigration.ts +3 -1
- package/src/model/migrations/MigrationInput.ts +25 -0
- package/src/model/migrations/ProjectMigrator.ts +113 -47
- package/src/model/migrations/SchemaVersionBuilder.ts +10 -5
- package/src/model/queries/migrations/ExecutedMigrationsQueryHelper.ts +3 -1
- package/src/resolvers/SystemResolverContext.ts +2 -2
- package/src/resolvers/mutation/MigrateMutationResolver.ts +61 -5
- package/src/resolvers/query/ExecutedMigrationsQueryResolver.ts +6 -1
- package/src/schema/system.graphql.ts +30 -4
- package/src/schema/types.ts +145 -106
- package/src/types.ts +1 -0
- package/tests/cases/migrations/2020-05-06-150000-composed-primary.test.ts +1 -0
- package/tests/cases/migrations/2020-06-01-103000-event-trigger-perf.test.ts +1 -0
- package/tests/cases/migrations/2022-10-03-110000-table-on-delete.test.ts +1 -0
- package/tests/cases/migrations/2023-09-11-174000-fix-on-delete.test.ts +2 -0
- package/tests/cases/migrations/2023-10-19-173000-fix-unique.test.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-system-api",
|
|
3
|
-
"version": "1.4.0-alpha.
|
|
3
|
+
"version": "1.4.0-alpha.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"typings": "dist/src/index.d.ts",
|
|
@@ -13,29 +13,29 @@
|
|
|
13
13
|
"test": "vitest --dir ./tests/cases"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@contember/authorization": "1.4.0-alpha.
|
|
17
|
-
"@contember/database": "1.4.0-alpha.
|
|
18
|
-
"@contember/database-migrations": "1.4.0-alpha.
|
|
19
|
-
"@contember/dic": "1.4.0-alpha.
|
|
20
|
-
"@contember/graphql-utils": "1.4.0-alpha.
|
|
21
|
-
"@contember/logger": "1.4.0-alpha.
|
|
22
|
-
"@contember/queryable": "1.4.0-alpha.
|
|
23
|
-
"@contember/schema": "1.4.0-alpha.
|
|
24
|
-
"@contember/schema-migrations": "1.4.0-alpha.
|
|
25
|
-
"@contember/schema-utils": "1.4.0-alpha.
|
|
26
|
-
"@graphql-tools/utils": "^10.0.
|
|
27
|
-
"graphql-tag": "^2.12.
|
|
16
|
+
"@contember/authorization": "1.4.0-alpha.3",
|
|
17
|
+
"@contember/database": "1.4.0-alpha.3",
|
|
18
|
+
"@contember/database-migrations": "1.4.0-alpha.3",
|
|
19
|
+
"@contember/dic": "1.4.0-alpha.3",
|
|
20
|
+
"@contember/graphql-utils": "1.4.0-alpha.3",
|
|
21
|
+
"@contember/logger": "1.4.0-alpha.3",
|
|
22
|
+
"@contember/queryable": "1.4.0-alpha.3",
|
|
23
|
+
"@contember/schema": "1.4.0-alpha.3",
|
|
24
|
+
"@contember/schema-migrations": "1.4.0-alpha.3",
|
|
25
|
+
"@contember/schema-utils": "1.4.0-alpha.3",
|
|
26
|
+
"@graphql-tools/utils": "^10.0.8",
|
|
27
|
+
"graphql-tag": "^2.12.6"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@contember/engine-api-tester": "1.4.0-alpha.
|
|
31
|
-
"@contember/schema-definition": "1.4.0-alpha.
|
|
32
|
-
"@graphql-codegen/cli": "^
|
|
33
|
-
"@graphql-codegen/typescript": "^
|
|
34
|
-
"@graphql-codegen/typescript-operations": "^
|
|
35
|
-
"@graphql-codegen/typescript-resolvers": "^
|
|
36
|
-
"@types/node": "^
|
|
37
|
-
"graphql": "^16.
|
|
38
|
-
"pg": "^8.
|
|
30
|
+
"@contember/engine-api-tester": "1.4.0-alpha.3",
|
|
31
|
+
"@contember/schema-definition": "1.4.0-alpha.3",
|
|
32
|
+
"@graphql-codegen/cli": "^5.0.0",
|
|
33
|
+
"@graphql-codegen/typescript": "^4.0.1",
|
|
34
|
+
"@graphql-codegen/typescript-operations": "^4.0.1",
|
|
35
|
+
"@graphql-codegen/typescript-resolvers": "^4.0.1",
|
|
36
|
+
"@types/node": "^20.9.0",
|
|
37
|
+
"graphql": "^16.8.1",
|
|
38
|
+
"pg": "^8.11.3"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"graphql": ">= 14.6.0",
|
package/src/SystemContainer.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
SchemaMigrator,
|
|
8
8
|
} from '@contember/schema-migrations'
|
|
9
9
|
import {
|
|
10
|
+
ContentQueryExecutor,
|
|
10
11
|
EventResponseBuilder,
|
|
11
12
|
ExecutedMigrationsResolver,
|
|
12
13
|
IdentityFetcher,
|
|
@@ -46,6 +47,7 @@ export class SystemContainerFactory {
|
|
|
46
47
|
constructor(
|
|
47
48
|
private readonly providers: UuidProvider,
|
|
48
49
|
private readonly modificationHandlerFactory: ModificationHandlerFactory,
|
|
50
|
+
private readonly contentQueryExecutor: ContentQueryExecutor,
|
|
49
51
|
) {
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -86,7 +88,7 @@ export class SystemContainerFactory {
|
|
|
86
88
|
.addService('migrationsDatabaseMetadataResolverStoreFactory', ({ databaseMetadataResolver }) =>
|
|
87
89
|
new MigrationsDatabaseMetadataResolverStoreFactory(databaseMetadataResolver))
|
|
88
90
|
.addService('projectMigrator', ({ migrationDescriber, schemaVersionBuilder, executedMigrationsResolver, migrationsDatabaseMetadataResolverStoreFactory }) =>
|
|
89
|
-
new ProjectMigrator(migrationDescriber, schemaVersionBuilder, executedMigrationsResolver, migrationsDatabaseMetadataResolverStoreFactory))
|
|
91
|
+
new ProjectMigrator(migrationDescriber, schemaVersionBuilder, executedMigrationsResolver, migrationsDatabaseMetadataResolverStoreFactory, this.contentQueryExecutor))
|
|
90
92
|
.addService('projectTruncateExecutor', () =>
|
|
91
93
|
new ProjectTruncateExecutor())
|
|
92
94
|
.addService('migrationAlterer', () =>
|
package/src/bin/runMigrations.ts
CHANGED
|
@@ -18,8 +18,7 @@ import { createLogger, PrettyPrintLoggerHandler } from '@contember/logger'
|
|
|
18
18
|
throw new Error()
|
|
19
19
|
},
|
|
20
20
|
}),
|
|
21
|
-
{ db: dbConfig, slug: 'test', stages: [] },
|
|
22
|
-
'system',
|
|
21
|
+
{ db: dbConfig, slug: 'test', stages: [], systemSchema: 'system' },
|
|
23
22
|
{
|
|
24
23
|
buildSchema: () => Promise.resolve(emptyVersionedSchema),
|
|
25
24
|
} as unknown as SchemaVersionBuilder,
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
export { SchemaMigrator
|
|
1
|
+
export { SchemaMigrator } from '@contember/schema-migrations'
|
|
2
2
|
|
|
3
3
|
export { typeDefs, devTypeDefs, Schema } from './schema'
|
|
4
4
|
|
|
5
5
|
export {
|
|
6
|
+
ContentQueryExecutor,
|
|
7
|
+
ContentQueryExecutorContext,
|
|
8
|
+
ContentQueryExecutorQuery,
|
|
9
|
+
ContentQueryExecutorResult,
|
|
6
10
|
DatabaseContext,
|
|
7
11
|
DatabaseContextFactory,
|
|
8
12
|
Command,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MigrationBuilder } from '@contember/database-migrations'
|
|
2
|
+
|
|
3
|
+
const sql = `
|
|
4
|
+
CREATE TYPE SCHEMA_MIGRATION_TYPE AS ENUM('schema', 'content');
|
|
5
|
+
ALTER TABLE schema_migration ALTER checksum DROP NOT NULL,
|
|
6
|
+
ADD COLUMN type SCHEMA_MIGRATION_TYPE NOT NULL DEFAULT 'schema';
|
|
7
|
+
`
|
|
8
|
+
|
|
9
|
+
export default async function (builder: MigrationBuilder) {
|
|
10
|
+
builder.sql(sql)
|
|
11
|
+
}
|
package/src/migrations/runner.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
GroupMigrationsResolver,
|
|
3
|
+
MigrationGroup,
|
|
4
|
+
MigrationsRunner as DbMigrationsRunner,
|
|
5
|
+
SnapshotMigrationResolver,
|
|
6
|
+
} from '@contember/database-migrations'
|
|
2
7
|
import _20180804102200init from './2018-08-04-102200-init'
|
|
3
8
|
import _20180805130501triggereventfunction from './2018-08-05-130501-trigger-event-function'
|
|
4
9
|
import _20190114143432eventtrxid from './2019-01-14-143432-event-trx-id'
|
|
@@ -19,14 +24,13 @@ import _20220208144400dynamicstageschema from './2022-02-08-144400-dynamic-stage
|
|
|
19
24
|
import _20221003110000tableondelete from './2022-10-03-110000-table-on-delete'
|
|
20
25
|
import _20230911174000fixondelete from './2023-09-11-174000-fix-on-delete'
|
|
21
26
|
import _20231019173000fixunique from './2023-10-19-173000-fix-unique'
|
|
27
|
+
import _20231024140000schemanullablechecksum from './2023-10-24-140000-schema-nullable-checksum'
|
|
22
28
|
import snapshot from './snapshot'
|
|
23
29
|
|
|
24
|
-
import {
|
|
30
|
+
import { Connection, createDatabaseIfNotExists, DatabaseConfig, DatabaseMetadataResolver } from '@contember/database'
|
|
25
31
|
import { DatabaseContextFactory, SchemaVersionBuilder } from '../model'
|
|
26
32
|
import { ProjectConfig } from '../types'
|
|
27
33
|
import { Logger } from '@contember/logger'
|
|
28
|
-
import { GroupMigrationsResolver } from '@contember/database-migrations'
|
|
29
|
-
import { SnapshotMigrationResolver } from '@contember/database-migrations'
|
|
30
34
|
|
|
31
35
|
const migrations = {
|
|
32
36
|
'2018-08-04-102200-init': _20180804102200init,
|
|
@@ -49,6 +53,7 @@ const migrations = {
|
|
|
49
53
|
'2022-10-03-110000-table-on-delete': _20221003110000tableondelete,
|
|
50
54
|
'2023-09-11-174000-fix-on-delete': _20230911174000fixondelete,
|
|
51
55
|
'2023-10-19-173000-fix-unique': _20231019173000fixunique,
|
|
56
|
+
'2023-10-24-140000-schema-nullable-checksum': _20231024140000schemanullablechecksum,
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
|
|
@@ -56,7 +61,6 @@ export class SystemMigrationsRunner {
|
|
|
56
61
|
constructor(
|
|
57
62
|
private readonly databaseContextFactory: DatabaseContextFactory,
|
|
58
63
|
private readonly project: ProjectConfig & { db: DatabaseConfig },
|
|
59
|
-
private readonly schema: string,
|
|
60
64
|
private readonly schemaVersionBuilder: SchemaVersionBuilder,
|
|
61
65
|
private readonly migrationGroups: Record<string, MigrationGroup<unknown>>,
|
|
62
66
|
private readonly databaseMetadataResolver: DatabaseMetadataResolver,
|
|
@@ -84,7 +88,7 @@ export class SystemMigrationsRunner {
|
|
|
84
88
|
]),
|
|
85
89
|
),
|
|
86
90
|
)
|
|
87
|
-
const migrationsRunner = new DbMigrationsRunner(connection, this.
|
|
91
|
+
const migrationsRunner = new DbMigrationsRunner(connection, this.project.systemSchema, migrationResolver)
|
|
88
92
|
await migrationsRunner.migrate(message => logger.warn(message), {
|
|
89
93
|
project: this.project,
|
|
90
94
|
schemaResolver,
|
|
@@ -4,6 +4,10 @@ CREATE DOMAIN "event_data_type" AS "text"
|
|
|
4
4
|
CONSTRAINT "event_data_type_check" CHECK ((VALUE = ANY (ARRAY['create'::"text", 'update'::"text", 'delete'::"text"])));
|
|
5
5
|
CREATE DOMAIN "event_type" AS "text"
|
|
6
6
|
CONSTRAINT "event_type_check" CHECK ((VALUE = ANY (ARRAY['init'::"text", 'create'::"text", 'update'::"text", 'delete'::"text", 'run_migration'::"text"])));
|
|
7
|
+
CREATE TYPE "schema_migration_type" AS ENUM (
|
|
8
|
+
'schema',
|
|
9
|
+
'content'
|
|
10
|
+
);
|
|
7
11
|
CREATE FUNCTION "trigger_event"() RETURNS "trigger"
|
|
8
12
|
LANGUAGE "plpgsql"
|
|
9
13
|
AS $$
|
|
@@ -116,8 +120,9 @@ CREATE TABLE "schema_migration" (
|
|
|
116
120
|
"version" character varying(20) NOT NULL,
|
|
117
121
|
"name" character varying(255) NOT NULL,
|
|
118
122
|
"migration" "json" NOT NULL,
|
|
119
|
-
"checksum" character(32)
|
|
120
|
-
"executed_at" timestamp with time zone DEFAULT "now"() NOT NULL
|
|
123
|
+
"checksum" character(32),
|
|
124
|
+
"executed_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
|
125
|
+
"type" "schema_migration_type" DEFAULT 'schema'::"schema_migration_type" NOT NULL
|
|
121
126
|
);
|
|
122
127
|
CREATE SEQUENCE "schema_migration_id_seq"
|
|
123
128
|
AS integer
|
|
@@ -8,4 +8,5 @@ export namespace AuthorizationActions {
|
|
|
8
8
|
export const PROJECT_HISTORY_ANY = Authorizator.createAction(Resources.project, 'historyAny')
|
|
9
9
|
|
|
10
10
|
export const PROJECT_MIGRATE = Authorizator.createAction(Resources.project, 'migrate')
|
|
11
|
+
export const PROJECT_LIST_MIGRATIONS = Authorizator.createAction(Resources.project, 'listMigrations')
|
|
11
12
|
}
|
|
@@ -7,6 +7,7 @@ export class PermissionsFactory {
|
|
|
7
7
|
const permissions = new Permissions()
|
|
8
8
|
permissions.allow(ProjectRole.ADMIN, { resource: Permissions.ALL, privilege: Permissions.ALL })
|
|
9
9
|
permissions.allow(ProjectRole.DEPLOYER, AuthorizationActions.PROJECT_MIGRATE)
|
|
10
|
+
permissions.allow(ProjectRole.DEPLOYER, AuthorizationActions.PROJECT_LIST_MIGRATIONS)
|
|
10
11
|
|
|
11
12
|
return permissions
|
|
12
13
|
}
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
import { InsertBuilder, QueryBuilder } from '@contember/database'
|
|
2
|
-
import { calculateMigrationChecksum
|
|
2
|
+
import { calculateMigrationChecksum } from '@contember/schema-migrations'
|
|
3
3
|
import { Command } from '../Command'
|
|
4
|
+
import { MigrationInput } from '../../migrations/MigrationInput'
|
|
4
5
|
|
|
5
|
-
export class SaveMigrationCommand implements Command<
|
|
6
|
-
constructor(private readonly migration:
|
|
6
|
+
export class SaveMigrationCommand implements Command<number> {
|
|
7
|
+
constructor(private readonly migration: MigrationInput) {}
|
|
8
|
+
|
|
9
|
+
public async execute({ db }: Command.Args): Promise<number> {
|
|
10
|
+
const { type, ...migrationData } = this.migration
|
|
11
|
+
|
|
12
|
+
if ('queries' in migrationData) {
|
|
13
|
+
// in case of content migration, we don't want to store the queries
|
|
14
|
+
migrationData.queries = []
|
|
15
|
+
}
|
|
7
16
|
|
|
8
|
-
public async execute({ db }: Command.Args): Promise<void> {
|
|
9
17
|
const values: QueryBuilder.Values = {
|
|
10
18
|
version: this.migration.version,
|
|
11
19
|
name: this.migration.name,
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
type,
|
|
21
|
+
migration: JSON.stringify(migrationData),
|
|
22
|
+
checksum: this.migration.type === 'schema' ? calculateMigrationChecksum(this.migration) : null,
|
|
14
23
|
}
|
|
15
|
-
|
|
24
|
+
|
|
25
|
+
const result = await InsertBuilder.create()
|
|
16
26
|
.into('schema_migration')
|
|
17
27
|
.values(values)
|
|
28
|
+
.returning<{id: number}>('id')
|
|
18
29
|
.execute(db)
|
|
30
|
+
|
|
31
|
+
return result[0].id
|
|
19
32
|
}
|
|
20
33
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Stage } from '../../dtos'
|
|
2
|
+
import { DatabaseContext } from '../../database'
|
|
3
|
+
import { Schema } from '@contember/schema'
|
|
4
|
+
import { ExecutionResult } from 'graphql'
|
|
5
|
+
import { DatabaseMetadata } from '@contember/database'
|
|
6
|
+
|
|
7
|
+
export interface ContentQueryExecutorContext {
|
|
8
|
+
schema: Schema & { id: number }
|
|
9
|
+
identity: { id: string }
|
|
10
|
+
db: DatabaseContext
|
|
11
|
+
stage: Stage
|
|
12
|
+
project: { slug: string; systemSchema: string }
|
|
13
|
+
databaseMetadata: DatabaseMetadata
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ContentQueryExecutorQuery {
|
|
17
|
+
query: string
|
|
18
|
+
variables?: Record<string, any>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ContentQueryExecutor {
|
|
22
|
+
execute: (ctx: ContentQueryExecutorContext, query: ContentQueryExecutorQuery) => Promise<ContentQueryExecutorResult>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ContentQueryExecutorResult =
|
|
26
|
+
| { ok: false; errors: string[] }
|
|
27
|
+
| { ok: true; result: ExecutionResult }
|
|
@@ -9,7 +9,9 @@ export class ExecutedMigration implements Migration {
|
|
|
9
9
|
public readonly formatVersion: number,
|
|
10
10
|
public readonly modifications: Migration.Modification[],
|
|
11
11
|
public readonly executedAt: Date,
|
|
12
|
-
public readonly checksum: string,
|
|
12
|
+
public readonly checksum: string | null,
|
|
13
|
+
public readonly type: 'schema' | 'content',
|
|
14
|
+
|
|
13
15
|
) {
|
|
14
16
|
this.version = MigrationVersionHelper.extractVersion(name)
|
|
15
17
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Migration } from '@contember/schema-migrations'
|
|
2
|
+
|
|
3
|
+
export type SchemaMigrationInput =
|
|
4
|
+
& {
|
|
5
|
+
readonly type: 'schema'
|
|
6
|
+
}
|
|
7
|
+
& Migration
|
|
8
|
+
|
|
9
|
+
export type ContentMigrationInput = {
|
|
10
|
+
readonly type: 'content'
|
|
11
|
+
readonly version: string // YYYY-MM-DD-HHIISS
|
|
12
|
+
readonly name: string // version-label
|
|
13
|
+
readonly queries: readonly ContentMigrationQuery[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ContentMigrationQuery = {
|
|
17
|
+
readonly query: string
|
|
18
|
+
readonly variables?: any
|
|
19
|
+
readonly stage?: string
|
|
20
|
+
readonly checkMutationResult?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type MigrationInput =
|
|
24
|
+
| SchemaMigrationInput
|
|
25
|
+
| ContentMigrationInput
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'
|
|
2
|
-
import { Client, QueryError, wrapIdentifier } from '@contember/database'
|
|
2
|
+
import { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'
|
|
3
3
|
import { Schema } from '@contember/schema'
|
|
4
4
|
import { SaveMigrationCommand } from '../commands'
|
|
5
5
|
import { Stage } from '../dtos'
|
|
@@ -9,10 +9,9 @@ import { MigrateErrorCode } from '../../schema'
|
|
|
9
9
|
import { SchemaVersionBuilder } from './SchemaVersionBuilder'
|
|
10
10
|
import { SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'
|
|
11
11
|
import { logger } from '@contember/logger'
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from '../metadata/SchemaDatabaseMetadataResolverStore'
|
|
12
|
+
import { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'
|
|
13
|
+
import { ContentMigrationQuery, MigrationInput } from './MigrationInput'
|
|
14
|
+
import { ContentQueryExecutor } from '../dependencies'
|
|
16
15
|
|
|
17
16
|
export class ProjectMigrator {
|
|
18
17
|
constructor(
|
|
@@ -20,17 +19,20 @@ export class ProjectMigrator {
|
|
|
20
19
|
private readonly schemaVersionBuilder: SchemaVersionBuilder,
|
|
21
20
|
private readonly executedMigrationsResolver: ExecutedMigrationsResolver,
|
|
22
21
|
private readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,
|
|
22
|
+
private readonly contentQueryExecutor: ContentQueryExecutor,
|
|
23
23
|
) {}
|
|
24
24
|
|
|
25
|
-
public async migrate(
|
|
26
|
-
db: DatabaseContext
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
public async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {
|
|
26
|
+
db: DatabaseContext
|
|
27
|
+
project: { slug: string; systemSchema: string }
|
|
28
|
+
identity: { id: string }
|
|
29
|
+
stages: Stage[]
|
|
30
|
+
migrationsToExecute: readonly MigrationInput[]
|
|
31
|
+
options: {
|
|
30
32
|
ignoreOrder?: boolean
|
|
31
33
|
skipExecuted?: boolean
|
|
32
|
-
}
|
|
33
|
-
) {
|
|
34
|
+
}
|
|
35
|
+
}) {
|
|
34
36
|
if (migrationsToExecute.length === 0) {
|
|
35
37
|
return
|
|
36
38
|
}
|
|
@@ -42,21 +44,73 @@ export class ProjectMigrator {
|
|
|
42
44
|
const metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)
|
|
43
45
|
|
|
44
46
|
for (const migration of sorted) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
|
|
48
|
+
if (migration.type === 'schema') {
|
|
49
|
+
const formatVersion = migration.formatVersion
|
|
50
|
+
|
|
51
|
+
for (const modification of migration.modifications) {
|
|
52
|
+
[schema] = await this.applyModification(
|
|
53
|
+
db.client,
|
|
54
|
+
stages,
|
|
55
|
+
schema,
|
|
56
|
+
modification,
|
|
57
|
+
formatVersion,
|
|
58
|
+
migration.version,
|
|
59
|
+
db.client.schema,
|
|
60
|
+
metadataStore,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
} else if (migration.type === 'content') {
|
|
64
|
+
const schemaWithId = {
|
|
65
|
+
...schema,
|
|
66
|
+
id,
|
|
67
|
+
}
|
|
68
|
+
for (const query of migration.queries) {
|
|
69
|
+
await this.executeContentMigration({
|
|
70
|
+
version: migration.version,
|
|
71
|
+
query,
|
|
72
|
+
db,
|
|
73
|
+
schema: schemaWithId,
|
|
74
|
+
stages,
|
|
75
|
+
databaseMetadata: await metadataStore.getMetadata(db.client.schema),
|
|
76
|
+
identity,
|
|
77
|
+
project,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
id = await db.commandBus.execute(new SaveMigrationCommand(migration))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async executeContentMigration({ query, stages, version, ...ctx }: {
|
|
87
|
+
version: string
|
|
88
|
+
db: DatabaseContext
|
|
89
|
+
query: ContentMigrationQuery
|
|
90
|
+
stages: Stage[]
|
|
91
|
+
schema: Schema & { id: number }
|
|
92
|
+
project: { slug: string; systemSchema: string }
|
|
93
|
+
identity: { id: string }
|
|
94
|
+
databaseMetadata: DatabaseMetadata
|
|
95
|
+
}) {
|
|
96
|
+
const queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages
|
|
97
|
+
for (const stage of queryStages) {
|
|
98
|
+
const response = await this.contentQueryExecutor.execute({
|
|
99
|
+
stage,
|
|
100
|
+
...ctx,
|
|
101
|
+
}, query)
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new FailedContentMigrationError(version, response.errors.join(', '))
|
|
105
|
+
}
|
|
106
|
+
if (query.checkMutationResult !== false) {
|
|
107
|
+
for (const [key, value] of Object.entries(response.result.data ?? {})) {
|
|
108
|
+
if (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {
|
|
109
|
+
const errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'
|
|
110
|
+
throw new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
58
113
|
}
|
|
59
|
-
await db.commandBus.execute(new SaveMigrationCommand(migration))
|
|
60
114
|
}
|
|
61
115
|
}
|
|
62
116
|
|
|
@@ -64,45 +118,49 @@ export class ProjectMigrator {
|
|
|
64
118
|
db: DatabaseContext,
|
|
65
119
|
schema: Schema,
|
|
66
120
|
version: string,
|
|
67
|
-
migrationsToExecute: readonly
|
|
121
|
+
migrationsToExecute: readonly MigrationInput[],
|
|
68
122
|
{ ignoreOrder, skipExecuted }: {
|
|
69
123
|
ignoreOrder: boolean
|
|
70
124
|
skipExecuted: boolean
|
|
71
125
|
},
|
|
72
|
-
): Promise<readonly
|
|
126
|
+
): Promise<readonly MigrationInput[]> {
|
|
73
127
|
const executedMigrations = await this.executedMigrationsResolver.getMigrations(db)
|
|
74
|
-
const toExecute = []
|
|
128
|
+
const toExecute: MigrationInput[] = []
|
|
75
129
|
let skippedErrors: SchemaValidatorSkippedErrors[] = []
|
|
76
130
|
for (const migration of migrationsToExecute) {
|
|
77
131
|
const executedMigration = executedMigrations.find(it => it.version === migration.version)
|
|
78
132
|
if (executedMigration) {
|
|
79
|
-
if (skipExecuted && executedMigration.checksum === calculateMigrationChecksum(migration)) {
|
|
133
|
+
if (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {
|
|
80
134
|
continue
|
|
81
135
|
} else {
|
|
82
136
|
throw new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)
|
|
83
137
|
}
|
|
84
138
|
}
|
|
139
|
+
|
|
85
140
|
if (migration.version < version && !ignoreOrder) {
|
|
86
141
|
throw new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)
|
|
87
142
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
143
|
+
if (migration.type === 'schema') {
|
|
144
|
+
const described = this.migrationDescriber.describeModifications(schema, migration)
|
|
145
|
+
if (described.length === 0) {
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
const latestModification = described[described.length - 1]
|
|
149
|
+
schema = latestModification.schema
|
|
150
|
+
skippedErrors = [
|
|
151
|
+
...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),
|
|
152
|
+
...migration.skippedErrors ?? [],
|
|
153
|
+
]
|
|
154
|
+
const errors = SchemaValidator.validate(schema, skippedErrors)
|
|
155
|
+
if (errors.length > 0) {
|
|
156
|
+
throw new InvalidSchemaError(
|
|
157
|
+
migration.version,
|
|
158
|
+
'Migration generates invalid schema: \n' +
|
|
159
|
+
errors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\n'),
|
|
160
|
+
)
|
|
161
|
+
}
|
|
105
162
|
}
|
|
163
|
+
|
|
106
164
|
toExecute.push(migration)
|
|
107
165
|
}
|
|
108
166
|
// intentionally not using skippedErrors here
|
|
@@ -180,3 +238,11 @@ export class MigrationFailedError extends MigrationError {
|
|
|
180
238
|
export class InvalidSchemaError extends MigrationError {
|
|
181
239
|
code = MigrateErrorCode.InvalidSchema
|
|
182
240
|
}
|
|
241
|
+
|
|
242
|
+
export class FailedContentMigrationError extends MigrationError {
|
|
243
|
+
code = MigrateErrorCode.ContentMigrationFailed
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export class NotSuccessfulContentMigrationError extends MigrationError {
|
|
247
|
+
code = MigrateErrorCode.ContentMigrationNotSuccessful
|
|
248
|
+
}
|
|
@@ -22,11 +22,16 @@ export class SchemaVersionBuilder {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const schema = newMigrations.reduce(
|
|
25
|
-
(schema, migration) =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
(schema, migration) => {
|
|
26
|
+
if (migration.type !== 'schema') {
|
|
27
|
+
return schema
|
|
28
|
+
}
|
|
29
|
+
return ({
|
|
30
|
+
...this.schemaMigrator.applyModifications(schema, migration.modifications, migration.formatVersion),
|
|
31
|
+
version: migration.version,
|
|
32
|
+
id: migration.id,
|
|
33
|
+
})
|
|
34
|
+
},
|
|
30
35
|
after?.notNormalized ?? emptyVersionedSchema.notNormalized,
|
|
31
36
|
)
|
|
32
37
|
const normalized = normalizeSchema(schema)
|
|
@@ -2,7 +2,7 @@ import { SelectBuilder } from '@contember/database'
|
|
|
2
2
|
import { ExecutedMigration } from '../../dtos'
|
|
3
3
|
import { VERSION_INITIAL } from '@contember/schema-migrations'
|
|
4
4
|
|
|
5
|
-
type ExecutedMigrationRow = { id: number; name: string; migration: any; checksum: string; executed_at: Date }
|
|
5
|
+
type ExecutedMigrationRow = { id: number; name: string; migration: any; checksum: string | null; executed_at: Date; type: 'content' | 'schema' }
|
|
6
6
|
export const createExecutedMigrationQueryBuilder = () =>
|
|
7
7
|
SelectBuilder.create<ExecutedMigrationRow>()
|
|
8
8
|
.select('id')
|
|
@@ -10,6 +10,7 @@ export const createExecutedMigrationQueryBuilder = () =>
|
|
|
10
10
|
.select('migration')
|
|
11
11
|
.select('checksum')
|
|
12
12
|
.select('executed_at')
|
|
13
|
+
.select('type')
|
|
13
14
|
.from('schema_migration')
|
|
14
15
|
.orderBy('version')
|
|
15
16
|
|
|
@@ -21,4 +22,5 @@ export const createExecutedMigrationDto = (row: ExecutedMigrationRow) =>
|
|
|
21
22
|
row.migration.modifications,
|
|
22
23
|
row.executed_at,
|
|
23
24
|
row.checksum,
|
|
25
|
+
row.type,
|
|
24
26
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Authorizator } from '@contember/authorization'
|
|
2
2
|
import { Schema } from '@contember/schema'
|
|
3
3
|
import { ForbiddenError } from '@contember/graphql-utils'
|
|
4
|
-
import { DatabaseContext, Identity,
|
|
4
|
+
import { DatabaseContext, Identity, VersionedSchema } from '../model'
|
|
5
5
|
import { ProjectConfig } from '../types'
|
|
6
6
|
import { StagePermissionsFactory } from '../model/authorization/StagePermissionsFactory'
|
|
7
7
|
import { StageScope } from '../model/authorization/StageScope'
|
|
@@ -13,7 +13,7 @@ export class SystemResolverContextFactory {
|
|
|
13
13
|
) {}
|
|
14
14
|
|
|
15
15
|
public async create(
|
|
16
|
-
schema:
|
|
16
|
+
schema: VersionedSchema,
|
|
17
17
|
systemDbContext: DatabaseContext,
|
|
18
18
|
project: ProjectConfig,
|
|
19
19
|
identity: Identity,
|