@contember/migrations-client 2.2.0-alpha.7 → 2.2.0-alpha.9
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/SchemaVersionBuilder.cjs +20 -2
- package/dist/development/src/SchemaVersionBuilder.cjs.map +1 -1
- package/dist/development/src/SchemaVersionBuilder.js +20 -2
- package/dist/development/src/SchemaVersionBuilder.js.map +1 -1
- package/dist/production/src/SchemaVersionBuilder.cjs +20 -2
- package/dist/production/src/SchemaVersionBuilder.cjs.map +1 -1
- package/dist/production/src/SchemaVersionBuilder.js +20 -2
- package/dist/production/src/SchemaVersionBuilder.js.map +1 -1
- package/dist/types/SchemaVersionBuilder.d.ts +6 -0
- package/dist/types/SchemaVersionBuilder.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/SchemaVersionBuilder.ts +26 -8
- package/tests/cases/unit/SchemaVersionBuilder.test.ts +69 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/migrations-client",
|
|
3
|
-
"version": "2.2.0-alpha.
|
|
3
|
+
"version": "2.2.0-alpha.9",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/production/index.js",
|
|
6
6
|
"typings": "./dist/types/index.d.ts",
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"test": "echo 'No tests'"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@contember/engine-common": "2.2.0-alpha.
|
|
30
|
-
"@contember/graphql-client": "2.2.0-alpha.
|
|
31
|
-
"@contember/schema": "2.2.0-alpha.
|
|
32
|
-
"@contember/schema-migrations": "2.2.0-alpha.
|
|
33
|
-
"@contember/schema-utils": "2.2.0-alpha.
|
|
29
|
+
"@contember/engine-common": "2.2.0-alpha.9",
|
|
30
|
+
"@contember/graphql-client": "2.2.0-alpha.9",
|
|
31
|
+
"@contember/schema": "2.2.0-alpha.9",
|
|
32
|
+
"@contember/schema-migrations": "2.2.0-alpha.9",
|
|
33
|
+
"@contember/schema-utils": "2.2.0-alpha.9"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^24.12.0"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Schema } from '@contember/schema'
|
|
2
2
|
import { emptySchema } from '@contember/schema-utils'
|
|
3
|
-
import { SchemaMigrator } from '@contember/schema-migrations'
|
|
3
|
+
import { Migration, SchemaMigrator } from '@contember/schema-migrations'
|
|
4
4
|
import { MigrationsResolver } from './MigrationsResolver.js'
|
|
5
5
|
import { SchemaStateManager } from './SchemaStateManager.js'
|
|
6
6
|
|
|
7
7
|
export class SchemaVersionBuilder {
|
|
8
|
+
private lastBuildUntil: { migrations: Migration[]; schema: Schema } | null = null
|
|
9
|
+
|
|
8
10
|
constructor(
|
|
9
11
|
private readonly migrationsResolver: MigrationsResolver,
|
|
10
12
|
private readonly schemaMigrator: SchemaMigrator,
|
|
@@ -20,8 +22,21 @@ export class SchemaVersionBuilder {
|
|
|
20
22
|
return schema
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Content migrations call this once each, with ascending versions. Continuing from the previous
|
|
27
|
+
* build keeps a run linear instead of replaying every schema migration from scratch each time.
|
|
28
|
+
*/
|
|
23
29
|
async buildSchemaUntil(targetVersion: string): Promise<Schema> {
|
|
24
|
-
|
|
30
|
+
const migrations = (await this.migrationsResolver.getSchemaMigrations()).filter(({ version }) => version < targetVersion)
|
|
31
|
+
const previous = this.lastBuildUntil
|
|
32
|
+
const canContinue = previous !== null
|
|
33
|
+
&& previous.migrations.length <= migrations.length
|
|
34
|
+
&& previous.migrations.every((migration, index) => migration === migrations[index])
|
|
35
|
+
const start = canContinue ? previous : { migrations: [], schema: emptySchema }
|
|
36
|
+
|
|
37
|
+
const schema = this.applyMigrations(start.schema, migrations.slice(start.migrations.length))
|
|
38
|
+
this.lastBuildUntil = { migrations, schema }
|
|
39
|
+
return schema
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
async continue(schema: Schema, previousVersion: string | null, targetVersion: string): Promise<Schema> {
|
|
@@ -29,11 +44,14 @@ export class SchemaVersionBuilder {
|
|
|
29
44
|
}
|
|
30
45
|
|
|
31
46
|
public async buildSchemaAdvanced(initialSchema: Schema, condition: (version: string) => boolean): Promise<Schema> {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
const migrations = (await this.migrationsResolver.getSchemaMigrations()).filter(({ version }) => condition(version))
|
|
48
|
+
return this.applyMigrations(initialSchema, migrations)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private applyMigrations(initialSchema: Schema, migrations: Migration[]): Schema {
|
|
52
|
+
return migrations.reduce<Schema>(
|
|
53
|
+
(schema, { modifications, formatVersion }) => this.schemaMigrator.applyModifications(schema, modifications, formatVersion),
|
|
54
|
+
initialSchema,
|
|
55
|
+
)
|
|
38
56
|
}
|
|
39
57
|
}
|
|
@@ -2,8 +2,15 @@ import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
|
|
2
2
|
import * as fs from 'node:fs/promises'
|
|
3
3
|
import * as os from 'node:os'
|
|
4
4
|
import * as path from 'node:path'
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
JsonLoader,
|
|
7
|
+
MigrationFilesManager,
|
|
8
|
+
MigrationParser,
|
|
9
|
+
MigrationsResolver,
|
|
10
|
+
SchemaStateManager,
|
|
11
|
+
SchemaVersionBuilder,
|
|
12
|
+
} from '../../../src/index.js'
|
|
13
|
+
import { Migration, ModificationHandlerFactory, SchemaMigrator } from '@contember/schema-migrations'
|
|
7
14
|
import { emptySchema } from '@contember/schema-utils'
|
|
8
15
|
import { Schema } from '@contember/schema'
|
|
9
16
|
|
|
@@ -56,3 +63,63 @@ describe('SchemaVersionBuilder schema state merge', () => {
|
|
|
56
63
|
expect(schema.acl).toStrictEqual(emptySchema.acl)
|
|
57
64
|
})
|
|
58
65
|
})
|
|
66
|
+
|
|
67
|
+
class CountingSchemaMigrator extends SchemaMigrator {
|
|
68
|
+
public appliedMigrations = 0
|
|
69
|
+
|
|
70
|
+
constructor() {
|
|
71
|
+
super(new ModificationHandlerFactory(ModificationHandlerFactory.defaultFactoryMap))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public override applyModifications(schema: Schema, diff: readonly Migration.Modification[], formatVersion: number): Schema {
|
|
75
|
+
this.appliedMigrations++
|
|
76
|
+
return super.applyModifications(schema, diff, formatVersion)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe('SchemaVersionBuilder buildSchemaUntil', () => {
|
|
81
|
+
let migrationsDir: string
|
|
82
|
+
let filesManager: MigrationFilesManager
|
|
83
|
+
let migrator: CountingSchemaMigrator
|
|
84
|
+
let builder: SchemaVersionBuilder
|
|
85
|
+
|
|
86
|
+
const createEnumMigration = (version: string, enumName: string) =>
|
|
87
|
+
filesManager.createFile(
|
|
88
|
+
JSON.stringify({ formatVersion: 6, modifications: [{ modification: 'createEnum', enumName, values: ['a'] }] }),
|
|
89
|
+
`${version}-${enumName}`,
|
|
90
|
+
)
|
|
91
|
+
const enumNames = (schema: Schema) => Object.keys(schema.model.enums)
|
|
92
|
+
|
|
93
|
+
beforeEach(async () => {
|
|
94
|
+
migrationsDir = await fs.mkdtemp(path.join(os.tmpdir(), 'contember-svb-until-'))
|
|
95
|
+
filesManager = new MigrationFilesManager(migrationsDir, { json: new JsonLoader(new MigrationParser()) })
|
|
96
|
+
migrator = new CountingSchemaMigrator()
|
|
97
|
+
builder = new SchemaVersionBuilder(new MigrationsResolver(filesManager), migrator, new SchemaStateManager(path.join(migrationsDir, 'state')))
|
|
98
|
+
await createEnumMigration('2024-01-01-000000', 'first')
|
|
99
|
+
await createEnumMigration('2024-01-02-000000', 'second')
|
|
100
|
+
await createEnumMigration('2024-01-03-000000', 'third')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
afterEach(async () => {
|
|
104
|
+
await fs.rm(migrationsDir, { recursive: true, force: true })
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('continues from the previous build for an ascending target', async () => {
|
|
108
|
+
expect(enumNames(await builder.buildSchemaUntil('2024-01-02-000000'))).toStrictEqual(['first'])
|
|
109
|
+
expect(enumNames(await builder.buildSchemaUntil('2024-01-04-000000'))).toStrictEqual(['first', 'second', 'third'])
|
|
110
|
+
expect(migrator.appliedMigrations).toBe(3)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('rebuilds from scratch for a descending target', async () => {
|
|
114
|
+
await builder.buildSchemaUntil('2024-01-04-000000')
|
|
115
|
+
|
|
116
|
+
expect(enumNames(await builder.buildSchemaUntil('2024-01-02-000000'))).toStrictEqual(['first'])
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('rebuilds from scratch when the migration files change', async () => {
|
|
120
|
+
await builder.buildSchemaUntil('2024-01-03-000000')
|
|
121
|
+
await createEnumMigration('2024-01-01-120000', 'inserted')
|
|
122
|
+
|
|
123
|
+
expect(enumNames(await builder.buildSchemaUntil('2024-01-04-000000'))).toStrictEqual(['first', 'inserted', 'second', 'third'])
|
|
124
|
+
})
|
|
125
|
+
})
|