@balena/pinejs 14.44.0-linear-runtime-migrator-e15bea04e85fb013aeed7d4770052b51747a619c → 15.0.0-delete-state-default-user-permissions-ba0732a0c5d0da9d1d5be818cb08cc898e86ebe3
Sign up to get free protection for your applications and to get access to all the features.
- package/.versionbot/CHANGELOG.yml +24 -45
- package/CHANGELOG.md +8 -6
- package/VERSION +1 -1
- package/docs/Migrations.md +1 -101
- package/out/config-loader/config-loader.d.ts +4 -2
- package/out/config-loader/config-loader.js +20 -35
- package/out/config-loader/config-loader.js.map +1 -1
- package/out/config-loader/env.d.ts +0 -3
- package/out/config-loader/env.js +0 -3
- package/out/config-loader/env.js.map +1 -1
- package/out/migrator/migrations.sbvr +0 -66
- package/out/migrator/migrator.d.ts +17 -0
- package/out/migrator/migrator.js +185 -0
- package/out/migrator/migrator.js.map +1 -0
- package/out/sbvr-api/sbvr-utils.d.ts +1 -3
- package/out/sbvr-api/sbvr-utils.js +4 -13
- package/out/sbvr-api/sbvr-utils.js.map +1 -1
- package/out/server-glue/module.d.ts +2 -2
- package/out/server-glue/module.js +1 -2
- package/out/server-glue/module.js.map +1 -1
- package/package.json +3 -3
- package/src/config-loader/config-loader.ts +26 -73
- package/src/config-loader/env.ts +0 -3
- package/src/migrator/migrations.sbvr +0 -66
- package/src/migrator/migrator.ts +278 -0
- package/src/sbvr-api/sbvr-utils.ts +3 -18
- package/src/server-glue/module.ts +2 -3
- package/out/migrator/async.d.ts +0 -6
- package/out/migrator/async.js +0 -160
- package/out/migrator/async.js.map +0 -1
- package/out/migrator/sync.d.ts +0 -9
- package/out/migrator/sync.js +0 -126
- package/out/migrator/sync.js.map +0 -1
- package/out/migrator/utils.d.ts +0 -56
- package/out/migrator/utils.js +0 -187
- package/out/migrator/utils.js.map +0 -1
- package/src/migrator/async.ts +0 -279
- package/src/migrator/sync.ts +0 -177
- package/src/migrator/utils.ts +0 -296
@@ -1,16 +1,9 @@
|
|
1
1
|
import type * as Express from 'express';
|
2
2
|
import type { AbstractSqlModel } from '@balena/abstract-sql-compiler';
|
3
3
|
import type { Database } from '../database-layer/db';
|
4
|
+
import type { Migration } from '../migrator/migrator';
|
4
5
|
import type { AnyObject, Resolvable } from '../sbvr-api/common-types';
|
5
6
|
|
6
|
-
import {
|
7
|
-
Migration,
|
8
|
-
Migrations,
|
9
|
-
defaultMigrationCategory,
|
10
|
-
MigrationCategories,
|
11
|
-
MigrationCategory,
|
12
|
-
} from '../migrator/utils';
|
13
|
-
|
14
7
|
import * as fs from 'fs';
|
15
8
|
import * as _ from 'lodash';
|
16
9
|
import * as path from 'path';
|
@@ -32,7 +25,9 @@ export interface Model {
|
|
32
25
|
modelText?: string;
|
33
26
|
abstractSql?: AbstractSqlModel;
|
34
27
|
migrationsPath?: string;
|
35
|
-
migrations?:
|
28
|
+
migrations?: {
|
29
|
+
[index: string]: Migration;
|
30
|
+
};
|
36
31
|
initSqlPath?: string;
|
37
32
|
initSql?: string;
|
38
33
|
customServerCode?:
|
@@ -133,15 +128,31 @@ export const setup = (app: Express.Application) => {
|
|
133
128
|
},
|
134
129
|
);
|
135
130
|
if (user.permissions != null) {
|
136
|
-
await Promise.all(
|
131
|
+
const permissionIds = await Promise.all(
|
137
132
|
user.permissions.map(async (permissionName) => {
|
138
133
|
const permissionID = await permissionsCache[permissionName];
|
139
134
|
await getOrCreate(authApiTx, 'user__has__permission', {
|
140
135
|
user: userID,
|
141
136
|
permission: permissionID,
|
142
137
|
});
|
138
|
+
return permissionID;
|
143
139
|
}),
|
144
140
|
);
|
141
|
+
if (permissionIds.length > 0) {
|
142
|
+
await authApiTx.delete({
|
143
|
+
resource: 'user__has__permission',
|
144
|
+
options: {
|
145
|
+
$filter: {
|
146
|
+
user: userID,
|
147
|
+
$not: {
|
148
|
+
permission: {
|
149
|
+
$in: permissionIds,
|
150
|
+
},
|
151
|
+
},
|
152
|
+
},
|
153
|
+
},
|
154
|
+
});
|
155
|
+
}
|
145
156
|
}
|
146
157
|
} catch (e) {
|
147
158
|
e.message = `Could not create or find user "${user.username}": ${e.message}`;
|
@@ -256,62 +267,18 @@ export const setup = (app: Express.Application) => {
|
|
256
267
|
await Promise.all(
|
257
268
|
fileNames.map(async (filename) => {
|
258
269
|
const filePath = path.join(migrationsPath, filename);
|
259
|
-
const fileNameParts = filename.split('.', 3);
|
260
|
-
const fileExtension = path.extname(filename);
|
261
270
|
const [migrationKey] = filename.split('-', 1);
|
262
|
-
let migrationCategory: MigrationCategory =
|
263
|
-
defaultMigrationCategory;
|
264
|
-
|
265
|
-
if (fileNameParts.length === 3) {
|
266
|
-
if (fileNameParts[1] in MigrationCategories) {
|
267
|
-
migrationCategory = fileNameParts[1] as MigrationCategory;
|
268
|
-
} else {
|
269
|
-
console.error(
|
270
|
-
`Unrecognised migration file category ${
|
271
|
-
fileNameParts[1]
|
272
|
-
}, skipping: ${path.extname(filename)}`,
|
273
|
-
);
|
274
|
-
return;
|
275
|
-
}
|
276
|
-
}
|
277
271
|
|
278
|
-
|
279
|
-
* helper to assign migrations with category level to model
|
280
|
-
* example migration file names:
|
281
|
-
*
|
282
|
-
* key0-name.ts ==> defaults startup migration
|
283
|
-
* key1-name1.sql ==> defaults startup migration
|
284
|
-
* key2-name2.sync.sql ==> explicit synchrony migration
|
285
|
-
* key3-name3.async.ts ==> async migration (async datafiller)
|
286
|
-
* key4-name4.async.sql ==> async migration (async datafiller)
|
287
|
-
*
|
288
|
-
*/
|
289
|
-
const assignMigrationWithCategory = (
|
290
|
-
newMigrationKey: string,
|
291
|
-
newMigration: Migration,
|
292
|
-
) => {
|
293
|
-
const catMigrations = migrations[migrationCategory] || {};
|
294
|
-
if (typeof catMigrations === 'object') {
|
295
|
-
migrations[migrationCategory] = {
|
296
|
-
[newMigrationKey]: newMigration,
|
297
|
-
...catMigrations,
|
298
|
-
};
|
299
|
-
}
|
300
|
-
};
|
301
|
-
|
302
|
-
switch (fileExtension) {
|
272
|
+
switch (path.extname(filename)) {
|
303
273
|
case '.coffee':
|
304
274
|
case '.ts':
|
305
275
|
case '.js':
|
306
|
-
|
307
|
-
migrationKey,
|
308
|
-
nodeRequire(filePath),
|
309
|
-
);
|
276
|
+
migrations[migrationKey] = nodeRequire(filePath);
|
310
277
|
break;
|
311
278
|
case '.sql':
|
312
|
-
|
313
|
-
|
314
|
-
|
279
|
+
migrations[migrationKey] = await fs.promises.readFile(
|
280
|
+
filePath,
|
281
|
+
'utf8',
|
315
282
|
);
|
316
283
|
break;
|
317
284
|
default:
|
@@ -331,26 +298,12 @@ export const setup = (app: Express.Application) => {
|
|
331
298
|
}),
|
332
299
|
);
|
333
300
|
await loadConfig(configObj);
|
334
|
-
runAsyncMigrations(configObj); // async migrations will run in background - nothing to wait for
|
335
301
|
} catch (err) {
|
336
302
|
console.error('Error loading application config', err, err.stack);
|
337
303
|
process.exit(1);
|
338
304
|
}
|
339
305
|
};
|
340
306
|
|
341
|
-
const runAsyncMigrations = (data: Config): void => {
|
342
|
-
for (const model of data.models) {
|
343
|
-
if (
|
344
|
-
model.migrations != null &&
|
345
|
-
Object.keys(model.migrations).includes(MigrationCategories.async)
|
346
|
-
) {
|
347
|
-
sbvrUtils.executeAsyncModelMigration(
|
348
|
-
model as sbvrUtils.ExecutableModel,
|
349
|
-
);
|
350
|
-
}
|
351
|
-
}
|
352
|
-
};
|
353
|
-
|
354
307
|
return {
|
355
308
|
loadConfig,
|
356
309
|
loadApplicationConfig,
|
package/src/config-loader/env.ts
CHANGED
@@ -108,7 +108,4 @@ export const migrator = {
|
|
108
108
|
lockTimeout: 5 * 60 * 1000,
|
109
109
|
// Used to delay the failure on lock taking, to avoid spam taking
|
110
110
|
lockFailDelay: 20 * 1000,
|
111
|
-
asyncMigrationDefaultDelayMS: 1000,
|
112
|
-
asyncMigrationDefaultBackoffDelayMS: 60000,
|
113
|
-
asyncMigrationDefaultErrorThreshold: 10,
|
114
111
|
};
|
@@ -7,7 +7,6 @@ Term: executed migrations
|
|
7
7
|
Term: lock time
|
8
8
|
Concept Type: Date Time (Type)
|
9
9
|
|
10
|
-
|
11
10
|
Term: migration
|
12
11
|
Reference Scheme: model name
|
13
12
|
Database ID Field: model name
|
@@ -22,68 +21,3 @@ Term: migration lock
|
|
22
21
|
|
23
22
|
Fact Type: migration lock has model name
|
24
23
|
Necessity: each migration lock has exactly one model name
|
25
|
-
|
26
|
-
|
27
|
-
Term: migration key
|
28
|
-
Concept Type: Short Text (Type)
|
29
|
-
Term: start time
|
30
|
-
Concept Type: Date Time (Type)
|
31
|
-
Term: last run time
|
32
|
-
Concept Type: Date Time (Type)
|
33
|
-
Term: run counter
|
34
|
-
Concept Type: Integer (Type)
|
35
|
-
Term: migrated rows
|
36
|
-
Concept Type: Integer (Type)
|
37
|
-
Term: error counter
|
38
|
-
Concept Type: Integer (Type)
|
39
|
-
Term: error threshold
|
40
|
-
Concept Type: Integer (Type)
|
41
|
-
Term: delayMS
|
42
|
-
Concept Type: Integer (Type)
|
43
|
-
Term: backoffDelayMS
|
44
|
-
Concept Type: Integer (Type)
|
45
|
-
Term: converged time
|
46
|
-
Concept Type: Date Time (Type)
|
47
|
-
Term: last error message
|
48
|
-
Concept Type: Text (Type)
|
49
|
-
|
50
|
-
Term: migration status
|
51
|
-
Reference Scheme: migration key
|
52
|
-
Database ID Field: migration key
|
53
|
-
|
54
|
-
Fact Type: migration status has migration key
|
55
|
-
Necessity: each migration status has exactly one migration key
|
56
|
-
|
57
|
-
Fact Type: migration status has start time
|
58
|
-
Necessity: each migration status has at most one start time
|
59
|
-
|
60
|
-
Fact Type: migration status has last run time
|
61
|
-
Necessity: each migration status has at most one last run time
|
62
|
-
|
63
|
-
Fact Type: migration status has run counter
|
64
|
-
Necessity: each migration status has at most one run counter
|
65
|
-
|
66
|
-
Fact Type: migration status has migrated rows
|
67
|
-
Necessity: each migration status has at most one migrated rows
|
68
|
-
|
69
|
-
Fact Type: migration status has error counter
|
70
|
-
Necessity: each migration status has at most one error counter
|
71
|
-
|
72
|
-
Fact Type: migration status has error threshold
|
73
|
-
Necessity: each migration status has at most one error threshold
|
74
|
-
|
75
|
-
Fact Type: migration status has delayMS
|
76
|
-
Necessity: each migration status has at most one delayMS
|
77
|
-
|
78
|
-
Fact Type: migration status has backoffDelayMS
|
79
|
-
Necessity: each migration status has at most one backoffDelayMS
|
80
|
-
|
81
|
-
Fact Type: migration status is backoff
|
82
|
-
|
83
|
-
Fact Type: migration status has converged time
|
84
|
-
Necessity: each migration status has at most one converged time
|
85
|
-
|
86
|
-
Fact Type: migration status has last error message
|
87
|
-
Necessity: each migration status has at most one last error message
|
88
|
-
|
89
|
-
Fact Type: migration status should stop
|
@@ -0,0 +1,278 @@
|
|
1
|
+
import type { Tx } from '../database-layer/db';
|
2
|
+
import type { Resolvable } from '../sbvr-api/common-types';
|
3
|
+
import type { Config, Model } from '../config-loader/config-loader';
|
4
|
+
|
5
|
+
import { Engines } from '@balena/abstract-sql-compiler';
|
6
|
+
import * as _ from 'lodash';
|
7
|
+
import { TypedError } from 'typed-error';
|
8
|
+
import { migrator as migratorEnv } from '../config-loader/env';
|
9
|
+
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
10
|
+
import { delay } from '../sbvr-api/control-flow';
|
11
|
+
|
12
|
+
// tslint:disable-next-line:no-var-requires
|
13
|
+
const modelText: string = require('./migrations.sbvr');
|
14
|
+
|
15
|
+
type ApiRootModel = Model & { apiRoot: string };
|
16
|
+
|
17
|
+
type SbvrUtils = typeof sbvrUtils;
|
18
|
+
|
19
|
+
type MigrationTuple = [string, Migration];
|
20
|
+
|
21
|
+
export type MigrationFn = (tx: Tx, sbvrUtils: SbvrUtils) => Resolvable<void>;
|
22
|
+
|
23
|
+
export type Migration = string | MigrationFn;
|
24
|
+
|
25
|
+
export class MigrationError extends TypedError {}
|
26
|
+
|
27
|
+
// Tagged template to convert binds from `?` format to the necessary output format,
|
28
|
+
// eg `$1`/`$2`/etc for postgres
|
29
|
+
const binds = (strings: TemplateStringsArray, ...bindNums: number[]) =>
|
30
|
+
strings
|
31
|
+
.map((str, i) => {
|
32
|
+
if (i === bindNums.length) {
|
33
|
+
return str;
|
34
|
+
}
|
35
|
+
if (i + 1 !== bindNums[i]) {
|
36
|
+
throw new SyntaxError('Migration sql binds must be sequential');
|
37
|
+
}
|
38
|
+
if (sbvrUtils.db.engine === Engines.postgres) {
|
39
|
+
return str + `$${bindNums[i]}`;
|
40
|
+
}
|
41
|
+
return str + `?`;
|
42
|
+
})
|
43
|
+
.join('');
|
44
|
+
|
45
|
+
export const postRun = async (tx: Tx, model: ApiRootModel): Promise<void> => {
|
46
|
+
const { initSql } = model;
|
47
|
+
if (initSql == null) {
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
|
51
|
+
const modelName = model.apiRoot;
|
52
|
+
|
53
|
+
const exists = await checkModelAlreadyExists(tx, modelName);
|
54
|
+
if (!exists) {
|
55
|
+
(sbvrUtils.api.migrations?.logger.info ?? console.info)(
|
56
|
+
'First time executing, running init script',
|
57
|
+
);
|
58
|
+
await lockMigrations(tx, modelName, async () => {
|
59
|
+
await tx.executeSql(initSql);
|
60
|
+
});
|
61
|
+
}
|
62
|
+
};
|
63
|
+
|
64
|
+
export const run = async (tx: Tx, model: ApiRootModel): Promise<void> => {
|
65
|
+
const { migrations } = model;
|
66
|
+
if (migrations == null || _.isEmpty(migrations)) {
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
|
70
|
+
const modelName = model.apiRoot;
|
71
|
+
|
72
|
+
// migrations only run if the model has been executed before,
|
73
|
+
// to make changes that can't be automatically applied
|
74
|
+
const exists = await checkModelAlreadyExists(tx, modelName);
|
75
|
+
if (!exists) {
|
76
|
+
(sbvrUtils.api.migrations?.logger.info ?? console.info)(
|
77
|
+
'First time model has executed, skipping migrations',
|
78
|
+
);
|
79
|
+
|
80
|
+
return await setExecutedMigrations(tx, modelName, Object.keys(migrations));
|
81
|
+
}
|
82
|
+
await lockMigrations(tx, modelName, async () => {
|
83
|
+
const executedMigrations = await getExecutedMigrations(tx, modelName);
|
84
|
+
const pendingMigrations = filterAndSortPendingMigrations(
|
85
|
+
migrations,
|
86
|
+
executedMigrations,
|
87
|
+
);
|
88
|
+
if (pendingMigrations.length === 0) {
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
|
92
|
+
const newlyExecutedMigrations = await executeMigrations(
|
93
|
+
tx,
|
94
|
+
pendingMigrations,
|
95
|
+
);
|
96
|
+
await setExecutedMigrations(tx, modelName, [
|
97
|
+
...executedMigrations,
|
98
|
+
...newlyExecutedMigrations,
|
99
|
+
]);
|
100
|
+
});
|
101
|
+
};
|
102
|
+
|
103
|
+
const checkModelAlreadyExists = async (
|
104
|
+
tx: Tx,
|
105
|
+
modelName: string,
|
106
|
+
): Promise<boolean> => {
|
107
|
+
const result = await tx.tableList("name = 'migration'");
|
108
|
+
if (result.rows.length === 0) {
|
109
|
+
return false;
|
110
|
+
}
|
111
|
+
const { rows } = await tx.executeSql(
|
112
|
+
binds`
|
113
|
+
SELECT 1
|
114
|
+
FROM "model"
|
115
|
+
WHERE "model"."is of-vocabulary" = ${1}
|
116
|
+
LIMIT 1`,
|
117
|
+
[modelName],
|
118
|
+
);
|
119
|
+
|
120
|
+
return rows.length > 0;
|
121
|
+
};
|
122
|
+
|
123
|
+
const getExecutedMigrations = async (
|
124
|
+
tx: Tx,
|
125
|
+
modelName: string,
|
126
|
+
): Promise<string[]> => {
|
127
|
+
const { rows } = await tx.executeSql(
|
128
|
+
binds`
|
129
|
+
SELECT "migration"."executed migrations" AS "executed_migrations"
|
130
|
+
FROM "migration"
|
131
|
+
WHERE "migration"."model name" = ${1}`,
|
132
|
+
[modelName],
|
133
|
+
);
|
134
|
+
|
135
|
+
const data = rows[0];
|
136
|
+
if (data == null) {
|
137
|
+
return [];
|
138
|
+
}
|
139
|
+
|
140
|
+
return JSON.parse(data.executed_migrations) as string[];
|
141
|
+
};
|
142
|
+
|
143
|
+
const setExecutedMigrations = async (
|
144
|
+
tx: Tx,
|
145
|
+
modelName: string,
|
146
|
+
executedMigrations: string[],
|
147
|
+
): Promise<void> => {
|
148
|
+
const stringifiedMigrations = JSON.stringify(executedMigrations);
|
149
|
+
|
150
|
+
const result = await tx.tableList("name = 'migration'");
|
151
|
+
if (result.rows.length === 0) {
|
152
|
+
return;
|
153
|
+
}
|
154
|
+
|
155
|
+
const { rowsAffected } = await tx.executeSql(
|
156
|
+
binds`
|
157
|
+
UPDATE "migration"
|
158
|
+
SET "model name" = ${1},
|
159
|
+
"executed migrations" = ${2}
|
160
|
+
WHERE "migration"."model name" = ${3}`,
|
161
|
+
[modelName, stringifiedMigrations, modelName],
|
162
|
+
);
|
163
|
+
|
164
|
+
if (rowsAffected === 0) {
|
165
|
+
await tx.executeSql(
|
166
|
+
binds`
|
167
|
+
INSERT INTO "migration" ("model name", "executed migrations")
|
168
|
+
VALUES (${1}, ${2})`,
|
169
|
+
[modelName, stringifiedMigrations],
|
170
|
+
);
|
171
|
+
}
|
172
|
+
};
|
173
|
+
|
174
|
+
// turns {"key1": migration, "key3": migration, "key2": migration}
|
175
|
+
// into [["key1", migration], ["key2", migration], ["key3", migration]]
|
176
|
+
const filterAndSortPendingMigrations = (
|
177
|
+
migrations: NonNullable<Model['migrations']>,
|
178
|
+
executedMigrations: string[],
|
179
|
+
): MigrationTuple[] =>
|
180
|
+
(_(migrations).omit(executedMigrations) as _.Object<typeof migrations>)
|
181
|
+
.toPairs()
|
182
|
+
.sortBy(([migrationKey]) => migrationKey)
|
183
|
+
.value();
|
184
|
+
|
185
|
+
const lockMigrations = async <T>(
|
186
|
+
tx: Tx,
|
187
|
+
modelName: string,
|
188
|
+
fn: () => Promise<T>,
|
189
|
+
): Promise<T> => {
|
190
|
+
try {
|
191
|
+
await tx.executeSql(
|
192
|
+
binds`
|
193
|
+
DELETE FROM "migration lock"
|
194
|
+
WHERE "model name" = ${1}
|
195
|
+
AND "created at" < ${2}`,
|
196
|
+
[modelName, new Date(Date.now() - migratorEnv.lockTimeout)],
|
197
|
+
);
|
198
|
+
await tx.executeSql(
|
199
|
+
binds`
|
200
|
+
INSERT INTO "migration lock" ("model name")
|
201
|
+
VALUES (${1})`,
|
202
|
+
[modelName],
|
203
|
+
);
|
204
|
+
} catch (err) {
|
205
|
+
await delay(migratorEnv.lockFailDelay);
|
206
|
+
throw err;
|
207
|
+
}
|
208
|
+
try {
|
209
|
+
return await fn();
|
210
|
+
} finally {
|
211
|
+
try {
|
212
|
+
await tx.executeSql(
|
213
|
+
binds`
|
214
|
+
DELETE FROM "migration lock"
|
215
|
+
WHERE "model name" = ${1}`,
|
216
|
+
[modelName],
|
217
|
+
);
|
218
|
+
} catch {
|
219
|
+
// We ignore errors here as it's mostly likely caused by the migration failing and
|
220
|
+
// rolling back the transaction, and if we rethrow here we'll overwrite the real error
|
221
|
+
// making it much harder for users to see what went wrong and fix it
|
222
|
+
}
|
223
|
+
}
|
224
|
+
};
|
225
|
+
|
226
|
+
const executeMigrations = async (
|
227
|
+
tx: Tx,
|
228
|
+
migrations: MigrationTuple[] = [],
|
229
|
+
): Promise<string[]> => {
|
230
|
+
try {
|
231
|
+
for (const migration of migrations) {
|
232
|
+
await executeMigration(tx, migration);
|
233
|
+
}
|
234
|
+
} catch (err) {
|
235
|
+
(sbvrUtils.api.migrations?.logger.error ?? console.error)(
|
236
|
+
'Error while executing migrations, rolled back',
|
237
|
+
);
|
238
|
+
throw new MigrationError(err);
|
239
|
+
}
|
240
|
+
return migrations.map(([migrationKey]) => migrationKey); // return migration keys
|
241
|
+
};
|
242
|
+
|
243
|
+
const executeMigration = async (
|
244
|
+
tx: Tx,
|
245
|
+
[key, migration]: MigrationTuple,
|
246
|
+
): Promise<void> => {
|
247
|
+
(sbvrUtils.api.migrations?.logger.info ?? console.info)(
|
248
|
+
`Running migration ${JSON.stringify(key)}`,
|
249
|
+
);
|
250
|
+
|
251
|
+
if (typeof migration === 'function') {
|
252
|
+
await migration(tx, sbvrUtils);
|
253
|
+
} else if (typeof migration === 'string') {
|
254
|
+
await tx.executeSql(migration);
|
255
|
+
} else {
|
256
|
+
throw new MigrationError(`Invalid migration type: ${typeof migration}`);
|
257
|
+
}
|
258
|
+
};
|
259
|
+
|
260
|
+
export const config: Config = {
|
261
|
+
models: [
|
262
|
+
{
|
263
|
+
modelName: 'migrations',
|
264
|
+
apiRoot: 'migrations',
|
265
|
+
modelText,
|
266
|
+
migrations: {
|
267
|
+
'11.0.0-modified-at': `
|
268
|
+
ALTER TABLE "migration"
|
269
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
270
|
+
`,
|
271
|
+
'11.0.1-modified-at': `
|
272
|
+
ALTER TABLE "migration lock"
|
273
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
274
|
+
`,
|
275
|
+
},
|
276
|
+
},
|
277
|
+
],
|
278
|
+
};
|
@@ -33,8 +33,7 @@ import { PinejsClientCore, PromiseResultTypes } from 'pinejs-client-core';
|
|
33
33
|
|
34
34
|
import { ExtendedSBVRParser } from '../extended-sbvr-parser/extended-sbvr-parser';
|
35
35
|
|
36
|
-
import * as
|
37
|
-
import * as syncMigrator from '../migrator/sync';
|
36
|
+
import * as migrator from '../migrator/migrator';
|
38
37
|
import { generateODataMetadata } from '../odata-metadata/odata-metadata-generator';
|
39
38
|
|
40
39
|
// tslint:disable-next-line:no-var-requires
|
@@ -431,20 +430,6 @@ export const generateModels = (
|
|
431
430
|
return { vocab, se, lf, abstractSql, sql, odataMetadata };
|
432
431
|
};
|
433
432
|
|
434
|
-
export const executeAsyncModelMigration = (
|
435
|
-
model: ExecutableModel,
|
436
|
-
): Promise<void> => executeAsyncModelMigrations([model]);
|
437
|
-
|
438
|
-
export const executeAsyncModelMigrations = async (
|
439
|
-
execModels: ExecutableModel[],
|
440
|
-
): Promise<void> => {
|
441
|
-
await Promise.all(
|
442
|
-
execModels.map(async (model) => {
|
443
|
-
await asyncMigrator.run(model);
|
444
|
-
}),
|
445
|
-
);
|
446
|
-
};
|
447
|
-
|
448
433
|
export const executeModel = (
|
449
434
|
tx: Db.Tx,
|
450
435
|
model: ExecutableModel,
|
@@ -459,7 +444,7 @@ export const executeModels = async (
|
|
459
444
|
execModels.map(async (model) => {
|
460
445
|
const { apiRoot } = model;
|
461
446
|
|
462
|
-
await
|
447
|
+
await migrator.run(tx, model);
|
463
448
|
const compiledModel = generateModels(model, db.engine);
|
464
449
|
|
465
450
|
// Create tables related to terms and fact types
|
@@ -476,7 +461,7 @@ export const executeModels = async (
|
|
476
461
|
}
|
477
462
|
await promise;
|
478
463
|
}
|
479
|
-
await
|
464
|
+
await migrator.postRun(tx, model);
|
480
465
|
|
481
466
|
odataResponse.prepareModel(compiledModel.abstractSql);
|
482
467
|
deepFreeze(compiledModel.abstractSql);
|
@@ -4,8 +4,7 @@ import './sbvr-loader';
|
|
4
4
|
|
5
5
|
import * as dbModule from '../database-layer/db';
|
6
6
|
import * as configLoader from '../config-loader/config-loader';
|
7
|
-
import * as migrator from '../migrator/
|
8
|
-
import * as migratorUtils from '../migrator/utils';
|
7
|
+
import * as migrator from '../migrator/migrator';
|
9
8
|
|
10
9
|
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
11
10
|
|
@@ -18,7 +17,7 @@ export * as env from '../config-loader/env';
|
|
18
17
|
export * as types from '../sbvr-api/common-types';
|
19
18
|
export * as hooks from '../sbvr-api/hooks';
|
20
19
|
export type { configLoader as ConfigLoader };
|
21
|
-
export type {
|
20
|
+
export type { migrator as Migrator };
|
22
21
|
|
23
22
|
let envDatabaseOptions: dbModule.DatabaseOptions<string>;
|
24
23
|
if (dbModule.engines.websql != null) {
|