@balena/pinejs 16.0.0-build--batch-09b8c466600d7df13e6df3eacabaf463d96f652f-1 → 16.0.0-build-fisehara-update-sbvr-types-b58e72aca3193964afac96c955fde178fe39d077-1
Sign up to get free protection for your applications and to get access to all the features.
- package/.pinejs-cache.json +1 -1
- package/.versionbot/CHANGELOG.yml +2164 -15
- package/CHANGELOG.md +815 -3
- package/Gruntfile.ts +9 -6
- package/README.md +10 -0
- package/build/browser.ts +2 -2
- package/build/config.ts +1 -1
- package/build/module.ts +2 -2
- package/build/server.ts +2 -2
- package/docker-compose.npm-test.yml +21 -3
- package/out/bin/abstract-sql-compiler.js +5 -5
- package/out/bin/abstract-sql-compiler.js.map +1 -1
- package/out/bin/odata-compiler.js +10 -10
- package/out/bin/odata-compiler.js.map +1 -1
- package/out/bin/sbvr-compiler.js +34 -11
- package/out/bin/sbvr-compiler.js.map +1 -1
- package/out/bin/utils.js +25 -2
- package/out/bin/utils.js.map +1 -1
- package/out/config-loader/config-loader.d.ts +4 -2
- package/out/config-loader/config-loader.js +54 -13
- package/out/config-loader/config-loader.js.map +1 -1
- package/out/config-loader/env.d.ts +2 -1
- package/out/config-loader/env.js +5 -2
- package/out/config-loader/env.js.map +1 -1
- package/out/data-server/sbvr-server.d.ts +1 -1
- package/out/data-server/sbvr-server.js +3 -1
- package/out/data-server/sbvr-server.js.map +1 -1
- package/out/database-layer/db.js +40 -14
- package/out/database-layer/db.js.map +1 -1
- package/out/express-emulator/express.js +5 -3
- package/out/express-emulator/express.js.map +1 -1
- package/out/http-transactions/transactions.d.ts +1 -1
- package/out/http-transactions/transactions.js +10 -5
- package/out/http-transactions/transactions.js.map +1 -1
- package/out/migrator/async.js +32 -5
- package/out/migrator/async.js.map +1 -1
- package/out/migrator/sync.d.ts +2 -1
- package/out/migrator/sync.js +29 -3
- package/out/migrator/sync.js.map +1 -1
- package/out/migrator/utils.d.ts +6 -3
- package/out/migrator/utils.js +30 -4
- package/out/migrator/utils.js.map +1 -1
- package/out/odata-metadata/odata-metadata-generator.js +4 -1
- package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
- package/out/passport-pinejs/mount-login-router.d.ts +3 -0
- package/out/passport-pinejs/mount-login-router.js +65 -0
- package/out/passport-pinejs/mount-login-router.js.map +1 -0
- package/out/passport-pinejs/passport-pinejs.d.ts +2 -1
- package/out/passport-pinejs/passport-pinejs.js +28 -2
- package/out/passport-pinejs/passport-pinejs.js.map +1 -1
- package/out/pinejs-session-store/pinejs-session-store.js +30 -7
- package/out/pinejs-session-store/pinejs-session-store.js.map +1 -1
- package/out/sbvr-api/abstract-sql.d.ts +2 -2
- package/out/sbvr-api/abstract-sql.js +35 -9
- package/out/sbvr-api/abstract-sql.js.map +1 -1
- package/out/sbvr-api/cached-compile.js +9 -6
- package/out/sbvr-api/cached-compile.js.map +1 -1
- package/out/sbvr-api/common-types.d.ts +1 -1
- package/out/sbvr-api/control-flow.js +5 -2
- package/out/sbvr-api/control-flow.js.map +1 -1
- package/out/sbvr-api/express-extension.d.ts +10 -7
- package/out/sbvr-api/express-extension.js +1 -0
- package/out/sbvr-api/hooks.d.ts +5 -1
- package/out/sbvr-api/hooks.js +12 -10
- package/out/sbvr-api/hooks.js.map +1 -1
- package/out/sbvr-api/odata-response.d.ts +5 -2
- package/out/sbvr-api/odata-response.js +36 -6
- package/out/sbvr-api/odata-response.js.map +1 -1
- package/out/sbvr-api/permissions.d.ts +6 -7
- package/out/sbvr-api/permissions.js +69 -38
- package/out/sbvr-api/permissions.js.map +1 -1
- package/out/sbvr-api/sbvr-utils.d.ts +21 -10
- package/out/sbvr-api/sbvr-utils.js +128 -124
- package/out/sbvr-api/sbvr-utils.js.map +1 -1
- package/out/sbvr-api/translations.d.ts +2 -2
- package/out/sbvr-api/translations.js +17 -10
- package/out/sbvr-api/translations.js.map +1 -1
- package/out/sbvr-api/uri-parser.d.ts +10 -12
- package/out/sbvr-api/uri-parser.js +46 -19
- package/out/sbvr-api/uri-parser.js.map +1 -1
- package/out/server-glue/global-ext.d.ts +2 -1
- package/out/server-glue/module.d.ts +3 -1
- package/out/server-glue/module.js +40 -13
- package/out/server-glue/module.js.map +1 -1
- package/out/server-glue/sbvr-loader.js.map +1 -1
- package/out/server-glue/server.js +31 -39
- package/out/server-glue/server.js.map +1 -1
- package/out/webresource-handler/handlers/NoopHandler.d.ts +7 -0
- package/out/webresource-handler/handlers/NoopHandler.js +20 -0
- package/out/webresource-handler/handlers/NoopHandler.js.map +1 -0
- package/out/webresource-handler/handlers/S3Handler.d.ts +28 -0
- package/out/webresource-handler/handlers/S3Handler.js +97 -0
- package/out/webresource-handler/handlers/S3Handler.js.map +1 -0
- package/out/webresource-handler/handlers/index.d.ts +2 -0
- package/out/webresource-handler/handlers/index.js +19 -0
- package/out/webresource-handler/handlers/index.js.map +1 -0
- package/out/webresource-handler/index.d.ts +34 -0
- package/out/webresource-handler/index.js +307 -0
- package/out/webresource-handler/index.js.map +1 -0
- package/package.json +68 -62
- package/src/bin/abstract-sql-compiler.ts +7 -9
- package/src/bin/odata-compiler.ts +12 -15
- package/src/bin/sbvr-compiler.ts +14 -18
- package/src/bin/utils.ts +1 -1
- package/src/config-loader/config-loader.ts +44 -10
- package/src/config-loader/env.ts +1 -1
- package/src/data-server/sbvr-server.js +3 -1
- package/src/database-layer/db.ts +23 -19
- package/src/express-emulator/express.js +5 -3
- package/src/extended-sbvr-parser/extended-sbvr-parser.ts +1 -1
- package/src/http-transactions/transactions.js +10 -5
- package/src/migrator/async.ts +7 -6
- package/src/migrator/sync.ts +10 -7
- package/src/migrator/utils.ts +11 -5
- package/src/odata-metadata/odata-metadata-generator.ts +2 -2
- package/src/passport-pinejs/mount-login-router.ts +46 -0
- package/src/passport-pinejs/passport-pinejs.ts +7 -3
- package/src/pinejs-session-store/pinejs-session-store.ts +6 -6
- package/src/sbvr-api/abstract-sql.ts +5 -5
- package/src/sbvr-api/cached-compile.ts +1 -2
- package/src/sbvr-api/common-types.ts +1 -1
- package/src/sbvr-api/control-flow.ts +1 -1
- package/src/sbvr-api/express-extension.ts +12 -8
- package/src/sbvr-api/hooks.ts +11 -11
- package/src/sbvr-api/odata-response.ts +56 -9
- package/src/sbvr-api/permissions.ts +44 -35
- package/src/sbvr-api/sbvr-utils.ts +117 -165
- package/src/sbvr-api/translations.ts +9 -6
- package/src/sbvr-api/uri-parser.ts +25 -30
- package/src/server-glue/global-ext.d.ts +2 -1
- package/src/server-glue/module.ts +8 -2
- package/src/server-glue/sbvr-loader.ts +1 -1
- package/src/server-glue/server.ts +11 -49
- package/src/webresource-handler/handlers/NoopHandler.ts +21 -0
- package/src/webresource-handler/handlers/S3Handler.ts +143 -0
- package/src/webresource-handler/handlers/index.ts +2 -0
- package/src/webresource-handler/index.ts +450 -0
- package/tsconfig.dev.json +2 -1
- package/tsconfig.json +1 -1
- package/typings/lf-to-abstract-sql.d.ts +1 -1
- package/typings/memoizee.d.ts +3 -4
package/src/bin/sbvr-compiler.ts
CHANGED
@@ -1,24 +1,21 @@
|
|
1
1
|
import { version, writeAll, writeSqlModel } from './utils';
|
2
|
-
|
3
|
-
import type * as SbvrUtils from '../sbvr-api/sbvr-utils';
|
4
|
-
|
5
2
|
import { program } from 'commander';
|
6
3
|
import * as fs from 'fs';
|
7
4
|
|
8
5
|
const getSE = (inputFile: string) => fs.readFileSync(inputFile, 'utf8');
|
9
6
|
|
10
|
-
const parse = (inputFile: string, outputFile?: string) => {
|
11
|
-
const { generateLfModel } =
|
12
|
-
require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
|
7
|
+
const parse = async (inputFile: string, outputFile?: string) => {
|
8
|
+
const { generateLfModel } = await import('../sbvr-api/sbvr-utils.js');
|
13
9
|
const seModel = getSE(inputFile);
|
14
10
|
const result = generateLfModel(seModel);
|
15
11
|
const json = JSON.stringify(result, null, 2);
|
16
12
|
writeAll(json, outputFile);
|
17
13
|
};
|
18
14
|
|
19
|
-
const transform = (inputFile: string, outputFile?: string) => {
|
20
|
-
const { generateLfModel, generateAbstractSqlModel } =
|
21
|
-
|
15
|
+
const transform = async (inputFile: string, outputFile?: string) => {
|
16
|
+
const { generateLfModel, generateAbstractSqlModel } = await import(
|
17
|
+
'../sbvr-api/sbvr-utils.js'
|
18
|
+
);
|
22
19
|
const seModel = getSE(inputFile);
|
23
20
|
const lfModel = generateLfModel(seModel);
|
24
21
|
const result = generateAbstractSqlModel(lfModel);
|
@@ -26,9 +23,8 @@ const transform = (inputFile: string, outputFile?: string) => {
|
|
26
23
|
writeAll(json, outputFile);
|
27
24
|
};
|
28
25
|
|
29
|
-
const runCompile = (inputFile: string, outputFile?: string) => {
|
30
|
-
const { generateModels } =
|
31
|
-
require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
|
26
|
+
const runCompile = async (inputFile: string, outputFile?: string) => {
|
27
|
+
const { generateModels } = await import('../sbvr-api/sbvr-utils.js');
|
32
28
|
const seModel = getSE(inputFile);
|
33
29
|
const models = generateModels(
|
34
30
|
{ apiRoot: 'sbvr-compiler', modelText: seModel },
|
@@ -38,16 +34,16 @@ const runCompile = (inputFile: string, outputFile?: string) => {
|
|
38
34
|
writeSqlModel(models.sql, outputFile);
|
39
35
|
};
|
40
36
|
|
41
|
-
const generateTypes = (inputFile: string, outputFile?: string) => {
|
42
|
-
const { generateModels } =
|
43
|
-
require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
|
37
|
+
const generateTypes = async (inputFile: string, outputFile?: string) => {
|
38
|
+
const { generateModels } = await import('../sbvr-api/sbvr-utils.js');
|
44
39
|
const seModel = getSE(inputFile);
|
45
40
|
const models = generateModels(
|
46
41
|
{ apiRoot: 'sbvr-compiler', modelText: seModel },
|
47
42
|
program.opts().engine,
|
48
43
|
);
|
49
|
-
const { abstractSqlToTypescriptTypes } =
|
50
|
-
|
44
|
+
const { abstractSqlToTypescriptTypes } = await import(
|
45
|
+
'@balena/abstract-sql-to-typescript'
|
46
|
+
);
|
51
47
|
const types = abstractSqlToTypescriptTypes(models.abstractSql);
|
52
48
|
|
53
49
|
writeAll(types, outputFile);
|
@@ -93,4 +89,4 @@ if (process.argv.length === 2) {
|
|
93
89
|
program.help();
|
94
90
|
}
|
95
91
|
|
96
|
-
program.
|
92
|
+
void program.parseAsync(process.argv);
|
package/src/bin/utils.ts
CHANGED
@@ -10,7 +10,6 @@ import * as fs from 'fs';
|
|
10
10
|
import * as path from 'path';
|
11
11
|
import '../server-glue/sbvr-loader';
|
12
12
|
|
13
|
-
// tslint:disable:no-var-requires
|
14
13
|
export const { version } = JSON.parse(
|
15
14
|
fs.readFileSync(require.resolve('../../package.json'), 'utf8'),
|
16
15
|
);
|
@@ -83,6 +82,7 @@ export const getAbstractSqlModelFromFile = (
|
|
83
82
|
throw new Error('Unrecognised config file');
|
84
83
|
}
|
85
84
|
const { generateLfModel, generateAbstractSqlModel } =
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
86
86
|
require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
|
87
87
|
let lfModel;
|
88
88
|
try {
|
@@ -12,8 +12,8 @@ import type {
|
|
12
12
|
} from '../sbvr-api/common-types';
|
13
13
|
|
14
14
|
import {
|
15
|
-
Migration,
|
16
|
-
Migrations,
|
15
|
+
type Migration,
|
16
|
+
type Migrations,
|
17
17
|
defaultMigrationCategory,
|
18
18
|
MigrationCategories,
|
19
19
|
isSyncMigration,
|
@@ -21,13 +21,20 @@ import {
|
|
21
21
|
} from '../migrator/utils';
|
22
22
|
|
23
23
|
import * as fs from 'fs';
|
24
|
-
import
|
24
|
+
import _ from 'lodash';
|
25
25
|
import * as path from 'path';
|
26
26
|
|
27
27
|
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
28
28
|
|
29
29
|
import * as permissions from '../sbvr-api/permissions';
|
30
|
-
import {
|
30
|
+
import {
|
31
|
+
getDefaultHandler,
|
32
|
+
getUploaderMiddlware,
|
33
|
+
type WebResourceHandler,
|
34
|
+
setupUploadHooks,
|
35
|
+
setupWebresourceHandler,
|
36
|
+
} from '../webresource-handler';
|
37
|
+
import type { AliasValidNodeType } from '../sbvr-api/translations';
|
31
38
|
|
32
39
|
export type SetupFunction = (
|
33
40
|
app: Express.Application,
|
@@ -64,6 +71,7 @@ export interface User {
|
|
64
71
|
export interface Config {
|
65
72
|
models: Model[];
|
66
73
|
users?: User[];
|
74
|
+
webResourceHandler?: WebResourceHandler;
|
67
75
|
}
|
68
76
|
|
69
77
|
const getOrCreate = async (
|
@@ -195,16 +203,37 @@ export const setup = (app: Express.Application) => {
|
|
195
203
|
);
|
196
204
|
|
197
205
|
const apiRoute = `/${model.apiRoot}/*`;
|
206
|
+
const webResourceHandler =
|
207
|
+
data.webResourceHandler ?? getDefaultHandler();
|
208
|
+
|
209
|
+
setupWebresourceHandler(webResourceHandler);
|
210
|
+
const fileUploadMiddleware =
|
211
|
+
getUploaderMiddlware(webResourceHandler);
|
198
212
|
app
|
199
213
|
.route(apiRoute)
|
200
214
|
.options((_req, res) => res.status(200).end())
|
201
215
|
.get(sbvrUtils.handleODataRequest)
|
202
216
|
.put(sbvrUtils.handleODataRequest)
|
203
|
-
.post(sbvrUtils.handleODataRequest)
|
204
|
-
.patch(sbvrUtils.handleODataRequest)
|
217
|
+
.post(fileUploadMiddleware, sbvrUtils.handleODataRequest)
|
218
|
+
.patch(fileUploadMiddleware, sbvrUtils.handleODataRequest)
|
205
219
|
.merge(sbvrUtils.handleODataRequest)
|
206
220
|
.delete(sbvrUtils.handleODataRequest);
|
207
221
|
|
222
|
+
const loadedModel = sbvrUtils.getModel(model.apiRoot!);
|
223
|
+
if (translateTo == null) {
|
224
|
+
const resourceNames = Object.values(
|
225
|
+
loadedModel.abstractSql.tables,
|
226
|
+
)
|
227
|
+
.filter((table) =>
|
228
|
+
table.fields.some((f) => f.dataType === 'WebResource'),
|
229
|
+
)
|
230
|
+
.map((table) => table.name);
|
231
|
+
|
232
|
+
for (const resource of new Set(resourceNames)) {
|
233
|
+
setupUploadHooks(webResourceHandler, model.apiRoot!, resource);
|
234
|
+
}
|
235
|
+
}
|
236
|
+
|
208
237
|
console.info(
|
209
238
|
'Successfully executed ' + model.modelName + ' model.',
|
210
239
|
);
|
@@ -251,7 +280,8 @@ export const setup = (app: Express.Application) => {
|
|
251
280
|
};
|
252
281
|
const loadConfigFile = async (configPath: string): Promise<Config> => {
|
253
282
|
console.info('Loading config:', configPath);
|
254
|
-
|
283
|
+
const { default: configObj } = await import(configPath);
|
284
|
+
return configObj;
|
255
285
|
};
|
256
286
|
|
257
287
|
const loadApplicationConfig = async (config: string | Config | undefined) => {
|
@@ -350,9 +380,12 @@ export const setup = (app: Express.Application) => {
|
|
350
380
|
switch (fileExtension) {
|
351
381
|
case '.coffee':
|
352
382
|
case '.ts':
|
353
|
-
case '.js':
|
354
|
-
const
|
355
|
-
const migration =
|
383
|
+
case '.js': {
|
384
|
+
const { default: loadedMigration } = await import(filePath);
|
385
|
+
const migration =
|
386
|
+
// This second check for `.default` is because it might be a commonjs file that was compiled from typescript with `export default`
|
387
|
+
// and typescript will add the `.default` but so will the `import()`
|
388
|
+
loadedMigration.default ?? loadedMigration;
|
356
389
|
|
357
390
|
if (
|
358
391
|
!isAsyncMigration(migration) &&
|
@@ -365,6 +398,7 @@ export const setup = (app: Express.Application) => {
|
|
365
398
|
|
366
399
|
assignMigrationWithCategory(migrationKey, migration);
|
367
400
|
break;
|
401
|
+
}
|
368
402
|
case '.sql':
|
369
403
|
if (migrationCategory === MigrationCategories.async) {
|
370
404
|
console.error(
|
package/src/config-loader/env.ts
CHANGED
@@ -50,7 +50,7 @@ export const cache = {
|
|
50
50
|
};
|
51
51
|
|
52
52
|
import { boolVar } from '@balena/env-parsing';
|
53
|
-
import
|
53
|
+
import memoize from 'memoizee';
|
54
54
|
import memoizeWeak = require('memoizee/weak');
|
55
55
|
export const createCache = <T extends (...args: any[]) => any>(
|
56
56
|
cacheName: keyof typeof cache,
|
@@ -48,7 +48,7 @@ const serverIsOnAir = async (_req, _res, next) => {
|
|
48
48
|
};
|
49
49
|
|
50
50
|
/** @type {import('../config-loader/config-loader').Config} */
|
51
|
-
export
|
51
|
+
export const config = {
|
52
52
|
models: [
|
53
53
|
{
|
54
54
|
modelName: 'ui',
|
@@ -267,6 +267,8 @@ export async function setup(app, sbvrUtils, db) {
|
|
267
267
|
try {
|
268
268
|
await tx.executeSql(query);
|
269
269
|
} catch (err) {
|
270
|
+
// TODO: Consider changing this to a custom Error
|
271
|
+
// eslint-disable-next-line no-throw-literal
|
270
272
|
throw [query, err];
|
271
273
|
}
|
272
274
|
}
|
package/src/database-layer/db.ts
CHANGED
@@ -6,7 +6,7 @@ import type { Dictionary, Resolvable } from '../sbvr-api/common-types';
|
|
6
6
|
|
7
7
|
import { Engines } from '@balena/abstract-sql-compiler';
|
8
8
|
import { EventEmitter } from 'eventemitter3';
|
9
|
-
import
|
9
|
+
import _ from 'lodash';
|
10
10
|
import { TypedError } from 'typed-error';
|
11
11
|
import * as env from '../config-loader/env';
|
12
12
|
import { fromCallback, timeout } from '../sbvr-api/control-flow';
|
@@ -168,7 +168,7 @@ const atomicExecuteSql: Database['executeSql'] = async function (
|
|
168
168
|
};
|
169
169
|
|
170
170
|
const asyncTryFn = (fn: () => any) => {
|
171
|
-
Promise.resolve().then(fn);
|
171
|
+
void Promise.resolve().then(fn);
|
172
172
|
};
|
173
173
|
|
174
174
|
type RejectedFunctions = (message: string) => {
|
@@ -189,7 +189,7 @@ const getRejectedFunctions: RejectedFunctions = env.DEBUG
|
|
189
189
|
executeSql: rejectFn,
|
190
190
|
rollback: rejectFn,
|
191
191
|
};
|
192
|
-
|
192
|
+
}
|
193
193
|
: (message) => {
|
194
194
|
const rejectFn = async () => {
|
195
195
|
throw new TransactionClosedError(message);
|
@@ -198,7 +198,7 @@ const getRejectedFunctions: RejectedFunctions = env.DEBUG
|
|
198
198
|
executeSql: rejectFn,
|
199
199
|
rollback: rejectFn,
|
200
200
|
};
|
201
|
-
|
201
|
+
};
|
202
202
|
|
203
203
|
const onEnd: Tx['on'] = (name: string, fn: () => void) => {
|
204
204
|
if (name === 'end') {
|
@@ -215,7 +215,10 @@ class AutomaticClose {
|
|
215
215
|
private automaticCloseTimeout: ReturnType<typeof setTimeout>;
|
216
216
|
private automaticClose: () => void;
|
217
217
|
private pending: false | number = 0;
|
218
|
-
constructor(
|
218
|
+
constructor(
|
219
|
+
tx: Tx,
|
220
|
+
private stackTraceErr?: Error,
|
221
|
+
) {
|
219
222
|
this.automaticClose = () => {
|
220
223
|
console.error(
|
221
224
|
`Transaction still open after ${env.db.timeoutMS}ms without an execute call.`,
|
@@ -223,7 +226,7 @@ class AutomaticClose {
|
|
223
226
|
if (this.stackTraceErr) {
|
224
227
|
console.error(this.stackTraceErr.stack);
|
225
228
|
}
|
226
|
-
tx.rollback();
|
229
|
+
void tx.rollback();
|
227
230
|
};
|
228
231
|
this.automaticCloseTimeout = setTimeout(
|
229
232
|
this.automaticClose,
|
@@ -400,10 +403,13 @@ export abstract class Tx {
|
|
400
403
|
protected abstract _rollback(): Promise<void>;
|
401
404
|
protected abstract _commit(): Promise<void>;
|
402
405
|
|
406
|
+
// TODO: Re-enable the lint rule once eslint properly supports abstract class base implementations
|
403
407
|
public async getTxLevelLock(
|
408
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
404
409
|
_namespaceKey: string,
|
405
410
|
_key: number,
|
406
411
|
_blocking: boolean = true,
|
412
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
407
413
|
): Promise<boolean> {
|
408
414
|
throw new Error(
|
409
415
|
'The getTxLevelLock method is not implemented for the current engine.',
|
@@ -462,7 +468,6 @@ const createTransaction = (createFunc: CreateTransactionFn): TransactionFn => {
|
|
462
468
|
|
463
469
|
let maybePg: typeof Pg | undefined;
|
464
470
|
try {
|
465
|
-
// tslint:disable-next-line:no-var-requires
|
466
471
|
maybePg = require('pg');
|
467
472
|
} catch (e) {
|
468
473
|
// Ignore errors
|
@@ -497,7 +502,7 @@ if (maybePg != null) {
|
|
497
502
|
const p = new pg.Pool(config);
|
498
503
|
if (PG_SCHEMA != null) {
|
499
504
|
p.on('connect', (client) => {
|
500
|
-
client.query({ text: `SET search_path TO "${PG_SCHEMA}"` });
|
505
|
+
void client.query({ text: `SET search_path TO "${PG_SCHEMA}"` });
|
501
506
|
});
|
502
507
|
}
|
503
508
|
p.on('connect', (client) => {
|
@@ -519,6 +524,7 @@ if (maybePg != null) {
|
|
519
524
|
let pool: Pg.Pool;
|
520
525
|
let replica: Pg.Pool;
|
521
526
|
if (typeof connectString === 'string') {
|
527
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
522
528
|
const pgConnectionString: typeof PgConnectionString = require('pg-connection-string');
|
523
529
|
// We have to cast because of the use of null vs undefined
|
524
530
|
const config = pgConnectionString.parse(connectString) as Pg.PoolConfig;
|
@@ -540,12 +546,12 @@ if (maybePg != null) {
|
|
540
546
|
rowCount,
|
541
547
|
rows,
|
542
548
|
}: {
|
543
|
-
rowCount: number;
|
549
|
+
rowCount: number | null;
|
544
550
|
rows: Row[];
|
545
551
|
}): Result => {
|
546
552
|
return {
|
547
553
|
rows,
|
548
|
-
rowsAffected: rowCount,
|
554
|
+
rowsAffected: rowCount ?? 0,
|
549
555
|
insertId: rows?.[0]?.id,
|
550
556
|
};
|
551
557
|
};
|
@@ -617,9 +623,9 @@ if (maybePg != null) {
|
|
617
623
|
);
|
618
624
|
this.db.release();
|
619
625
|
} catch (err: any) {
|
620
|
-
|
621
|
-
this.db.release(
|
622
|
-
throw
|
626
|
+
const errorToReturn = wrapDatabaseError(err);
|
627
|
+
this.db.release(errorToReturn);
|
628
|
+
throw errorToReturn;
|
623
629
|
}
|
624
630
|
}
|
625
631
|
|
@@ -686,13 +692,13 @@ if (maybePg != null) {
|
|
686
692
|
transaction: createTransaction(async (stackTraceErr) => {
|
687
693
|
const client = await pool.connect();
|
688
694
|
const tx = new PostgresTx(client, false, stackTraceErr);
|
689
|
-
tx.executeSql('START TRANSACTION;');
|
695
|
+
void tx.executeSql('START TRANSACTION;');
|
690
696
|
return tx;
|
691
697
|
}),
|
692
698
|
readTransaction: createTransaction(async (stackTraceErr) => {
|
693
699
|
const client = await replica.connect();
|
694
700
|
const tx = new PostgresTx(client, false, stackTraceErr);
|
695
|
-
tx.executeSql('START TRANSACTION READ ONLY;');
|
701
|
+
void tx.executeSql('START TRANSACTION READ ONLY;');
|
696
702
|
return tx.asReadOnly();
|
697
703
|
}),
|
698
704
|
...alwaysExport,
|
@@ -702,7 +708,6 @@ if (maybePg != null) {
|
|
702
708
|
|
703
709
|
let maybeMysql: typeof Mysql | undefined;
|
704
710
|
try {
|
705
|
-
// tslint:disable-next-line:no-var-requires
|
706
711
|
maybeMysql = require('mysql');
|
707
712
|
} catch (e) {
|
708
713
|
// Ignore errors
|
@@ -809,14 +814,14 @@ if (maybeMysql != null) {
|
|
809
814
|
const client = await getConnectionAsync();
|
810
815
|
const close = () => client.release();
|
811
816
|
const tx = new MySqlTx(client, close, false, stackTraceErr);
|
812
|
-
tx.executeSql('START TRANSACTION;');
|
817
|
+
void tx.executeSql('START TRANSACTION;');
|
813
818
|
return tx;
|
814
819
|
}),
|
815
820
|
readTransaction: createTransaction(async (stackTraceErr) => {
|
816
821
|
const client = await getConnectionAsync();
|
817
822
|
const close = () => client.release();
|
818
823
|
const tx = new MySqlTx(client, close, false, stackTraceErr);
|
819
|
-
tx.executeSql('START TRANSACTION READ ONLY;');
|
824
|
+
void tx.executeSql('START TRANSACTION READ ONLY;');
|
820
825
|
return tx.asReadOnly();
|
821
826
|
}),
|
822
827
|
...alwaysExport,
|
@@ -935,7 +940,6 @@ if (typeof window !== 'undefined' && window.openDatabase != null) {
|
|
935
940
|
// allowing us to use async calls within the API.
|
936
941
|
private asyncRecurse = () => {
|
937
942
|
let args: AsyncQuery | undefined;
|
938
|
-
// tslint:disable-next-line no-conditional-assignment
|
939
943
|
while ((args = this.queue.pop())) {
|
940
944
|
console.debug('Running', args[0]);
|
941
945
|
this.tx.executeSql(args[0], args[1], args[2], args[3]);
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
1
|
+
import _ from 'lodash';
|
2
2
|
|
3
3
|
/**
|
4
4
|
* @typedef {Partial<Window> & {GLOBAL_PERMISSIONS?: string[]}} ExtendedWindow
|
@@ -34,10 +34,10 @@ const app = (function () {
|
|
34
34
|
/** @type {string} */ match,
|
35
35
|
/** @type import('express').Handler[] */ ...middleware
|
36
36
|
) {
|
37
|
-
//Strip wildcard
|
37
|
+
// Strip wildcard
|
38
38
|
let paramName;
|
39
39
|
match = match.toLowerCase();
|
40
|
-
const newMatch = match.replace(/[
|
40
|
+
const newMatch = match.replace(/[/*]*$/, '');
|
41
41
|
if (newMatch !== match) {
|
42
42
|
match = newMatch;
|
43
43
|
paramName = '*';
|
@@ -59,6 +59,8 @@ const app = (function () {
|
|
59
59
|
/** @type any */ body = '',
|
60
60
|
) {
|
61
61
|
if (!handlers[method]) {
|
62
|
+
// TODO: Consider changing this to a custom Error
|
63
|
+
// eslint-disable-next-line no-throw-literal
|
62
64
|
throw [404, null, null];
|
63
65
|
}
|
64
66
|
const req = {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { SBVRParser } from '@balena/sbvr-parser';
|
2
|
-
//
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
3
3
|
const Types: string = require('@balena/sbvr-types/Type.sbvr');
|
4
4
|
import { version as sbvrParserVersion } from '@balena/sbvr-parser/package.json';
|
5
5
|
import { version } from '@balena/sbvr-parser/package.json';
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import * as _ from 'lodash';
|
2
1
|
import { odataNameToSqlName } from '@balena/odata-to-abstract-sql';
|
3
|
-
// @ts-
|
2
|
+
// @ts-expect-error b/c TS doesn't know what the result of requiring an sbvr file would be
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
4
4
|
const transactionModel = require('./transaction.sbvr');
|
5
5
|
|
6
6
|
/** @type {import('../config-loader/config-loader').Config} */
|
7
|
-
export
|
7
|
+
export const config = {
|
8
8
|
models: [
|
9
9
|
{
|
10
10
|
apiRoot: 'transaction',
|
@@ -130,7 +130,12 @@ WHERE "conditional field"."conditional resource" = ?;`,
|
|
130
130
|
modelField.dataType === 'ForeignKey' &&
|
131
131
|
Number.isNaN(Number(fieldValue))
|
132
132
|
) {
|
133
|
-
if (
|
133
|
+
if (
|
134
|
+
!Object.prototype.hasOwnProperty.call(
|
135
|
+
placeholders,
|
136
|
+
fieldValue,
|
137
|
+
)
|
138
|
+
) {
|
134
139
|
throw new Error('Cannot resolve placeholder' + fieldValue);
|
135
140
|
} else {
|
136
141
|
try {
|
@@ -170,7 +175,7 @@ WHERE "conditional resource"."transaction" = ?;\
|
|
170
175
|
resolve = $resolve;
|
171
176
|
reject = $reject;
|
172
177
|
});
|
173
|
-
// @ts-
|
178
|
+
// @ts-expect-error we use resolve & reject before they are assigned b/c we treat them as a deferred.
|
174
179
|
placeholders[placeholder] = { promise, resolve, reject };
|
175
180
|
}
|
176
181
|
}
|
package/src/migrator/async.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import type { Tx } from '../database-layer/db';
|
2
2
|
import type { Model } from '../config-loader/config-loader';
|
3
3
|
|
4
|
-
import
|
4
|
+
import _ from 'lodash';
|
5
5
|
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
6
6
|
|
7
7
|
type ApiRootModel = Model & { apiRoot: string };
|
@@ -12,18 +12,18 @@ type InitialMigrationStatus = MigrationStatus &
|
|
12
12
|
>;
|
13
13
|
|
14
14
|
import {
|
15
|
-
MigrationTuple,
|
15
|
+
type MigrationTuple,
|
16
16
|
getExecutedMigrations,
|
17
17
|
migratorEnv,
|
18
18
|
lockMigrations,
|
19
19
|
initMigrationStatus,
|
20
20
|
readMigrationStatus,
|
21
21
|
updateMigrationStatus,
|
22
|
-
RunnableAsyncMigrations,
|
22
|
+
type RunnableAsyncMigrations,
|
23
23
|
getRunnableAsyncMigrations,
|
24
24
|
filterAndSortPendingMigrations,
|
25
|
-
MigrationStatus,
|
26
|
-
BaseAsyncMigration,
|
25
|
+
type MigrationStatus,
|
26
|
+
type BaseAsyncMigration,
|
27
27
|
} from './utils';
|
28
28
|
import { booleanToEnabledString } from '../config-loader/env';
|
29
29
|
|
@@ -220,7 +220,8 @@ const $run = async (
|
|
220
220
|
// skip execution
|
221
221
|
if (migrationState.last_run_time) {
|
222
222
|
const durationSinceLastRun =
|
223
|
-
Date.now() -
|
223
|
+
Date.now() -
|
224
|
+
new Date(migrationState.last_run_time).getTime();
|
224
225
|
const delayMs = migrationState.is_backing_off
|
225
226
|
? initMigrationState.backoffDelayMS
|
226
227
|
: initMigrationState.delayMS;
|
package/src/migrator/sync.ts
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
import {
|
2
|
-
MigrationTuple,
|
2
|
+
type MigrationTuple,
|
3
3
|
MigrationError,
|
4
|
+
type MigrationExecutionResult,
|
4
5
|
setExecutedMigrations,
|
5
6
|
getExecutedMigrations,
|
6
7
|
lockMigrations,
|
7
|
-
RunnableMigrations,
|
8
|
+
type RunnableMigrations,
|
8
9
|
filterAndSortPendingMigrations,
|
9
10
|
getRunnableSyncMigrations,
|
10
11
|
} from './utils';
|
11
12
|
import type { Tx } from '../database-layer/db';
|
12
13
|
import type { Config, Model } from '../config-loader/config-loader';
|
13
14
|
|
14
|
-
import
|
15
|
+
import _ from 'lodash';
|
15
16
|
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
16
17
|
|
17
|
-
//
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
18
19
|
const modelText = require('./migrations.sbvr');
|
19
20
|
|
20
21
|
type ApiRootModel = Model & { apiRoot: string };
|
@@ -45,7 +46,10 @@ export const postRun = async (tx: Tx, model: ApiRootModel): Promise<void> => {
|
|
45
46
|
}
|
46
47
|
};
|
47
48
|
|
48
|
-
export const run = async (
|
49
|
+
export const run = async (
|
50
|
+
tx: Tx,
|
51
|
+
model: ApiRootModel,
|
52
|
+
): Promise<MigrationExecutionResult> => {
|
49
53
|
const { migrations } = model;
|
50
54
|
if (migrations == null || _.isEmpty(migrations)) {
|
51
55
|
return;
|
@@ -58,7 +62,7 @@ const $run = async (
|
|
58
62
|
tx: Tx,
|
59
63
|
model: ApiRootModel,
|
60
64
|
migrations: RunnableMigrations,
|
61
|
-
): Promise<
|
65
|
+
): Promise<MigrationExecutionResult> => {
|
62
66
|
const modelName = model.apiRoot;
|
63
67
|
|
64
68
|
// migrations only run if the model has been executed before,
|
@@ -68,7 +72,6 @@ const $run = async (
|
|
68
72
|
(sbvrUtils.api.migrations?.logger.info ?? console.info)(
|
69
73
|
`First time model '${modelName}' has executed, skipping migrations`,
|
70
74
|
);
|
71
|
-
|
72
75
|
return await setExecutedMigrations(tx, modelName, Object.keys(migrations));
|
73
76
|
}
|
74
77
|
await lockMigrations({ tx, modelName, blocking: true }, async () => {
|
package/src/migrator/utils.ts
CHANGED
@@ -3,7 +3,7 @@ import type { Resolvable } from '../sbvr-api/common-types';
|
|
3
3
|
|
4
4
|
import { createHash } from 'crypto';
|
5
5
|
import { Engines } from '@balena/abstract-sql-compiler';
|
6
|
-
import
|
6
|
+
import _ from 'lodash';
|
7
7
|
import { TypedError } from 'typed-error';
|
8
8
|
import { migrator as migratorEnv } from '../config-loader/env';
|
9
9
|
export { migrator as migratorEnv } from '../config-loader/env';
|
@@ -32,12 +32,12 @@ export type AsyncMigrationFn = (
|
|
32
32
|
sbvrUtils: SbvrUtils,
|
33
33
|
) => Resolvable<number>;
|
34
34
|
|
35
|
-
type AddFn<T extends
|
35
|
+
type AddFn<T extends object, x extends 'sync' | 'async'> = T & {
|
36
36
|
[key in `${x}Fn`]: key extends 'syncFn' ? MigrationFn : AsyncMigrationFn;
|
37
37
|
} & {
|
38
38
|
[key in `${x}Sql`]?: undefined;
|
39
39
|
};
|
40
|
-
type AddSql<T extends
|
40
|
+
type AddSql<T extends object, x extends 'sync' | 'async'> = T & {
|
41
41
|
[key in `${x}Fn`]?: undefined;
|
42
42
|
} & {
|
43
43
|
[key in `${x}Sql`]: string;
|
@@ -105,6 +105,12 @@ export type MigrationStatus = {
|
|
105
105
|
is_backing_off: boolean;
|
106
106
|
};
|
107
107
|
|
108
|
+
export type MigrationExecutionResult =
|
109
|
+
| undefined
|
110
|
+
| {
|
111
|
+
pendingUnsetMigrations: string[];
|
112
|
+
};
|
113
|
+
|
108
114
|
export const getRunnableAsyncMigrations = (
|
109
115
|
$migrations: Migrations,
|
110
116
|
): RunnableAsyncMigrations | undefined => {
|
@@ -270,9 +276,9 @@ export const setExecutedMigrations = async (
|
|
270
276
|
tx: Tx,
|
271
277
|
modelName: string,
|
272
278
|
executedMigrations: string[],
|
273
|
-
): Promise<
|
279
|
+
): Promise<MigrationExecutionResult> => {
|
274
280
|
if (!(await migrationTablesExist(tx))) {
|
275
|
-
return;
|
281
|
+
return { pendingUnsetMigrations: executedMigrations };
|
276
282
|
}
|
277
283
|
|
278
284
|
const stringifiedMigrations = await sbvrUtils.sbvrTypes.JSON.validate(
|
@@ -3,9 +3,9 @@ import type {
|
|
3
3
|
AbstractSqlTable,
|
4
4
|
} from '@balena/abstract-sql-compiler';
|
5
5
|
|
6
|
-
import sbvrTypes, { SbvrType } from '@balena/sbvr-types';
|
6
|
+
import sbvrTypes, { type SbvrType } from '@balena/sbvr-types';
|
7
7
|
|
8
|
-
//
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
9
9
|
const { version }: { version: string } = require('../../package.json');
|
10
10
|
|
11
11
|
const getResourceName = (resourceName: string): string =>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import * as passportPinejs from './passport-pinejs';
|
2
|
+
import type { Express } from 'express';
|
3
|
+
import { PinejsSessionStore } from '../pinejs-session-store/pinejs-session-store';
|
4
|
+
import type { setup } from '../config-loader/config-loader';
|
5
|
+
|
6
|
+
export const mountLoginRouter = async (
|
7
|
+
configLoader: ReturnType<typeof setup>,
|
8
|
+
expressApp: Express,
|
9
|
+
) => {
|
10
|
+
await Promise.all([
|
11
|
+
configLoader.loadConfig(passportPinejs.config),
|
12
|
+
configLoader.loadConfig(PinejsSessionStore.config),
|
13
|
+
]);
|
14
|
+
|
15
|
+
if (
|
16
|
+
typeof process === 'undefined' ||
|
17
|
+
process == null ||
|
18
|
+
!process.env.DISABLE_DEFAULT_AUTH
|
19
|
+
) {
|
20
|
+
expressApp.post(
|
21
|
+
'/login',
|
22
|
+
passportPinejs.login((err, user, req, res) => {
|
23
|
+
if (err) {
|
24
|
+
console.error('Error logging in', err);
|
25
|
+
res.status(500).end();
|
26
|
+
} else if (user === false) {
|
27
|
+
if (req.xhr === true) {
|
28
|
+
res.status(401).end();
|
29
|
+
} else {
|
30
|
+
res.redirect('/login.html');
|
31
|
+
}
|
32
|
+
} else {
|
33
|
+
if (req.xhr === true) {
|
34
|
+
res.status(200).end();
|
35
|
+
} else {
|
36
|
+
res.redirect('/');
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}),
|
40
|
+
);
|
41
|
+
|
42
|
+
expressApp.get('/logout', passportPinejs.logout, (_req, res) => {
|
43
|
+
res.redirect('/');
|
44
|
+
});
|
45
|
+
}
|
46
|
+
};
|