@contember/engine-system-api 2.1.0-alpha.27 → 2.1.0-alpha.29
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/src/model/migrations/ProjectMigrator.cjs +14 -4
- package/dist/development/src/model/migrations/ProjectMigrator.cjs.map +1 -1
- package/dist/development/src/model/migrations/ProjectMigrator.js +14 -4
- package/dist/development/src/model/migrations/ProjectMigrator.js.map +1 -1
- package/dist/production/src/model/migrations/ProjectMigrator.cjs +14 -4
- package/dist/production/src/model/migrations/ProjectMigrator.cjs.map +1 -1
- package/dist/production/src/model/migrations/ProjectMigrator.js +14 -4
- package/dist/production/src/model/migrations/ProjectMigrator.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/model/migrations/ProjectMigrator.d.ts +1 -0
- package/dist/types/model/migrations/ProjectMigrator.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/model/migrations/ProjectMigrator.ts +20 -4
|
@@ -45,7 +45,7 @@ class ProjectMigrator {
|
|
|
45
45
|
modification,
|
|
46
46
|
formatVersion,
|
|
47
47
|
migration.version,
|
|
48
|
-
|
|
48
|
+
project,
|
|
49
49
|
metadataStore
|
|
50
50
|
);
|
|
51
51
|
}
|
|
@@ -150,7 +150,13 @@ class ProjectMigrator {
|
|
|
150
150
|
}
|
|
151
151
|
return toExecute;
|
|
152
152
|
}
|
|
153
|
-
|
|
153
|
+
sqlPreprocessor(text, variables) {
|
|
154
|
+
return Object.entries(variables).reduce(
|
|
155
|
+
(content, [key, value]) => content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value),
|
|
156
|
+
text
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
async applyModification(db, stages, schema, modification, formatVersion, migrationVersion, project, metadataStore) {
|
|
154
160
|
const {
|
|
155
161
|
getSql,
|
|
156
162
|
schema: newSchema
|
|
@@ -158,11 +164,15 @@ class ProjectMigrator {
|
|
|
158
164
|
for (const stage of stages) {
|
|
159
165
|
const databaseMetadata = await metadataStore.getMetadata(stage.schema);
|
|
160
166
|
const sql = getSql({
|
|
161
|
-
systemSchema,
|
|
167
|
+
systemSchema: project.systemSchema,
|
|
162
168
|
databaseMetadata,
|
|
163
169
|
invalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema)
|
|
164
170
|
});
|
|
165
|
-
|
|
171
|
+
const processedSql = this.sqlPreprocessor(sql, {
|
|
172
|
+
project_slug: project.slug,
|
|
173
|
+
system_schema: project.systemSchema
|
|
174
|
+
});
|
|
175
|
+
await this.executeOnStage(db, stage, processedSql, migrationVersion);
|
|
166
176
|
}
|
|
167
177
|
return [newSchema];
|
|
168
178
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectMigrator.cjs","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tdb.client.schema,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tsystemSchema: string,\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, sql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["SaveMigrationCommand","ImplementationException","SaveSchemaCommand","calculateSchemaChecksum","calculateMigrationChecksum","MigrationVersionHelper","errors","SchemaValidator","wrapIdentifier","QueryError","logger","MigrateErrorCode"],"mappings":";;;;;;;;;;;;;;;AAiBO,MAAM,gBAAgB;AAAA,EAC5B,YACkB,oBACA,gBACA,4BACA,gDACA,sBAChB;AALgB,SAAA,qBAAA;AACA,SAAA,iBAAA;AACA,SAAA,6BAAA;AACA,SAAA,iDAAA;AACA,SAAA,uBAAA;AAAA,EAAA;AAAA,EAGlB,MAAa,QAAQ,EAAE,IAAI,SAAS,UAAU,SAAS,EAAE,cAAc,OAAO,eAAe,MAAS,GAAA,qBAAqB,UAUxH;AACE,QAAA,oBAAoB,WAAW,GAAG;AACrC;AAAA,IAAA;AAED,UAAM,iBAAiB,MAAM,KAAK,eAAe,MAAM,EAAE,IAAI;AAC7D,QAAI,SAAS,eAAe;AACxB,QAAA,KAAK,eAAe,KAAK;AAE7B,UAAM,YAAY,MAAM,KAAK,mBAAmB,IAAI,QAAQ,eAAe,KAAK,WAAW,MAAM,qBAAqB,EAAE,aAAa,cAAc;AAC/I,QAAA,UAAU,WAAW,GAAG;AAC3B;AAAA,IAAA;AAGD,UAAM,SAAS,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC;AAE/E,UAAM,gBAAgB,KAAK,+CAA+C,OAAO,EAAE;AAEnF,eAAW,aAAa,QAAQ;AAE3B,UAAA,UAAU,SAAS,UAAU;AAChC,cAAM,gBAAgB,UAAU;AAErB,mBAAA,gBAAgB,UAAU,eAAe;AAClD,WAAA,MAAM,IAAI,MAAM,KAAK;AAAA,YACrB,GAAG;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,UAAU;AAAA,YACV,GAAG,OAAO;AAAA,YACV;AAAA,UACD;AAAA,QAAA;AAAA,MACD,WACU,UAAU,SAAS,WAAW;AACxC,YAAI,CAAC,IAAI;AACR,gBAAM,IAAI,4BAA4B,UAAU,SAAS,iDAAiD;AAAA,QAAA;AAEhG,mBAAA,SAAS,UAAU,SAAS;AACtC,gBAAM,KAAK,wBAAwB;AAAA,YAClC,SAAS,UAAU;AAAA,YACnB;AAAA,YACA;AAAA,YACA;AAAA,YACA,YAAY,EAAE,GAAG;AAAA,YACjB;AAAA,YACA,kBAAkB,MAAM,cAAc,YAAY,GAAG,OAAO,MAAM;AAAA,YAClE;AAAA,YACA;AAAA,UAAA,CACA;AAAA,QAAA;AAGI,cAAA,GAAG,OAAO,MAAM,+BAA+B;AAC/C,cAAA,GAAG,OAAO,MAAM,8BAA8B;AAAA,MAAA;AAGrD,WAAK,MAAM,GAAG,WAAW,QAAQ,IAAIA,qBAAAA,qBAAqB,SAAS,CAAC;AAAA,IAAA;AAErE,QAAI,CAAC,IAAI;AACR,YAAM,IAAIC,WAAAA,wBAAwB;AAAA,IAAA;AAGnC,UAAM,GAAG,WAAW,QAAQ,IAAIC,kBAAAA,kBAAkB;AAAA,MACjD;AAAA,MACA,MAAM;AAAA,QACL;AAAA,QACA,+BAAe,KAAK;AAAA,QACpB,SAAS,OAAO,OAAO,SAAS,CAAC,EAAE;AAAA,QACnC,UAAUC,oCAAwB,MAAM;AAAA,MAAA;AAAA,IACzC,CACA,CAAC;AAAA,EAAA;AAAA,EAGH,MAAc,wBAAwB,EAAE,OAAO,QAAQ,SAAS,GAAG,OAUhE;AACI,UAAA,cAAc,MAAM,QAAQ,OAAO,OAAO,QAAM,GAAG,SAAS,MAAM,KAAK,IAAI;AACjF,eAAW,SAAS,aAAa;AAChC,YAAM,WAAW,MAAM,KAAK,qBAAqB,QAAQ;AAAA,QACxD;AAAA,QACA,GAAG;AAAA,SACD,KAAK;AAEJ,UAAA,CAAC,SAAS,IAAI;AACjB,cAAM,IAAI,4BAA4B,SAAS,SAAS,OAAO,KAAK,IAAI,CAAC;AAAA,MAAA;AAEtE,UAAA,MAAM,wBAAwB,OAAO;AAC7B,mBAAA,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,OAAO,QAAQ,CAAA,CAAE,GAAG;AAClE,cAAA,OAAO,UAAU,YAAY,UAAU,QAAQ,QAAQ,SAAS,CAAC,MAAM,IAAI;AAC9E,kBAAM,eAAe,kBAAkB,QAAQ,MAAM,eAAe;AACpE,kBAAM,IAAI,mCAAmC,SAAS,YAAY,GAAG,YAAY,YAAY,EAAE;AAAA,UAAA;AAAA,QAChG;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EAGD,MAAc,mBACb,IACA,QACA,SACA,qBACA,EAAE,aAAa,gBAIsB;AACrC,UAAM,qBAAqB,MAAM,KAAK,2BAA2B,cAAc,EAAE;AACjF,UAAM,YAA8B,CAAC;AACrC,QAAI,gBAAgD,CAAC;AACrD,eAAW,aAAa,qBAAqB;AAC5C,YAAM,oBAAoB,mBAAmB,KAAK,QAAM,GAAG,YAAY,UAAU,OAAO;AACxF,UAAI,mBAAmB;AAClB,YAAA,iBAAiB,UAAU,SAAS,YAAY,kBAAkB,aAAaC,iBAAAA,2BAA2B,SAAS,IAAI;AAC1H;AAAA,QAAA,OACM;AACN,gBAAM,IAAI,8BAA8B,UAAU,SAAS,+BAA+B;AAAA,QAAA;AAAA,MAC3F;AAGD,UAAI,WAAW,UAAU,UAAU,WAAW,CAAC,aAAa;AAC3D,cAAM,IAAI,+BAA+B,UAAU,SAAS,yCAAyC,OAAO,EAAE;AAAA,MAAA;AAE3G,UAAA,UAAU,SAAS,UAAU;AAChC,cAAM,YAAY,KAAK,mBAAmB,sBAAsB,QAAQ,SAAS;AAC7E,YAAA,UAAU,WAAW,GAAG;AAC3B;AAAA,QAAA;AAED,cAAM,qBAAqB,UAAU,UAAU,SAAS,CAAC;AACzD,iBAAS,mBAAmB;AACZ,wBAAA;AAAA,UACf,GAAG,cAAc,OAAO,CAAA,OAAM,GAAG,aAAaC,wCAAuB,eAAe,GAAG,SAAS,KAAK,UAAU,OAAO;AAAA,UACtH,GAAG,UAAU,iBAAiB,CAAA;AAAA,QAC/B;AACA,cAAMC,UAASC,YAAA,gBAAgB,SAAS,QAAQ,aAAa;AACzDD,YAAAA,QAAO,SAAS,GAAG;AACtB,gBAAM,IAAI;AAAA,YACT,UAAU;AAAA,YACV,2CACAA,QAAO,IAAI,QAAM,GAAG,GAAG,KAAK,KAAK,GAAG,CAAC,MAAM,GAAG,IAAI,KAAK,GAAG,OAAO,EAAE,EAAE,KAAK,IAAI;AAAA,UAC/E;AAAA,QAAA;AAAA,MACD;AAGD,gBAAU,KAAK,SAAS;AAAA,IAAA;AAGnB,UAAA,SAASC,YAAAA,gBAAgB,SAAS,MAAM;AAC1C,QAAA,OAAO,SAAS,GAAG;AACtB,YAAM,IAAI;AAAA,QACT,UAAU,UAAU,SAAS,CAAC,EAAE;AAAA,QAChC,2CACA,OAAO,IAAI,CAAA,OAAM,GAAG,KAAK,KAAK,GAAG,IAAI,OAAO,GAAG,OAAO,EAAE,KAAK,IAAI;AAAA,MAClE;AAAA,IAAA;AAEM,WAAA;AAAA,EAAA;AAAA,EAGR,MAAc,kBACb,IACA,QACA,QACA,cACA,eACA,kBACA,cACA,eACoB;AACd,UAAA;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,IAAA,IACL,KAAK,mBAAmB,qBAAqB,QAAQ,cAAc,EAAE,eAAe;AACxF,eAAW,SAAS,QAAQ;AAC3B,YAAM,mBAAmB,MAAM,cAAc,YAAY,MAAM,MAAM;AACrE,YAAM,MAAM,OAAO;AAAA,QAClB;AAAA,QACA;AAAA,QACA,4BAA4B,MAAM,cAAc,WAAW,MAAM,MAAM;AAAA,MAAA,CACvE;AACD,YAAM,KAAK,eAAe,IAAI,OAAO,KAAK,gBAAgB;AAAA,IAAA;AAE3D,WAAO,CAAC,SAAS;AAAA,EAAA;AAAA,EAGlB,MAAc,eAAe,IAAY,OAAc,KAAa,kBAA0B;AAC7F,UAAM,GAAG,MAAM,8BAA8BC,SAAAA,eAAe,MAAM,MAAM,CAAC;AACrE,QAAA;AACG,YAAA,GAAG,MAAM,GAAG;AAAA,aACV,GAAG;AACX,UAAI,aAAaC,SAAAA,YAAY;AAC5BC,eAAAA,OAAO,MAAM,GAAG,EAAE,SAAS,oBAAoB;AAC/C,cAAM,IAAI,qBAAqB,kBAAkB,EAAE,OAAO;AAAA,MAAA;AAErD,YAAA;AAAA,IAAA;AAAA,EACP;AAEF;AAEO,MAAe,uBAAuB,MAAM;AAAA,EAGlD,YAA4B,SAAiC,gBAAwB;AACpF,UAAM,GAAG,OAAO,KAAK,cAAc,EAAE;AADV,SAAA,UAAA;AAAiC,SAAA,iBAAA;AAAA,EAAA;AAG9D;AAEO,MAAM,uCAAuC,eAAe;AAAA,EAA5D,cAAA;AAAA,UAAA,GAAA,SAAA;AACN,kBAAA,MAAA,QAAOC,MAAAA,iBAAiB,gBAAA;AAAA,EAAA;AACzB;AAEO,MAAM,sCAAsC,eAAe;AAAA,EAA3D,cAAA;AAAA,UAAA,GAAA,SAAA;AACN,kBAAA,MAAA,QAAOA,MAAAA,iBAAiB,eAAA;AAAA,EAAA;AACzB;AAEO,MAAM,6BAA6B,eAAe;AAAA,EAAlD,cAAA;AAAA,UAAA,GAAA,SAAA;AACN,kBAAA,MAAA,QAAOA,MAAAA,iBAAiB,eAAA;AAAA,EAAA;AACzB;AAEO,MAAM,2BAA2B,eAAe;AAAA,EAAhD,cAAA;AAAA,UAAA,GAAA,SAAA;AACN,kBAAA,MAAA,QAAOA,MAAAA,iBAAiB,aAAA;AAAA,EAAA;AACzB;AAEO,MAAM,oCAAoC,eAAe;AAAA,EAAzD,cAAA;AAAA,UAAA,GAAA,SAAA;AACN,kBAAA,MAAA,QAAOA,MAAAA,iBAAiB,sBAAA;AAAA,EAAA;AACzB;AAEO,MAAM,2CAA2C,eAAe;AAAA,EAAhE,cAAA;AAAA,UAAA,GAAA,SAAA;AACN,kBAAA,MAAA,QAAOA,MAAAA,iBAAiB,6BAAA;AAAA,EAAA;AACzB;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"ProjectMigrator.cjs","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\ntype MigrationVariables = {\n\tsystem_schema: string\n\tproject_slug: string\n}\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate sqlPreprocessor(text: string, variables: MigrationVariables): string {\n\t\treturn Object.entries(variables).reduce(\n\t\t\t(content, [key, value]) => content.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value),\n\t\t\ttext,\n\t\t)\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tproject: { slug: string; systemSchema: string },\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema: project.systemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tconst processedSql = this.sqlPreprocessor(sql, {\n\t\t\t\tproject_slug: project.slug,\n\t\t\t\tsystem_schema: project.systemSchema,\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, processedSql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["SaveMigrationCommand","ImplementationException","SaveSchemaCommand","calculateSchemaChecksum","calculateMigrationChecksum","MigrationVersionHelper","errors","SchemaValidator","wrapIdentifier","QueryError","logger","MigrateErrorCode"],"mappings":"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"}
|
|
@@ -43,7 +43,7 @@ class ProjectMigrator {
|
|
|
43
43
|
modification,
|
|
44
44
|
formatVersion,
|
|
45
45
|
migration.version,
|
|
46
|
-
|
|
46
|
+
project,
|
|
47
47
|
metadataStore
|
|
48
48
|
);
|
|
49
49
|
}
|
|
@@ -148,7 +148,13 @@ class ProjectMigrator {
|
|
|
148
148
|
}
|
|
149
149
|
return toExecute;
|
|
150
150
|
}
|
|
151
|
-
|
|
151
|
+
sqlPreprocessor(text, variables) {
|
|
152
|
+
return Object.entries(variables).reduce(
|
|
153
|
+
(content, [key, value]) => content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value),
|
|
154
|
+
text
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
async applyModification(db, stages, schema, modification, formatVersion, migrationVersion, project, metadataStore) {
|
|
152
158
|
const {
|
|
153
159
|
getSql,
|
|
154
160
|
schema: newSchema
|
|
@@ -156,11 +162,15 @@ class ProjectMigrator {
|
|
|
156
162
|
for (const stage of stages) {
|
|
157
163
|
const databaseMetadata = await metadataStore.getMetadata(stage.schema);
|
|
158
164
|
const sql = getSql({
|
|
159
|
-
systemSchema,
|
|
165
|
+
systemSchema: project.systemSchema,
|
|
160
166
|
databaseMetadata,
|
|
161
167
|
invalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema)
|
|
162
168
|
});
|
|
163
|
-
|
|
169
|
+
const processedSql = this.sqlPreprocessor(sql, {
|
|
170
|
+
project_slug: project.slug,
|
|
171
|
+
system_schema: project.systemSchema
|
|
172
|
+
});
|
|
173
|
+
await this.executeOnStage(db, stage, processedSql, migrationVersion);
|
|
164
174
|
}
|
|
165
175
|
return [newSchema];
|
|
166
176
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectMigrator.js","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tdb.client.schema,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tsystemSchema: string,\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, sql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["errors"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectMigrator.js","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\ntype MigrationVariables = {\n\tsystem_schema: string\n\tproject_slug: string\n}\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate sqlPreprocessor(text: string, variables: MigrationVariables): string {\n\t\treturn Object.entries(variables).reduce(\n\t\t\t(content, [key, value]) => content.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value),\n\t\t\ttext,\n\t\t)\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tproject: { slug: string; systemSchema: string },\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema: project.systemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tconst processedSql = this.sqlPreprocessor(sql, {\n\t\t\t\tproject_slug: project.slug,\n\t\t\t\tsystem_schema: project.systemSchema,\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, processedSql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["errors"],"mappings":"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"}
|
|
@@ -45,7 +45,7 @@ class ProjectMigrator {
|
|
|
45
45
|
modification,
|
|
46
46
|
formatVersion,
|
|
47
47
|
migration.version,
|
|
48
|
-
|
|
48
|
+
project,
|
|
49
49
|
metadataStore
|
|
50
50
|
);
|
|
51
51
|
}
|
|
@@ -150,7 +150,13 @@ class ProjectMigrator {
|
|
|
150
150
|
}
|
|
151
151
|
return toExecute;
|
|
152
152
|
}
|
|
153
|
-
|
|
153
|
+
sqlPreprocessor(text, variables) {
|
|
154
|
+
return Object.entries(variables).reduce(
|
|
155
|
+
(content, [key, value]) => content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value),
|
|
156
|
+
text
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
async applyModification(db, stages, schema, modification, formatVersion, migrationVersion, project, metadataStore) {
|
|
154
160
|
const {
|
|
155
161
|
getSql,
|
|
156
162
|
schema: newSchema
|
|
@@ -158,11 +164,15 @@ class ProjectMigrator {
|
|
|
158
164
|
for (const stage of stages) {
|
|
159
165
|
const databaseMetadata = await metadataStore.getMetadata(stage.schema);
|
|
160
166
|
const sql = getSql({
|
|
161
|
-
systemSchema,
|
|
167
|
+
systemSchema: project.systemSchema,
|
|
162
168
|
databaseMetadata,
|
|
163
169
|
invalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema)
|
|
164
170
|
});
|
|
165
|
-
|
|
171
|
+
const processedSql = this.sqlPreprocessor(sql, {
|
|
172
|
+
project_slug: project.slug,
|
|
173
|
+
system_schema: project.systemSchema
|
|
174
|
+
});
|
|
175
|
+
await this.executeOnStage(db, stage, processedSql, migrationVersion);
|
|
166
176
|
}
|
|
167
177
|
return [newSchema];
|
|
168
178
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectMigrator.cjs","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tdb.client.schema,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tsystemSchema: string,\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, sql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["SaveMigrationCommand","ImplementationException","SaveSchemaCommand","calculateSchemaChecksum","calculateMigrationChecksum","MigrationVersionHelper","errors","SchemaValidator","wrapIdentifier","QueryError","logger","MigrateErrorCode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectMigrator.cjs","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\ntype MigrationVariables = {\n\tsystem_schema: string\n\tproject_slug: string\n}\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate sqlPreprocessor(text: string, variables: MigrationVariables): string {\n\t\treturn Object.entries(variables).reduce(\n\t\t\t(content, [key, value]) => content.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value),\n\t\t\ttext,\n\t\t)\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tproject: { slug: string; systemSchema: string },\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema: project.systemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tconst processedSql = this.sqlPreprocessor(sql, {\n\t\t\t\tproject_slug: project.slug,\n\t\t\t\tsystem_schema: project.systemSchema,\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, processedSql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["SaveMigrationCommand","ImplementationException","SaveSchemaCommand","calculateSchemaChecksum","calculateMigrationChecksum","MigrationVersionHelper","errors","SchemaValidator","wrapIdentifier","QueryError","logger","MigrateErrorCode"],"mappings":"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"}
|
|
@@ -43,7 +43,7 @@ class ProjectMigrator {
|
|
|
43
43
|
modification,
|
|
44
44
|
formatVersion,
|
|
45
45
|
migration.version,
|
|
46
|
-
|
|
46
|
+
project,
|
|
47
47
|
metadataStore
|
|
48
48
|
);
|
|
49
49
|
}
|
|
@@ -148,7 +148,13 @@ class ProjectMigrator {
|
|
|
148
148
|
}
|
|
149
149
|
return toExecute;
|
|
150
150
|
}
|
|
151
|
-
|
|
151
|
+
sqlPreprocessor(text, variables) {
|
|
152
|
+
return Object.entries(variables).reduce(
|
|
153
|
+
(content, [key, value]) => content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value),
|
|
154
|
+
text
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
async applyModification(db, stages, schema, modification, formatVersion, migrationVersion, project, metadataStore) {
|
|
152
158
|
const {
|
|
153
159
|
getSql,
|
|
154
160
|
schema: newSchema
|
|
@@ -156,11 +162,15 @@ class ProjectMigrator {
|
|
|
156
162
|
for (const stage of stages) {
|
|
157
163
|
const databaseMetadata = await metadataStore.getMetadata(stage.schema);
|
|
158
164
|
const sql = getSql({
|
|
159
|
-
systemSchema,
|
|
165
|
+
systemSchema: project.systemSchema,
|
|
160
166
|
databaseMetadata,
|
|
161
167
|
invalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema)
|
|
162
168
|
});
|
|
163
|
-
|
|
169
|
+
const processedSql = this.sqlPreprocessor(sql, {
|
|
170
|
+
project_slug: project.slug,
|
|
171
|
+
system_schema: project.systemSchema
|
|
172
|
+
});
|
|
173
|
+
await this.executeOnStage(db, stage, processedSql, migrationVersion);
|
|
164
174
|
}
|
|
165
175
|
return [newSchema];
|
|
166
176
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectMigrator.js","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tdb.client.schema,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tsystemSchema: string,\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, sql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["errors"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectMigrator.js","sources":["../../../../../../packages/engine-system-api/src/model/migrations/ProjectMigrator.ts"],"sourcesContent":["import { calculateMigrationChecksum, Migration, MigrationDescriber, MigrationVersionHelper } from '@contember/schema-migrations'\nimport { Client, DatabaseMetadata, QueryError, wrapIdentifier } from '@contember/database'\nimport { Schema } from '@contember/schema'\nimport { SaveMigrationCommand } from '../commands'\nimport { Stage } from '../dtos'\nimport { DatabaseContext } from '../database'\nimport { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'\nimport { MigrateErrorCode } from '../../schema'\nimport { calculateSchemaChecksum, SchemaValidator, SchemaValidatorSkippedErrors } from '@contember/schema-utils'\nimport { logger } from '@contember/logger'\nimport { MigrationsDatabaseMetadataResolverStoreFactory, SchemaDatabaseMetadataResolverStore } from '../metadata'\nimport { ContentMigrationQuery, MigrationInput } from './MigrationInput'\nimport { ContentQueryExecutor } from '../dependencies'\nimport { SchemaProvider } from './SchemaProvider'\nimport { SaveSchemaCommand } from '../commands/schema/SaveSchemaCommand'\nimport { ImplementationException } from '../../utils'\n\ntype MigrationVariables = {\n\tsystem_schema: string\n\tproject_slug: string\n}\n\nexport class ProjectMigrator {\n\tconstructor(\n\t\tprivate readonly migrationDescriber: MigrationDescriber,\n\t\tprivate readonly schemaProvider: SchemaProvider,\n\t\tprivate readonly executedMigrationsResolver: ExecutedMigrationsResolver,\n\t\tprivate readonly migrationsDatabaseMetadataResolverStoreFactory: MigrationsDatabaseMetadataResolverStoreFactory,\n\t\tprivate readonly contentQueryExecutor: ContentQueryExecutor,\n\t) {}\n\n\tpublic async migrate({ db, project, identity, options: { ignoreOrder = false, skipExecuted = false }, migrationsToExecute, stages }: {\n\t\tdb: DatabaseContext\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tstages: Stage[]\n\t\tmigrationsToExecute: readonly MigrationInput[]\n\t\toptions: {\n\t\t\tignoreOrder?: boolean\n\t\t\tskipExecuted?: boolean\n\t\t}\n\t}) {\n\t\tif (migrationsToExecute.length === 0) {\n\t\t\treturn\n\t\t}\n\t\tconst schemaWithMeta = await this.schemaProvider.fetch({ db })\n\t\tlet schema = schemaWithMeta.schema\n\t\tlet id = schemaWithMeta.meta.id\n\n\t\tconst validated = await this.validateMigrations(db, schema, schemaWithMeta.meta.version ?? null, migrationsToExecute, { ignoreOrder, skipExecuted })\n\t\tif (validated.length === 0) {\n\t\t\treturn\n\t\t}\n\n\t\tconst sorted = [...validated].sort((a, b) => a.version.localeCompare(b.version))\n\n\t\tconst metadataStore = this.migrationsDatabaseMetadataResolverStoreFactory.create(db)\n\n\t\tfor (const migration of sorted) {\n\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst formatVersion = migration.formatVersion\n\n\t\t\t\tfor (const modification of migration.modifications) {\n\t\t\t\t\t[schema] = await this.applyModification(\n\t\t\t\t\t\tdb.client,\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tmodification,\n\t\t\t\t\t\tformatVersion,\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tmetadataStore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t} else if (migration.type === 'content') {\n\t\t\t\tif (!id) {\n\t\t\t\t\tthrow new FailedContentMigrationError(migration.version, `Cannot execute content migration without schema`)\n\t\t\t\t}\n\t\t\t\tfor (const query of migration.queries) {\n\t\t\t\t\tawait this.executeContentMigration({\n\t\t\t\t\t\tversion: migration.version,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\tschema,\n\t\t\t\t\t\tschemaMeta: { id },\n\t\t\t\t\t\tstages,\n\t\t\t\t\t\tdatabaseMetadata: await metadataStore.getMetadata(db.client.schema),\n\t\t\t\t\t\tidentity,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// event log uses deferred constraint triggers, we need to fire them before ALTER\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL IMMEDIATE`)\n\t\t\t\tawait db.client.query(`SET CONSTRAINTS ALL DEFERRED`)\n\n\t\t\t}\n\t\t\tid = await db.commandBus.execute(new SaveMigrationCommand(migration))\n\t\t}\n\t\tif (!id) {\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tawait db.commandBus.execute(new SaveSchemaCommand({\n\t\t\tschema,\n\t\t\tmeta: {\n\t\t\t\tid,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\tversion: sorted[sorted.length - 1].version,\n\t\t\t\tchecksum: calculateSchemaChecksum(schema),\n\t\t\t},\n\t\t}))\n\t}\n\n\tprivate async executeContentMigration({ query, stages, version, ...ctx }: {\n\t\tversion: string\n\t\tdb: DatabaseContext\n\t\tquery: ContentMigrationQuery\n\t\tstages: Stage[]\n\t\tschema: Schema\n\t\tschemaMeta: { id: number }\n\t\tproject: { slug: string; systemSchema: string }\n\t\tidentity: { id: string }\n\t\tdatabaseMetadata: DatabaseMetadata\n\t}) {\n\t\tconst queryStages = query.stage ? stages.filter(it => it.slug === query.stage) : stages\n\t\tfor (const stage of queryStages) {\n\t\t\tconst response = await this.contentQueryExecutor.execute({\n\t\t\t\tstage,\n\t\t\t\t...ctx,\n\t\t\t}, query)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new FailedContentMigrationError(version, response.errors.join(', '))\n\t\t\t}\n\t\t\tif (query.checkMutationResult !== false) {\n\t\t\t\tfor (const [key, value] of Object.entries(response.result.data ?? {})) {\n\t\t\t\t\tif (typeof value === 'object' && value !== null && 'ok' in value && !value.ok) {\n\t\t\t\t\t\tconst errorDetails = 'errorMessage' in value ? value.errorMessage : 'No details available. Please fetch errorMessage field.'\n\t\t\t\t\t\tthrow new NotSuccessfulContentMigrationError(version, `Mutation ${key} failed: ${errorDetails}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async validateMigrations(\n\t\tdb: DatabaseContext,\n\t\tschema: Schema,\n\t\tversion: string | null,\n\t\tmigrationsToExecute: readonly MigrationInput[],\n\t\t{ ignoreOrder, skipExecuted }: {\n\t\t\tignoreOrder: boolean\n\t\t\tskipExecuted: boolean\n\t\t},\n\t): Promise<readonly MigrationInput[]> {\n\t\tconst executedMigrations = await this.executedMigrationsResolver.getMigrations(db)\n\t\tconst toExecute: MigrationInput[] = []\n\t\tlet skippedErrors: SchemaValidatorSkippedErrors[] = []\n\t\tfor (const migration of migrationsToExecute) {\n\t\t\tconst executedMigration = executedMigrations.find(it => it.version === migration.version)\n\t\t\tif (executedMigration) {\n\t\t\t\tif (skipExecuted && (migration.type !== 'schema' || executedMigration.checksum === calculateMigrationChecksum(migration))) {\n\t\t\t\t\tcontinue\n\t\t\t\t} else {\n\t\t\t\t\tthrow new AlreadyExecutedMigrationError(migration.version, `Migration is already executed`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (version && migration.version < version && !ignoreOrder) {\n\t\t\t\tthrow new MustFollowLatestMigrationError(migration.version, `Must follow latest executed migration ${version}`)\n\t\t\t}\n\t\t\tif (migration.type === 'schema') {\n\t\t\t\tconst described = this.migrationDescriber.describeModifications(schema, migration)\n\t\t\t\tif (described.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst latestModification = described[described.length - 1]\n\t\t\t\tschema = latestModification.schema\n\t\t\t\tskippedErrors = [\n\t\t\t\t\t...skippedErrors.filter(it => it.skipUntil && MigrationVersionHelper.extractVersion(it.skipUntil) >= migration.version),\n\t\t\t\t\t...migration.skippedErrors ?? [],\n\t\t\t\t]\n\t\t\t\tconst errors = SchemaValidator.validate(schema, skippedErrors)\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tthrow new InvalidSchemaError(\n\t\t\t\t\t\tmigration.version,\n\t\t\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\t\t\terrors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\\n'),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttoExecute.push(migration)\n\t\t}\n\t\t// intentionally not using skippedErrors here\n\t\tconst errors = SchemaValidator.validate(schema)\n\t\tif (errors.length > 0) {\n\t\t\tthrow new InvalidSchemaError(\n\t\t\t\ttoExecute[toExecute.length - 1].version,\n\t\t\t\t'Migration generates invalid schema: \\n' +\n\t\t\t\terrors.map(it => it.path.join('.') + ': ' + it.message).join('\\n'),\n\t\t\t)\n\t\t}\n\t\treturn toExecute\n\t}\n\n\tprivate sqlPreprocessor(text: string, variables: MigrationVariables): string {\n\t\treturn Object.entries(variables).reduce(\n\t\t\t(content, [key, value]) => content.replace(new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g'), value),\n\t\t\ttext,\n\t\t)\n\t}\n\n\tprivate async applyModification(\n\t\tdb: Client,\n\t\tstages: Stage[],\n\t\tschema: Schema,\n\t\tmodification: Migration.Modification,\n\t\tformatVersion: number,\n\t\tmigrationVersion: string,\n\t\tproject: { slug: string; systemSchema: string },\n\t\tmetadataStore: SchemaDatabaseMetadataResolverStore,\n\t): Promise<[Schema]> {\n\t\tconst {\n\t\t\tgetSql,\n\t\t\tschema: newSchema,\n\t\t} = this.migrationDescriber.describeModification(schema, modification, { formatVersion })\n\t\tfor (const stage of stages) {\n\t\t\tconst databaseMetadata = await metadataStore.getMetadata(stage.schema)\n\t\t\tconst sql = getSql({\n\t\t\t\tsystemSchema: project.systemSchema,\n\t\t\t\tdatabaseMetadata,\n\t\t\t\tinvalidateDatabaseMetadata: () => metadataStore.invalidate(stage.schema),\n\t\t\t})\n\t\t\tconst processedSql = this.sqlPreprocessor(sql, {\n\t\t\t\tproject_slug: project.slug,\n\t\t\t\tsystem_schema: project.systemSchema,\n\t\t\t})\n\t\t\tawait this.executeOnStage(db, stage, processedSql, migrationVersion)\n\t\t}\n\t\treturn [newSchema]\n\t}\n\n\tprivate async executeOnStage(db: Client, stage: Stage, sql: string, migrationVersion: string) {\n\t\tawait db.query('SET LOCAL search_path TO ' + wrapIdentifier(stage.schema))\n\t\ttry {\n\t\t\tawait db.query(sql)\n\t\t} catch (e) {\n\t\t\tif (e instanceof QueryError) {\n\t\t\t\tlogger.error(e, { message: 'Migration failed' })\n\t\t\t\tthrow new MigrationFailedError(migrationVersion, e.message)\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\t}\n}\n\nexport abstract class MigrationError extends Error {\n\tpublic abstract code: MigrateErrorCode\n\n\tconstructor(public readonly version: string, public readonly migrationError: string) {\n\t\tsuper(`${version}: ${migrationError}`)\n\t}\n}\n\nexport class MustFollowLatestMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.MustFollowLatest\n}\n\nexport class AlreadyExecutedMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.AlreadyExecuted\n}\n\nexport class MigrationFailedError extends MigrationError {\n\tcode = MigrateErrorCode.MigrationFailed\n}\n\nexport class InvalidSchemaError extends MigrationError {\n\tcode = MigrateErrorCode.InvalidSchema\n}\n\nexport class FailedContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationFailed\n}\n\nexport class NotSuccessfulContentMigrationError extends MigrationError {\n\tcode = MigrateErrorCode.ContentMigrationNotSuccessful\n}\n"],"names":["errors"],"mappings":"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"}
|