@balena/pinejs 15.0.0-delete-state-default-user-permissions-981931563dc47b2a8b873bc787d7dacfcc6c52e3 → 15.0.0-deprecate-node12-8a99d72ae66d7708293afc56c5d7eb19b39081cd

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. package/.resinci.yml +0 -1
  2. package/.versionbot/CHANGELOG.yml +226 -8
  3. package/CHANGELOG.md +85 -1
  4. package/out/bin/utils.js +1 -1
  5. package/out/bin/utils.js.map +1 -1
  6. package/out/config-loader/config-loader.d.ts +3 -5
  7. package/out/config-loader/config-loader.js +35 -31
  8. package/out/config-loader/config-loader.js.map +1 -1
  9. package/out/data-server/sbvr-server.js +8 -8
  10. package/out/data-server/sbvr-server.js.map +1 -1
  11. package/out/database-layer/db.d.ts +1 -1
  12. package/out/database-layer/db.js +3 -3
  13. package/out/database-layer/db.js.map +1 -1
  14. package/out/express-emulator/express.js +1 -1
  15. package/out/express-emulator/express.js.map +1 -1
  16. package/out/http-transactions/transactions.js +4 -4
  17. package/out/http-transactions/transactions.js.map +1 -1
  18. package/out/migrator/sync.d.ts +9 -0
  19. package/out/migrator/sync.js +121 -0
  20. package/out/migrator/sync.js.map +1 -0
  21. package/out/migrator/utils.d.ts +28 -0
  22. package/out/migrator/utils.js +104 -0
  23. package/out/migrator/utils.js.map +1 -0
  24. package/out/odata-metadata/odata-metadata-generator.js +6 -9
  25. package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
  26. package/out/passport-pinejs/passport-pinejs.js +4 -3
  27. package/out/passport-pinejs/passport-pinejs.js.map +1 -1
  28. package/out/pinejs-session-store/pinejs-session-store.js +1 -1
  29. package/out/pinejs-session-store/pinejs-session-store.js.map +1 -1
  30. package/out/sbvr-api/abstract-sql.d.ts +1 -1
  31. package/out/sbvr-api/abstract-sql.js.map +1 -1
  32. package/out/sbvr-api/control-flow.js.map +1 -1
  33. package/out/sbvr-api/hooks.d.ts +6 -3
  34. package/out/sbvr-api/hooks.js +3 -3
  35. package/out/sbvr-api/hooks.js.map +1 -1
  36. package/out/sbvr-api/odata-response.js +5 -5
  37. package/out/sbvr-api/odata-response.js.map +1 -1
  38. package/out/sbvr-api/permissions.js +25 -20
  39. package/out/sbvr-api/permissions.js.map +1 -1
  40. package/out/sbvr-api/sbvr-utils.d.ts +4 -3
  41. package/out/sbvr-api/sbvr-utils.js +71 -48
  42. package/out/sbvr-api/sbvr-utils.js.map +1 -1
  43. package/out/sbvr-api/uri-parser.d.ts +13 -11
  44. package/out/sbvr-api/uri-parser.js +4 -4
  45. package/out/sbvr-api/uri-parser.js.map +1 -1
  46. package/out/server-glue/module.d.ts +2 -2
  47. package/out/server-glue/module.js +2 -1
  48. package/out/server-glue/module.js.map +1 -1
  49. package/package.json +19 -19
  50. package/src/bin/utils.ts +1 -1
  51. package/src/config-loader/config-loader.ts +69 -44
  52. package/src/data-server/sbvr-server.js +8 -8
  53. package/src/database-layer/db.ts +11 -11
  54. package/src/express-emulator/express.js +1 -1
  55. package/src/http-transactions/transactions.js +4 -4
  56. package/src/migrator/sync.ts +169 -0
  57. package/src/migrator/utils.ts +154 -0
  58. package/src/odata-metadata/odata-metadata-generator.ts +8 -11
  59. package/src/passport-pinejs/passport-pinejs.ts +3 -2
  60. package/src/sbvr-api/abstract-sql.ts +6 -3
  61. package/src/sbvr-api/control-flow.ts +2 -2
  62. package/src/sbvr-api/hooks.ts +18 -8
  63. package/src/sbvr-api/odata-response.ts +4 -4
  64. package/src/sbvr-api/permissions.ts +42 -36
  65. package/src/sbvr-api/sbvr-utils.ts +121 -58
  66. package/src/sbvr-api/uri-parser.ts +29 -21
  67. package/src/server-glue/module.ts +4 -3
  68. package/tsconfig.json +1 -3
  69. package/typings/lf-to-abstract-sql.d.ts +6 -9
  70. package/out/migrator/migrator.d.ts +0 -17
  71. package/out/migrator/migrator.js +0 -185
  72. package/out/migrator/migrator.js.map +0 -1
  73. package/src/migrator/migrator.ts +0 -278
