{"version":3,"file":"ExportExecutor.cjs","sources":["../../../../../packages/engine-http/src/transfer/ExportExecutor.ts"],"sourcesContent":["import { asyncIterableTransaction, Client, Compiler, Connection, wrapIdentifier } from '@contember/database'\nimport { Command } from './Command'\nimport { TransferMapping, TransferTableMapping } from './TransferMapping'\nimport { Model } from '@contember/schema'\nimport { ContentSchemaTransferMappingFactory } from './ContentSchemaTransferMappingFactory'\nimport { SystemSchemaTransferMappingFactory } from './SystemSchemaTransferMappingFactory'\nimport * as Typesafe from '@contember/typesafe'\nimport { StagesQuery } from '@contember/engine-system-api'\nimport { ProjectContainer } from '../project'\n\nexport type ExportRequest = ReturnType<typeof ExportRequest>\nexport const ExportRequest = Typesafe.object({\n\t// tenant: Typesafe.boolean,\n\tprojects: Typesafe.array(Typesafe.intersection(\n\t\tTypesafe.object({\n\t\t\tslug: Typesafe.string,\n\t\t\tsystem: Typesafe.boolean,\n\t\t}),\n\t\tTypesafe.partial({\n\t\t\ttargetSlug: Typesafe.string,\n\t\t\texcludeTables: Typesafe.array(Typesafe.string),\n\t\t}),\n\t)),\n})\n\nexport class ExportExecutor {\n\tconstructor(\n\t\tprivate readonly contentSchemaTransferMappingFactory: ContentSchemaTransferMappingFactory,\n\t\tprivate readonly systemSchemaTransferMappingFactory: SystemSchemaTransferMappingFactory,\n\t) {\n\t}\n\n\tasync* export(request: ExportRequest, projectContainers: Record<string, ProjectContainer>): AsyncIterable<Command> {\n\t\tfor (const project of request.projects) {\n\t\t\tconst projectContainer = projectContainers[project.slug]\n\t\t\tconst systemContext = projectContainer.systemDatabaseContext\n\n\t\t\tif (project.system) {\n\t\t\t\tconst systemMapping = this.systemSchemaTransferMappingFactory.build({\n\t\t\t\t\texcludeTables: project.excludeTables ?? [],\n\t\t\t\t})\n\t\t\t\tyield ['importSystemSchemaBegin', { project: project.targetSlug ?? project.slug, tables: Object.keys(systemMapping.tables) }]\n\t\t\t\tyield* this.exportSchema(systemContext.client, systemMapping)\n\t\t\t}\n\n\t\t\tfor (const stage of await systemContext.queryHandler.fetch(new StagesQuery())) {\n\t\t\t\tconst contentSchema = await projectContainer.contentSchemaResolver.getSchema({ db: systemContext, stage: stage.slug })\n\t\t\t\tconst contentMapping = this.contentSchemaTransferMappingFactory.createContentSchemaMapping(contentSchema.schema, {\n\t\t\t\t\texcludeTables: project.excludeTables ?? [],\n\t\t\t\t})\n\t\t\t\tconst contentDatabaseClient = projectContainer.connection.createClient(stage.schema, {})\n\n\t\t\t\tyield [\n\t\t\t\t\t'importContentSchemaBegin', {\n\t\t\t\t\t\tproject: project.targetSlug ?? project.slug,\n\t\t\t\t\t\tstage: stage.slug,\n\t\t\t\t\t\tschemaVersion: contentSchema.meta.version ?? '0000-00-00-000000',\n\t\t\t\t\t\ttables: Object.keys(contentMapping.tables),\n\t\t\t\t\t},\n\t\t\t\t]\n\n\t\t\t\tyield* this.exportSchema(contentDatabaseClient, contentMapping)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async* exportSchema(db: Client, mapping: TransferMapping): AsyncIterable<Command> {\n\t\t// eslint-disable-next-line @typescript-eslint/no-this-alias\n\t\tconst that = this\n\t\tyield* asyncIterableTransaction(db, async function* (db) {\n\t\t\tawait db.query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE')\n\t\t\tfor (const table of Object.values(mapping.tables)) {\n\t\t\t\tyield* that.exportSequences(db, table)\n\t\t\t\tyield* that.exportTable(db, table)\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate async* exportSequences(db: Client<Connection.TransactionLike>, table: TransferTableMapping): AsyncIterable<Command> {\n\t\tfor (const column of Object.values(table.columns)) {\n\t\t\tif (column.type === Model.ColumnType.Int && column.sequence) {\n\t\t\t\tconst seqNameResult = await db.query<{ name: string }>(\n\t\t\t\t\t'SELECT pg_get_serial_sequence(?, ?) AS name',\n\t\t\t\t\t[`${db.schema}.${table.name}`, column.name],\n\t\t\t\t)\n\n\t\t\t\tconst seqNameQuoted = seqNameResult.rows[0].name.split('.').map(it => wrapIdentifier(it)).join('.')\n\t\t\t\tconst seqValueResult = await db.query<{ last_value: number|string }>(`SELECT last_value FROM ${seqNameQuoted}`)\n\t\t\t\tconst seqValue = seqValueResult.rows[0].last_value\n\t\t\t\tyield ['importSequence', { table: table.name, column: column.name, value: Number(seqValue) }]\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async* exportTable(db: Client<Connection.TransactionLike>, table: TransferTableMapping): AsyncIterable<Command> {\n\t\tconst DB_FETCH_BATCH_SIZE = 100\n\t\tconst query = (table.createSelect ?? this.buildQuery)(db, table)\n\t\tlet empty = true\n\n\t\tfor await (const row of this.cursorQuery(db, DB_FETCH_BATCH_SIZE, query.sql, query.parameters)) {\n\t\t\tif (empty) {\n\t\t\t\tyield ['insertBegin', { table: table.name, columns: Object.keys(table.columns) }]\n\t\t\t\tempty = false\n\t\t\t}\n\n\t\t\tyield ['insertRow', Object.values(row)]\n\t\t}\n\n\t\tif (!empty) {\n\t\t\tyield ['insertEnd']\n\t\t}\n\t}\n\n\tprivate buildQuery(db: Client<Connection.TransactionLike>, table: TransferTableMapping) {\n\t\tlet builder = db.selectBuilder().from(table.name)\n\n\t\tfor (const column of Object.values(table.columns)) {\n\t\t\tif (column.type === Model.ColumnType.Json || column.type === Model.ColumnType.Date || column.type === Model.ColumnType.DateTime) {\n\t\t\t\tbuilder = builder.select(expr => expr.raw(`${wrapIdentifier(column.name)}::text`))\n\n\t\t\t} else {\n\t\t\t\tbuilder = builder.select(column.name)\n\t\t\t}\n\t\t}\n\n\t\tconst namespaceContext = new Compiler.Context(db.schema, new Set())\n\t\treturn builder.createQuery(namespaceContext)\n\t}\n\n\tprivate async* cursorQuery(db: Client<Connection.TransactionLike>, batchSize: number, sql: string, parameters: readonly any[] = []) {\n\t\tawait db.query(`DECLARE contember_cursor NO SCROLL CURSOR FOR ${sql}`, parameters)\n\n\t\tconst fetchSql = `FETCH ${Number(batchSize)} FROM contember_cursor`\n\t\tlet resultPromise: Promise<Connection.Result> | null = db.query(fetchSql)\n\n\t\twhile (resultPromise !== null) {\n\t\t\tconst result: Connection.Result = await resultPromise\n\t\t\tresultPromise = (result.rowCount ?? 0) < batchSize ? null : db.query(fetchSql) // pipeline\n\t\t\tyield* result.rows\n\t\t}\n\n\t\tawait db.query(`CLOSE contember_cursor`)\n\t}\n}\n"],"names":["Typesafe","StagesQuery","asyncIterableTransaction","db","Model","wrapIdentifier","Compiler"],"mappings":"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"}