@contember/schema-migrations 1.2.0-alpha.18 → 1.2.0-alpha.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Migration.d.ts +2 -0
- package/dist/src/Migration.d.ts.map +1 -1
- package/dist/src/Migration.js +2 -2
- package/dist/src/Migration.js.map +1 -1
- package/dist/src/MigrationCreator.d.ts +3 -1
- package/dist/src/MigrationCreator.d.ts.map +1 -1
- package/dist/src/MigrationCreator.js +2 -2
- package/dist/src/MigrationCreator.js.map +1 -1
- package/dist/src/MigrationFilesManager.d.ts.map +1 -1
- package/dist/src/MigrationFilesManager.js +10 -19
- package/dist/src/MigrationFilesManager.js.map +1 -1
- package/dist/src/MigrationsResolver.d.ts.map +1 -1
- package/dist/src/MigrationsResolver.js +2 -0
- package/dist/src/MigrationsResolver.js.map +1 -1
- package/dist/src/SchemaDiffer.d.ts +6 -1
- package/dist/src/SchemaDiffer.d.ts.map +1 -1
- package/dist/src/SchemaDiffer.js +10 -6
- package/dist/src/SchemaDiffer.js.map +1 -1
- package/dist/src/modifications/ModificationHandlerFactory.d.ts.map +1 -1
- package/dist/src/modifications/ModificationHandlerFactory.js +2 -0
- package/dist/src/modifications/ModificationHandlerFactory.js.map +1 -1
- package/dist/src/modifications/settings/UpdateSettingsModification.d.ts +28 -0
- package/dist/src/modifications/settings/UpdateSettingsModification.d.ts.map +1 -0
- package/dist/src/modifications/settings/UpdateSettingsModification.js +64 -0
- package/dist/src/modifications/settings/UpdateSettingsModification.js.map +1 -0
- package/dist/src/modifications/settings/index.d.ts +2 -0
- package/dist/src/modifications/settings/index.d.ts.map +1 -0
- package/dist/src/modifications/settings/index.js +18 -0
- package/dist/src/modifications/settings/index.js.map +1 -0
- package/dist/src/modifications/utils/schemaUpdateUtils.d.ts.map +1 -1
- package/dist/src/modifications/utils/schemaUpdateUtils.js +8 -1
- package/dist/src/modifications/utils/schemaUpdateUtils.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/cases/integration/createView.test.js +80 -0
- package/dist/tests/cases/integration/createView.test.js.map +1 -1
- package/dist/tests/cases/integration/removeField.test.js +72 -0
- package/dist/tests/cases/integration/removeField.test.js.map +1 -1
- package/dist/tests/scripts/checkCreateConsistentMigrations.js +5 -4
- package/dist/tests/scripts/checkCreateConsistentMigrations.js.map +1 -1
- package/dist/tests/src/tests.d.ts.map +1 -1
- package/dist/tests/src/tests.js +15 -5
- package/dist/tests/src/tests.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/Migration.ts +3 -1
- package/src/MigrationCreator.ts +2 -1
- package/src/MigrationFilesManager.ts +10 -20
- package/src/MigrationsResolver.ts +1 -0
- package/src/SchemaDiffer.ts +15 -6
- package/src/modifications/ModificationHandlerFactory.ts +2 -0
- package/src/modifications/settings/UpdateSettingsModification.ts +87 -0
- package/src/modifications/settings/index.ts +1 -0
- package/src/modifications/utils/schemaUpdateUtils.ts +8 -1
- package/src/tsconfig.json +2 -2
- package/tests/cases/integration/createView.test.ts +70 -0
- package/tests/cases/integration/removeField.test.ts +58 -0
- package/tests/scripts/checkCreateConsistentMigrations.ts +3 -2
- package/tests/src/tests.ts +16 -7
|
@@ -1,38 +1,28 @@
|
|
|
1
1
|
import { MigrationVersionHelper } from './MigrationVersionHelper'
|
|
2
|
-
import * as fs from 'fs'
|
|
3
|
-
import
|
|
4
|
-
import * as path from 'path'
|
|
5
|
-
|
|
6
|
-
const readFile = promisify(fs.readFile)
|
|
7
|
-
const fsWrite = promisify(fs.writeFile)
|
|
8
|
-
const fsRemove = promisify(fs.unlink)
|
|
9
|
-
const fsRealpath = promisify(fs.realpath)
|
|
10
|
-
const mkdir = promisify(fs.mkdir)
|
|
11
|
-
const lstatFile = promisify(fs.lstat)
|
|
12
|
-
const readDir = promisify(fs.readdir)
|
|
13
|
-
const mvFile = promisify(fs.rename)
|
|
2
|
+
import * as fs from 'node:fs/promises'
|
|
3
|
+
import * as path from 'node:path'
|
|
14
4
|
|
|
15
5
|
class MigrationFilesManager {
|
|
16
6
|
constructor(public readonly directory: string) {}
|
|
17
7
|
|
|
18
8
|
public async createFile(content: string, name: string): Promise<string> {
|
|
19
9
|
const path = this.formatPath(name)
|
|
20
|
-
await
|
|
21
|
-
return await
|
|
10
|
+
await fs.writeFile(path, content, { encoding: 'utf8' })
|
|
11
|
+
return await fs.realpath(path)
|
|
22
12
|
}
|
|
23
13
|
|
|
24
14
|
public async removeFile(name: string) {
|
|
25
15
|
const path = this.formatPath(name)
|
|
26
|
-
await
|
|
16
|
+
await fs.unlink(path)
|
|
27
17
|
}
|
|
28
18
|
|
|
29
19
|
public async moveFile(oldName: string, newName: string) {
|
|
30
|
-
await
|
|
20
|
+
await fs.rename(this.formatPath(oldName), this.formatPath(newName))
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
public async createDirIfNotExist(): Promise<void> {
|
|
34
24
|
try {
|
|
35
|
-
await mkdir(this.directory)
|
|
25
|
+
await fs.mkdir(this.directory)
|
|
36
26
|
} catch (e) {
|
|
37
27
|
if (!(e instanceof Error) || !('code' in e) || (e as any).code !== 'EEXIST') {
|
|
38
28
|
throw e
|
|
@@ -47,7 +37,7 @@ class MigrationFilesManager {
|
|
|
47
37
|
files
|
|
48
38
|
.filter(file => file.endsWith(`.json`))
|
|
49
39
|
.filter(async file => {
|
|
50
|
-
return (await
|
|
40
|
+
return (await fs.lstat(`${this.directory}/${file}`)).isFile()
|
|
51
41
|
}),
|
|
52
42
|
)
|
|
53
43
|
return filteredFiles.sort()
|
|
@@ -55,7 +45,7 @@ class MigrationFilesManager {
|
|
|
55
45
|
|
|
56
46
|
private async tryReadDir(): Promise<string[]> {
|
|
57
47
|
try {
|
|
58
|
-
return await
|
|
48
|
+
return await fs.readdir(this.directory)
|
|
59
49
|
} catch (e) {
|
|
60
50
|
if (e instanceof Error && 'code' in e && (e as any).code === 'ENOENT') {
|
|
61
51
|
return []
|
|
@@ -72,7 +62,7 @@ class MigrationFilesManager {
|
|
|
72
62
|
const filesWithContent = files.map(async filename => ({
|
|
73
63
|
filename: filename,
|
|
74
64
|
path: `${this.directory}/${filename}`,
|
|
75
|
-
content: await readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
|
|
65
|
+
content: await fs.readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
|
|
76
66
|
}))
|
|
77
67
|
|
|
78
68
|
return await Promise.all(filesWithContent)
|
package/src/SchemaDiffer.ts
CHANGED
|
@@ -44,14 +44,22 @@ import {
|
|
|
44
44
|
import { ChangeViewNonViewDiffer, RemoveChangedFieldDiffer, RemoveChangedViewDiffer } from './modifications/differs'
|
|
45
45
|
import { CreateIndexDiffer, RemoveIndexDiffer } from './modifications/indexes'
|
|
46
46
|
import { SchemaWithMeta } from './modifications/utils/schemaMeta'
|
|
47
|
+
import { UpdateSettingsDiffer } from './modifications/settings'
|
|
48
|
+
|
|
49
|
+
type DiffOptions = { skipRecreateValidation?: boolean; skipInitialSchemaValidation?: boolean }
|
|
47
50
|
|
|
48
51
|
export class SchemaDiffer {
|
|
49
52
|
constructor(private readonly schemaMigrator: SchemaMigrator) {}
|
|
50
53
|
|
|
51
|
-
diffSchemas(originalSchema: Schema, updatedSchema: Schema,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
diffSchemas(originalSchema: Schema, updatedSchema: Schema, {
|
|
55
|
+
skipInitialSchemaValidation = false,
|
|
56
|
+
skipRecreateValidation = false,
|
|
57
|
+
}: DiffOptions = {}): Migration.Modification[] {
|
|
58
|
+
if (!skipInitialSchemaValidation) {
|
|
59
|
+
const originalErrors = SchemaValidator.validate(originalSchema)
|
|
60
|
+
if (originalErrors.length > 0) {
|
|
61
|
+
throw new InvalidSchemaException('original schema is not valid', originalErrors)
|
|
62
|
+
}
|
|
55
63
|
}
|
|
56
64
|
const updatedErrors = SchemaValidator.validate(updatedSchema)
|
|
57
65
|
if (updatedErrors.length > 0) {
|
|
@@ -59,6 +67,7 @@ export class SchemaDiffer {
|
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
const differs: Differ[] = [
|
|
70
|
+
new UpdateSettingsDiffer(),
|
|
62
71
|
new ConvertOneToManyRelationDiffer(),
|
|
63
72
|
new ConvertOneHasManyToManyHasManyRelationDiffer(),
|
|
64
73
|
new RemoveUniqueConstraintDiffer(),
|
|
@@ -84,8 +93,8 @@ export class SchemaDiffer {
|
|
|
84
93
|
new RemoveChangedFieldDiffer(it => isRelation(it) && isInverseRelation(it)),
|
|
85
94
|
new CreateEntityDiffer(),
|
|
86
95
|
new CreateColumnDiffer(),
|
|
87
|
-
new CreateViewDiffer(),
|
|
88
96
|
new CreateRelationDiffer(),
|
|
97
|
+
new CreateViewDiffer(),
|
|
89
98
|
new CreateRelationInverseSideDiffer(),
|
|
90
99
|
new CreateUniqueConstraintDiffer(),
|
|
91
100
|
new CreateIndexDiffer(),
|
|
@@ -103,7 +112,7 @@ export class SchemaDiffer {
|
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
|
|
106
|
-
if (
|
|
115
|
+
if (!skipRecreateValidation) {
|
|
107
116
|
const { meta, ...appliedDiffsSchema2 } = appliedDiffsSchema as SchemaWithMeta
|
|
108
117
|
const errors = deepCompare(updatedSchema, appliedDiffsSchema2, [])
|
|
109
118
|
if (errors.length === 0) {
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { patchValidationSchemaModification, updateValidationSchemaModification } from './validation'
|
|
31
31
|
import { createIndexModification, removeIndexModification } from './indexes'
|
|
32
32
|
import { SchemaWithMeta } from './utils/schemaMeta'
|
|
33
|
+
import { updateSettingsModification } from './settings'
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
class ModificationHandlerFactory {
|
|
@@ -47,6 +48,7 @@ namespace ModificationHandlerFactory {
|
|
|
47
48
|
type HandlerMap<D> = { [modificationName: string]: ModificationType<string, D> }
|
|
48
49
|
|
|
49
50
|
const handlers = [
|
|
51
|
+
updateSettingsModification,
|
|
50
52
|
updateAclSchemaModification,
|
|
51
53
|
patchAclSchemaModification,
|
|
52
54
|
createColumnModification,
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { MigrationBuilder } from '@contember/database-migrations'
|
|
2
|
+
import { Schema, Settings } from '@contember/schema'
|
|
3
|
+
import { SchemaUpdater } from '../utils/schemaUpdateUtils'
|
|
4
|
+
import { createModificationType, Differ, ModificationHandler } from '../ModificationHandler'
|
|
5
|
+
import deepEqual from 'fast-deep-equal'
|
|
6
|
+
|
|
7
|
+
export class UpdateSettingsModificationHandler implements ModificationHandler<UpdateSettingsModificationData> {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly data: UpdateSettingsModificationData,
|
|
10
|
+
) {
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public createSql(builder: MigrationBuilder): void {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public getSchemaUpdater(): SchemaUpdater {
|
|
17
|
+
return ({ schema }) => {
|
|
18
|
+
const { [this.data.key]: _, ...settings } = schema.settings
|
|
19
|
+
if (this.data.op === 'unset') {
|
|
20
|
+
return {
|
|
21
|
+
...schema,
|
|
22
|
+
settings,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
...schema,
|
|
27
|
+
settings: { ...settings, [this.data.key]: this.data.value },
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
describe() {
|
|
34
|
+
return { message: `Change settings of ${this.data.key}` }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const updateSettingsModification = createModificationType({
|
|
40
|
+
id: 'updateSettings',
|
|
41
|
+
handler: UpdateSettingsModificationHandler,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
export class UpdateSettingsDiffer implements Differ {
|
|
45
|
+
createDiff(originalSchema: Schema, updatedSchema: Schema) {
|
|
46
|
+
const allKeys = Array.from(new Set([
|
|
47
|
+
...Object.keys(originalSchema.settings),
|
|
48
|
+
...Object.keys(updatedSchema.settings),
|
|
49
|
+
] as (keyof Settings.Schema)[]))
|
|
50
|
+
|
|
51
|
+
return allKeys
|
|
52
|
+
.filter(key => !deepEqual(originalSchema.settings[key], updatedSchema.settings[key]))
|
|
53
|
+
.map(key => {
|
|
54
|
+
const value = updatedSchema.settings[key]
|
|
55
|
+
if (value === undefined) {
|
|
56
|
+
return updateSettingsModification.createModification({
|
|
57
|
+
op: 'unset',
|
|
58
|
+
key,
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
return updateSettingsModification.createModification({
|
|
62
|
+
op: 'set',
|
|
63
|
+
key,
|
|
64
|
+
value,
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
export interface SetSettingsModificationData<K extends keyof Settings.Schema = keyof Settings.Schema> {
|
|
73
|
+
op: 'set'
|
|
74
|
+
key: K
|
|
75
|
+
value: Settings.Schema[K]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
export interface UnsetSettingsModificationData<K extends keyof Settings.Schema = keyof Settings.Schema> {
|
|
80
|
+
op: 'unset'
|
|
81
|
+
key: K
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
export type UpdateSettingsModificationData =
|
|
86
|
+
| SetSettingsModificationData
|
|
87
|
+
| UnsetSettingsModificationData
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './UpdateSettingsModification'
|
|
@@ -271,7 +271,14 @@ export const removeField = (entityName: string, fieldName: string, version: numb
|
|
|
271
271
|
updateModel(
|
|
272
272
|
updateEntity(entity.name, ({ entity }) => {
|
|
273
273
|
const { [field.name]: removed, ...fields } = entity.fields
|
|
274
|
-
|
|
274
|
+
const indexes = Object.entries(entity.indexes).filter(([, index]) => !index.fields.includes(field.name))
|
|
275
|
+
const unique = Object.entries(entity.unique).filter(([, index]) => !index.fields.includes(field.name))
|
|
276
|
+
return {
|
|
277
|
+
...entity,
|
|
278
|
+
fields,
|
|
279
|
+
indexes: Object.fromEntries(indexes),
|
|
280
|
+
unique: Object.fromEntries(unique),
|
|
281
|
+
}
|
|
275
282
|
}),
|
|
276
283
|
isRelation(field) && isInverseRelation(field)
|
|
277
284
|
? updateEntity(
|
package/src/tsconfig.json
CHANGED
|
@@ -86,3 +86,73 @@ testMigrations('create view', {
|
|
|
86
86
|
sql: SQL`CREATE VIEW "author_stats" AS SELECT 1;`,
|
|
87
87
|
})
|
|
88
88
|
|
|
89
|
+
|
|
90
|
+
namespace ViewAddRelationOriginalSchema {
|
|
91
|
+
export class Article {
|
|
92
|
+
title = def.stringColumn()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
export class Category {
|
|
97
|
+
name = def.stringColumn()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
namespace ViewAddRelationUpdateSchema {
|
|
102
|
+
export class Article {
|
|
103
|
+
title = def.stringColumn()
|
|
104
|
+
category = def.manyHasOne(Category)
|
|
105
|
+
stats = def.oneHasOneInverse(ArticleStats, 'article')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class Category {
|
|
109
|
+
name = def.stringColumn()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@def.View('SELECT 1')
|
|
113
|
+
export class ArticleStats {
|
|
114
|
+
article = def.oneHasOne(Article, 'stats')
|
|
115
|
+
visitCount = def.intColumn()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
testMigrations('create a relation and a view', {
|
|
121
|
+
originalSchema: def.createModel(ViewAddRelationOriginalSchema), updatedSchema: def.createModel(ViewAddRelationUpdateSchema), diff: [
|
|
122
|
+
{
|
|
123
|
+
modification: 'createRelation',
|
|
124
|
+
entityName: 'Article',
|
|
125
|
+
owningSide: { name: 'category', nullable: true, type: 'ManyHasOne', target: 'Category', joiningColumn: { columnName: 'category_id', onDelete: 'restrict' } },
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
modification: 'createView',
|
|
129
|
+
entity: { name: 'ArticleStats',
|
|
130
|
+
primary: 'id',
|
|
131
|
+
primaryColumn: 'id',
|
|
132
|
+
unique: {},
|
|
133
|
+
indexes: {},
|
|
134
|
+
fields: { id: { name: 'id', columnName: 'id', nullable: false, type: 'Uuid', columnType: 'uuid' },
|
|
135
|
+
article: { name: 'article', inversedBy: 'stats', nullable: true, type: 'OneHasOne', target: 'Article', joiningColumn: { columnName: 'article_id', onDelete: 'restrict' } },
|
|
136
|
+
visitCount: { name: 'visitCount', columnName: 'visit_count', nullable: true, type: 'Integer', columnType: 'integer' } },
|
|
137
|
+
tableName: 'article_stats',
|
|
138
|
+
eventLog: { enabled: true },
|
|
139
|
+
view: { sql: 'SELECT 1' } },
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
modification: 'createRelationInverseSide',
|
|
143
|
+
entityName: 'Article',
|
|
144
|
+
relation: {
|
|
145
|
+
name: 'stats',
|
|
146
|
+
ownedBy: 'article',
|
|
147
|
+
target: 'ArticleStats',
|
|
148
|
+
type: 'OneHasOne',
|
|
149
|
+
nullable: true,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
sql: SQL`ALTER TABLE "article" ADD "category_id" uuid;
|
|
154
|
+
ALTER TABLE "article" ADD CONSTRAINT "fk_article_category_id_703b8b" FOREIGN KEY ("category_id") REFERENCES "category"("id") ON DELETE NO ACTION DEFERRABLE INITIALLY IMMEDIATE;
|
|
155
|
+
CREATE INDEX "article_category_id_index" ON "article" ("category_id");
|
|
156
|
+
CREATE VIEW "article_stats" AS SELECT 1;`,
|
|
157
|
+
})
|
|
158
|
+
|
|
@@ -2,6 +2,8 @@ import { testMigrations } from '../../src/tests'
|
|
|
2
2
|
import { SchemaBuilder } from '@contember/schema-definition'
|
|
3
3
|
import { Model } from '@contember/schema'
|
|
4
4
|
import { SQL } from '../../src/tags'
|
|
5
|
+
import { SchemaDefinition as def } from '@contember/schema-definition'
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
testMigrations('remove relation (many has one)', {
|
|
7
9
|
originalSchema: new SchemaBuilder()
|
|
@@ -128,3 +130,59 @@ testMigrations('remove relation inverse side', {
|
|
|
128
130
|
],
|
|
129
131
|
sql: SQL``,
|
|
130
132
|
})
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
namespace DropIndexOrigSchema {
|
|
136
|
+
|
|
137
|
+
@def.Unique('title', 'author')
|
|
138
|
+
@def.Index('title', 'author')
|
|
139
|
+
export class Article {
|
|
140
|
+
title = def.stringColumn()
|
|
141
|
+
author = def.manyHasOne(Author)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export class Author {
|
|
145
|
+
name = def.stringColumn()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
namespace DropIndexUpSchema {
|
|
150
|
+
@def.Unique('title', 'author')
|
|
151
|
+
@def.Index('title', 'author')
|
|
152
|
+
export class Article {
|
|
153
|
+
title = def.stringColumn()
|
|
154
|
+
author = def.stringColumn()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class Author {
|
|
158
|
+
name = def.stringColumn()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
testMigrations('test drop index / unique when removing a field', {
|
|
164
|
+
originalSchema: def.createModel(DropIndexOrigSchema),
|
|
165
|
+
updatedSchema: def.createModel(DropIndexUpSchema),
|
|
166
|
+
diff: [{
|
|
167
|
+
modification: 'removeField',
|
|
168
|
+
entityName: 'Article',
|
|
169
|
+
fieldName: 'author',
|
|
170
|
+
}, {
|
|
171
|
+
modification: 'createColumn',
|
|
172
|
+
entityName: 'Article',
|
|
173
|
+
field: { name: 'author', columnName: 'author', nullable: true, type: 'String', columnType: 'text' },
|
|
174
|
+
}, {
|
|
175
|
+
modification: 'createUniqueConstraint',
|
|
176
|
+
entityName: 'Article',
|
|
177
|
+
unique: { name: 'unique_Article_title_author_7157ea', fields: ['title', 'author'] },
|
|
178
|
+
}, {
|
|
179
|
+
modification: 'createIndex',
|
|
180
|
+
entityName: 'Article',
|
|
181
|
+
index: { name: 'idx_Article_title_author_7157ea', fields: ['title', 'author'] },
|
|
182
|
+
}],
|
|
183
|
+
sql: SQL`ALTER TABLE "article" DROP "author_id";
|
|
184
|
+
ALTER TABLE "article" ADD "author" text;
|
|
185
|
+
ALTER TABLE "article" ADD CONSTRAINT "unique_Article_title_author_7157ea" UNIQUE ("title", "author");
|
|
186
|
+
CREATE INDEX "idx_Article_title_author_7157ea" ON "article" ("title", "author");`,
|
|
187
|
+
})
|
|
188
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { relative } from 'path'
|
|
1
|
+
import { relative } from 'node:path'
|
|
2
2
|
import {
|
|
3
3
|
MigrationFilesManager,
|
|
4
4
|
MigrationsResolver,
|
|
@@ -18,7 +18,8 @@ import { emptySchema, schemaType } from '@contember/schema-utils'
|
|
|
18
18
|
for (const migration of await migrationsResolver.getMigrations()) {
|
|
19
19
|
const nextSchema = migrator.applyModifications(schema, migration.modifications, migration.formatVersion)
|
|
20
20
|
schemaType(nextSchema)
|
|
21
|
-
|
|
21
|
+
const { meta, ...nextSchemaWithoutMeta } = nextSchema
|
|
22
|
+
differ.diffSchemas(schema, nextSchemaWithoutMeta)
|
|
22
23
|
|
|
23
24
|
schema = nextSchema
|
|
24
25
|
}
|
package/tests/src/tests.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Acl, Model } from '@contember/schema'
|
|
|
3
3
|
import { createMigrationBuilder } from '@contember/database-migrations'
|
|
4
4
|
import { assert, describe, it } from 'vitest'
|
|
5
5
|
import { SchemaWithMeta } from '../../src/modifications/utils/schemaMeta'
|
|
6
|
+
import { emptySchema } from '@contember/schema-utils'
|
|
6
7
|
|
|
7
8
|
const modificationFactory = new ModificationHandlerFactory(ModificationHandlerFactory.defaultFactoryMap)
|
|
8
9
|
const schemaMigrator = new SchemaMigrator(modificationFactory)
|
|
@@ -28,17 +29,24 @@ export function testDiffSchemas(
|
|
|
28
29
|
updatedAcl: Acl.Schema = emptyAcl,
|
|
29
30
|
) {
|
|
30
31
|
const actualDiff = schemaDiffer.diffSchemas(
|
|
31
|
-
{ model: originalModel, acl: originalAcl, validation: {} },
|
|
32
|
-
{ model: updatedModel, acl: updatedAcl, validation: {} },
|
|
33
|
-
|
|
32
|
+
{ ...emptySchema, model: originalModel, acl: originalAcl, validation: {} },
|
|
33
|
+
{ ...emptySchema, model: updatedModel, acl: updatedAcl, validation: {} },
|
|
34
|
+
{ skipRecreateValidation: true },
|
|
34
35
|
)
|
|
35
|
-
|
|
36
|
+
try {
|
|
37
|
+
assert.deepStrictEqual(actualDiff, expectedDiff)
|
|
38
|
+
} catch (e) {
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
40
|
+
console.log(JSON.stringify(actualDiff))
|
|
41
|
+
throw e
|
|
42
|
+
}
|
|
36
43
|
const { meta, ...schema } = schemaMigrator.applyModifications(
|
|
37
|
-
{ model: originalModel, acl: originalAcl, validation: {} },
|
|
44
|
+
{ ...emptySchema, model: originalModel, acl: originalAcl, validation: {} },
|
|
38
45
|
actualDiff,
|
|
39
46
|
VERSION_LATEST,
|
|
40
47
|
) as SchemaWithMeta
|
|
41
48
|
assert.deepStrictEqual(schema, {
|
|
49
|
+
...emptySchema,
|
|
42
50
|
model: updatedModel,
|
|
43
51
|
acl: updatedAcl,
|
|
44
52
|
validation: {},
|
|
@@ -53,12 +61,13 @@ export function testApplyDiff(
|
|
|
53
61
|
expectedAcl: Acl.Schema = emptyAcl,
|
|
54
62
|
) {
|
|
55
63
|
const { meta, ...actualSchema } = schemaMigrator.applyModifications(
|
|
56
|
-
{ model: originalModel, acl: originalAcl, validation: {} },
|
|
64
|
+
{ ...emptySchema, model: originalModel, acl: originalAcl, validation: {} },
|
|
57
65
|
diff,
|
|
58
66
|
VERSION_LATEST,
|
|
59
67
|
) as SchemaWithMeta
|
|
60
68
|
|
|
61
69
|
assert.deepStrictEqual(actualSchema, {
|
|
70
|
+
...emptySchema,
|
|
62
71
|
model: expectedModel,
|
|
63
72
|
acl: expectedAcl,
|
|
64
73
|
validation: {},
|
|
@@ -66,7 +75,7 @@ export function testApplyDiff(
|
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
export function testGenerateSql(originalSchema: Model.Schema, diff: Migration.Modification[], expectedSql: string) {
|
|
69
|
-
let schema = { model: originalSchema, acl: emptyAcl, validation: {} }
|
|
78
|
+
let schema = { ...emptySchema, model: originalSchema, acl: emptyAcl, validation: {} }
|
|
70
79
|
const builder = createMigrationBuilder()
|
|
71
80
|
for (let { modification, ...data } of diff) {
|
|
72
81
|
const modificationHandler = modificationFactory.create(modification, data, schema, { formatVersion: VERSION_LATEST, systemSchema: 'system' })
|