@@ -37,20 +37,22 @@ export interface UnparsedRequest {
37
37
  _isChangeSet?: boolean;
38
38
  }
39
39
 
40
- export interface ODataRequest {
40
+ export interface ParsedODataRequest {
41
41
  method: SupportedMethod;
42
42
  url: string;
43
+ vocabulary: string;
44
+ resourceName: string;
45
+ values: AnyObject;
43
46
  odataQuery: ODataQuery;
44
47
  odataBinds: OdataBinds;
45
- values: AnyObject;
48
+ custom: AnyObject;
49
+ id?: number | undefined;
50
+ _defer?: boolean;
51
+ }
52
+ export interface ODataRequest extends ParsedODataRequest {
46
53
  abstractSqlModel?: AbstractSQLCompiler.AbstractSqlModel;
47
54
  abstractSqlQuery?: AbstractSQLCompiler.AbstractSqlQuery;
48
55
  sqlQuery?: AbstractSQLCompiler.SqlResult | AbstractSQLCompiler.SqlResult[];
49
- resourceName: string;
50
- vocabulary: string;
51
- _defer?: boolean;
52
- id?: number | undefined;
53
- custom: AnyObject;
54
56
  tx?: Tx;
55
57
  modifiedFields?: ReturnType<
56
58
  AbstractSQLCompiler.EngineInstance['getModifiedFields']
@@ -58,7 +60,7 @@ export interface ODataRequest {
58
60
  affectedIds?: number[];
59
61
  pendingAffectedIds?: Promise<number[]>;
60
62
  hooks?: InstantiatedHooks;
61
- engine?: AbstractSQLCompiler.Engines;
63
+ engine: AbstractSQLCompiler.Engines;
62
64
  }
63
65
 
64
66
  // Converts a value to its string representation and tries to parse is as an
@@ -96,12 +98,12 @@ export const memoizedParseOdata = (() => {
96
98
  // Try to cache based on parameter aliases if there might be some
97
99
  const parameterAliases = new URLSearchParams();
98
100
  const queryParams = new URLSearchParams(url.slice(queryParamsIndex));
99
- Array.from(queryParams.entries()).forEach(([key, value]) => {
101
+ for (const [key, value] of Array.from(queryParams.entries())) {
100
102
  if (key.startsWith('@')) {
101
103
  parameterAliases.append(key, value);
102
104
  queryParams.delete(key);
103
105
  }
104
- });
106
+ }
105
107
  const parameterAliasesString = parameterAliases.toString();
106
108
  if (parameterAliasesString !== '') {
107
109
  const parsed = _.cloneDeep(
@@ -257,23 +259,26 @@ export const metadataEndpoints = ['$metadata', '$serviceroot'];
257
259
 
258
260
  export async function parseOData(
259
261
  b: UnparsedRequest & { _isChangeSet?: false },
260
- ): Promise<ODataRequest>;
262
+ ): Promise<ParsedODataRequest>;
261
263
  export async function parseOData(
262
264
  b: UnparsedRequest & { _isChangeSet: true },
263
- ): Promise<ODataRequest[]>;
265
+ ): Promise<ParsedODataRequest[]>;
264
266
  export async function parseOData(
265
267
  b: UnparsedRequest,
266
- ): Promise<ODataRequest | ODataRequest[]>;
268
+ ): Promise<ParsedODataRequest | ParsedODataRequest[]>;
267
269
  export async function parseOData(
268
270
  b: UnparsedRequest,
269
- ): Promise<ODataRequest | ODataRequest[]> {
271
+ ): Promise<ParsedODataRequest | ParsedODataRequest[]> {
270
272
  try {
271
273
  if (b._isChangeSet && b.changeSet != null) {
272
274
  // We sort the CS set once, we must assure that requests which reference
273
275
  // other requests in the changeset are placed last. Once they are sorted
274
276
  // Map will guarantee retrival of results in insertion order
275
277
  const sortedCS = _.sortBy(b.changeSet, (el) => el.url[0] !== '/');
276
- const csReferences = new Map<ODataRequest['id'], ODataRequest>();
278
+ const csReferences = new Map<
279
+ ParsedODataRequest['id'],
280
+ ParsedODataRequest
281
+ >();
277
282
  for (const cs of sortedCS) {
278
283
  parseODataChangeset(csReferences, cs);
279
284
  }
@@ -287,14 +292,14 @@ export async function parseOData(
287
292
  url,
288
293
  vocabulary: apiRoot,
289
294
  resourceName: odata.tree.resource,
290
- odataBinds: odata.binds,
291
- odataQuery: odata.tree,
292
295
  values: b.data ?? {},
296
+ odataQuery: odata.tree,
297
+ odataBinds: odata.binds,
293
298
  custom: {},
294
299
  _defer: false,
295
300
  };
296
301
  }
297
- } catch (err) {
302
+ } catch (err: any) {
298
303
  if (err instanceof ODataParser.SyntaxError) {
299
304
  throw new BadRequestError(`Malformed url: '${b.url}'`);
300
305
  }
@@ -307,10 +312,13 @@ export async function parseOData(
307
312
  }
308
313
 
309
314
  const parseODataChangeset = (
310
- csReferences: Map<ODataRequest['id'], ODataRequest>,
315
+ csReferences: Map<ParsedODataRequest['id'], ParsedODataRequest>,
311
316
  b: UnparsedRequest,
312
317
  ): void => {
313
- const contentId: ODataRequest['id'] = mustExtractHeader(b, 'content-id');
318
+ const contentId: ParsedODataRequest['id'] = mustExtractHeader(
319
+ b,
320
+ 'content-id',
321
+ );
314
322
 
315
323
  if (csReferences.has(contentId)) {
316
324
  throw new BadRequestError('Content-Id must be unique inside a changeset');
@@ -341,7 +349,7 @@ const parseODataChangeset = (
341
349
  defer = true;
342
350
  }
343
351
 
344
- const parseResult: ODataRequest = {
352
+ const parseResult: ParsedODataRequest = {
345
353
  method: b.method as SupportedMethod,
346
354
  url,
347
355
  vocabulary: apiRoot,
@@ -4,7 +4,8 @@ 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/migrator';
7
+ import * as migrator from '../migrator/sync';
8
+ import * as migratorUtils from '../migrator/utils';
8
9
 
9
10
  import * as sbvrUtils from '../sbvr-api/sbvr-utils';
10
11
 
@@ -17,7 +18,7 @@ export * as env from '../config-loader/env';
17
18
  export * as types from '../sbvr-api/common-types';
18
19
  export * as hooks from '../sbvr-api/hooks';
19
20
  export type { configLoader as ConfigLoader };
20
- export type { migrator as Migrator };
21
+ export type { migratorUtils as Migrator };
21
22
 
22
23
  let envDatabaseOptions: dbModule.DatabaseOptions<string>;
23
24
  if (dbModule.engines.websql != null) {
@@ -72,7 +73,7 @@ export const init = async <T extends string>(
72
73
  await Promise.all(promises);
73
74
 
74
75
  return cfgLoader;
75
- } catch (err) {
76
+ } catch (err: any) {
76
77
  console.error('Error initialising server', err, err.stack);
77
78
  process.exit(1);
78
79
  }
package/tsconfig.json CHANGED
@@ -3,8 +3,6 @@
3
3
  "module": "commonjs",
4
4
  "strict": true,
5
5
  "strictFunctionTypes": false,
6
- "strictPropertyInitialization": false,
7
- "useUnknownInCatchVariables": false,
8
6
  "noImplicitThis": false,
9
7
  "noUnusedParameters": true,
10
8
  "noUnusedLocals": true,
@@ -13,7 +11,7 @@
13
11
  "removeComments": true,
14
12
  "rootDir": "src",
15
13
  "sourceMap": true,
16
- "target": "es2018",
14
+ "target": "es2019",
17
15
  "declaration": true,
18
16
  "skipLibCheck": true,
19
17
  "resolveJsonModule": true,
@@ -1,22 +1,19 @@
1
1
  declare module '@balena/lf-to-abstract-sql' {
2
+ import type sbvrTypes = require('@balena/sbvr-types');
3
+ import type { LFModel } from '@balena/sbvr-parser';
4
+ import type { AbstractSqlModel } from '@balena/abstract-sql-compiler';
2
5
  export const LF2AbstractSQL: {
3
6
  createInstance: () => {
4
- match: (
5
- lfModel: LFModel,
6
- rule: 'Process',
7
- ) => AbstractSQLCompiler.AbstractSqlModel;
7
+ match: (lfModel: LFModel, rule: 'Process') => AbstractSqlModel;
8
8
  addTypes: (types: typeof sbvrTypes) => void;
9
9
  reset: () => void;
10
10
  };
11
11
  };
12
12
  export const LF2AbstractSQLPrep: {
13
13
  match: (lfModel: LFModel, rule: 'Process') => LFModel;
14
- _extend({}): typeof LF2AbstractSQL.LF2AbstractSQLPrep;
14
+ _extend({}): typeof LF2AbstractSQLPrep;
15
15
  };
16
16
  export const createTranslator: (
17
17
  types: typeof sbvrTypes,
18
- ) => (
19
- lfModel: LFModel,
20
- rule: 'Process',
21
- ) => AbstractSQLCompiler.AbstractSqlModel;
18
+ ) => (lfModel: LFModel, rule: 'Process') => AbstractSqlModel;
22
19
  }
@@ -1,17 +0,0 @@
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
- import { TypedError } from 'typed-error';
5
- import * as sbvrUtils from '../sbvr-api/sbvr-utils';
6
- declare type ApiRootModel = Model & {
7
- apiRoot: string;
8
- };
9
- declare type SbvrUtils = typeof sbvrUtils;
10
- export declare type MigrationFn = (tx: Tx, sbvrUtils: SbvrUtils) => Resolvable<void>;
11
- export declare type Migration = string | MigrationFn;
12
- export declare class MigrationError extends TypedError {
13
- }
14
- export declare const postRun: (tx: Tx, model: ApiRootModel) => Promise<void>;
15
- export declare const run: (tx: Tx, model: ApiRootModel) => Promise<void>;
16
- export declare const config: Config;
17
- export {};
@@ -1,185 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.config = exports.run = exports.postRun = exports.MigrationError = void 0;
4
- const _ = require("lodash");
5
- const typed_error_1 = require("typed-error");
6
- const env_1 = require("../config-loader/env");
7
- const sbvrUtils = require("../sbvr-api/sbvr-utils");
8
- const control_flow_1 = require("../sbvr-api/control-flow");
9
- const modelText = require('./migrations.sbvr');
10
- class MigrationError extends typed_error_1.TypedError {
11
- }
12
- exports.MigrationError = MigrationError;
13
- const binds = (strings, ...bindNums) => strings
14
- .map((str, i) => {
15
- if (i === bindNums.length) {
16
- return str;
17
- }
18
- if (i + 1 !== bindNums[i]) {
19
- throw new SyntaxError('Migration sql binds must be sequential');
20
- }
21
- if (sbvrUtils.db.engine === "postgres") {
22
- return str + `$${bindNums[i]}`;
23
- }
24
- return str + `?`;
25
- })
26
- .join('');
27
- const postRun = async (tx, model) => {
28
- var _a, _b;
29
- const { initSql } = model;
30
- if (initSql == null) {
31
- return;
32
- }
33
- const modelName = model.apiRoot;
34
- const exists = await checkModelAlreadyExists(tx, modelName);
35
- if (!exists) {
36
- ((_b = (_a = sbvrUtils.api.migrations) === null || _a === void 0 ? void 0 : _a.logger.info) !== null && _b !== void 0 ? _b : console.info)('First time executing, running init script');
37
- await lockMigrations(tx, modelName, async () => {
38
- await tx.executeSql(initSql);
39
- });
40
- }
41
- };
42
- exports.postRun = postRun;
43
- const run = async (tx, model) => {
44
- var _a, _b;
45
- const { migrations } = model;
46
- if (migrations == null || _.isEmpty(migrations)) {
47
- return;
48
- }
49
- const modelName = model.apiRoot;
50
- const exists = await checkModelAlreadyExists(tx, modelName);
51
- if (!exists) {
52
- ((_b = (_a = sbvrUtils.api.migrations) === null || _a === void 0 ? void 0 : _a.logger.info) !== null && _b !== void 0 ? _b : console.info)('First time model has executed, skipping migrations');
53
- return await setExecutedMigrations(tx, modelName, Object.keys(migrations));
54
- }
55
- await lockMigrations(tx, modelName, async () => {
56
- const executedMigrations = await getExecutedMigrations(tx, modelName);
57
- const pendingMigrations = filterAndSortPendingMigrations(migrations, executedMigrations);
58
- if (pendingMigrations.length === 0) {
59
- return;
60
- }
61
- const newlyExecutedMigrations = await executeMigrations(tx, pendingMigrations);
62
- await setExecutedMigrations(tx, modelName, [
63
- ...executedMigrations,
64
- ...newlyExecutedMigrations,
65
- ]);
66
- });
67
- };
68
- exports.run = run;
69
- const checkModelAlreadyExists = async (tx, modelName) => {
70
- const result = await tx.tableList("name = 'migration'");
71
- if (result.rows.length === 0) {
72
- return false;
73
- }
74
- const { rows } = await tx.executeSql(binds `
75
- SELECT 1
76
- FROM "model"
77
- WHERE "model"."is of-vocabulary" = ${1}
78
- LIMIT 1`, [modelName]);
79
- return rows.length > 0;
80
- };
81
- const getExecutedMigrations = async (tx, modelName) => {
82
- const { rows } = await tx.executeSql(binds `
83
- SELECT "migration"."executed migrations" AS "executed_migrations"
84
- FROM "migration"
85
- WHERE "migration"."model name" = ${1}`, [modelName]);
86
- const data = rows[0];
87
- if (data == null) {
88
- return [];
89
- }
90
- return JSON.parse(data.executed_migrations);
91
- };
92
- const setExecutedMigrations = async (tx, modelName, executedMigrations) => {
93
- const stringifiedMigrations = JSON.stringify(executedMigrations);
94
- const result = await tx.tableList("name = 'migration'");
95
- if (result.rows.length === 0) {
96
- return;
97
- }
98
- const { rowsAffected } = await tx.executeSql(binds `
99
- UPDATE "migration"
100
- SET "model name" = ${1},
101
- "executed migrations" = ${2}
102
- WHERE "migration"."model name" = ${3}`, [modelName, stringifiedMigrations, modelName]);
103
- if (rowsAffected === 0) {
104
- await tx.executeSql(binds `
105
- INSERT INTO "migration" ("model name", "executed migrations")
106
- VALUES (${1}, ${2})`, [modelName, stringifiedMigrations]);
107
- }
108
- };
109
- const filterAndSortPendingMigrations = (migrations, executedMigrations) => _(migrations).omit(executedMigrations)
110
- .toPairs()
111
- .sortBy(([migrationKey]) => migrationKey)
112
- .value();
113
- const lockMigrations = async (tx, modelName, fn) => {
114
- try {
115
- await tx.executeSql(binds `
116
- DELETE FROM "migration lock"
117
- WHERE "model name" = ${1}
118
- AND "created at" < ${2}`, [modelName, new Date(Date.now() - env_1.migrator.lockTimeout)]);
119
- await tx.executeSql(binds `
120
- INSERT INTO "migration lock" ("model name")
121
- VALUES (${1})`, [modelName]);
122
- }
123
- catch (err) {
124
- await (0, control_flow_1.delay)(env_1.migrator.lockFailDelay);
125
- throw err;
126
- }
127
- try {
128
- return await fn();
129
- }
130
- finally {
131
- try {
132
- await tx.executeSql(binds `
133
- DELETE FROM "migration lock"
134
- WHERE "model name" = ${1}`, [modelName]);
135
- }
136
- catch (_a) {
137
- }
138
- }
139
- };
140
- const executeMigrations = async (tx, migrations = []) => {
141
- var _a, _b;
142
- try {
143
- for (const migration of migrations) {
144
- await executeMigration(tx, migration);
145
- }
146
- }
147
- catch (err) {
148
- ((_b = (_a = sbvrUtils.api.migrations) === null || _a === void 0 ? void 0 : _a.logger.error) !== null && _b !== void 0 ? _b : console.error)('Error while executing migrations, rolled back');
149
- throw new MigrationError(err);
150
- }
151
- return migrations.map(([migrationKey]) => migrationKey);
152
- };
153
- const executeMigration = async (tx, [key, migration]) => {
154
- var _a, _b;
155
- ((_b = (_a = sbvrUtils.api.migrations) === null || _a === void 0 ? void 0 : _a.logger.info) !== null && _b !== void 0 ? _b : console.info)(`Running migration ${JSON.stringify(key)}`);
156
- if (typeof migration === 'function') {
157
- await migration(tx, sbvrUtils);
158
- }
159
- else if (typeof migration === 'string') {
160
- await tx.executeSql(migration);
161
- }
162
- else {
163
- throw new MigrationError(`Invalid migration type: ${typeof migration}`);
164
- }
165
- };
166
- exports.config = {
167
- models: [
168
- {
169
- modelName: 'migrations',
170
- apiRoot: 'migrations',
171
- modelText,
172
- migrations: {
173
- '11.0.0-modified-at': `
174
- ALTER TABLE "migration"
175
- ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
176
- `,
177
- '11.0.1-modified-at': `
178
- ALTER TABLE "migration lock"
179
- ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
180
- `,
181
- },
182
- },
183
- ],
184
- };
185
- //# sourceMappingURL=migrator.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrator.js","sourceRoot":"","sources":["../../src/migrator/migrator.ts"],"names":[],"mappings":"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"}
@@ -1,278 +0,0 @@
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
- };