@balena/pinejs 19.7.2-build-renovate-postgres-17-x-38c77839796930404f59a924e2e4573ffdc6405d-1 → 19.7.2-build-esm-ce2a147ed7533c40f456b27f17ac4e559a0f9da8-1
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/.versionbot/CHANGELOG.yml +5 -6
- package/CHANGELOG.md +1 -1
- package/{Gruntfile.ts → Gruntfile.cts} +9 -22
- package/bin/abstract-sql-compiler.js +1 -1
- package/bin/odata-compiler.js +1 -1
- package/bin/sbvr-compiler.js +1 -1
- package/build/{browser.ts → browser.cts} +1 -1
- package/build/{config.ts → config.cts} +5 -1
- package/build/{module.ts → module.cts} +1 -1
- package/build/{server.ts → server.cts} +1 -1
- package/docker-compose.npm-test.yml +1 -1
- package/out/bin/abstract-sql-compiler.js +17 -19
- package/out/bin/abstract-sql-compiler.js.map +1 -1
- package/out/bin/odata-compiler.js +19 -21
- package/out/bin/odata-compiler.js.map +1 -1
- package/out/bin/sbvr-compiler.js +19 -54
- package/out/bin/sbvr-compiler.js.map +1 -1
- package/out/bin/utils.d.ts +3 -3
- package/out/bin/utils.js +16 -53
- package/out/bin/utils.js.map +1 -1
- package/out/config-loader/config-loader.d.ts +6 -6
- package/out/config-loader/config-loader.js +23 -63
- package/out/config-loader/config-loader.js.map +1 -1
- package/out/config-loader/env.d.ts +1 -1
- package/out/config-loader/env.js +24 -66
- package/out/config-loader/env.js.map +1 -1
- package/out/data-server/sbvr-server.d.ts +3 -3
- package/out/data-server/sbvr-server.js +5 -42
- package/out/data-server/sbvr-server.js.map +1 -1
- package/out/database-layer/db.d.ts +3 -3
- package/out/database-layer/db.js +37 -83
- package/out/database-layer/db.js.map +1 -1
- package/out/express-emulator/express.js.map +1 -1
- package/out/extended-sbvr-parser/extended-sbvr-parser.js +8 -11
- package/out/extended-sbvr-parser/extended-sbvr-parser.js.map +1 -1
- package/out/http-transactions/transactions.d.ts +2 -2
- package/out/http-transactions/transactions.js +4 -4
- package/out/http-transactions/transactions.js.map +1 -1
- package/out/migrator/async.d.ts +2 -2
- package/out/migrator/async.js +20 -60
- package/out/migrator/async.js.map +1 -1
- package/out/migrator/migrations.js +1 -2
- package/out/migrator/sync.d.ts +6 -6
- package/out/migrator/sync.js +20 -61
- package/out/migrator/sync.js.map +1 -1
- package/out/migrator/utils.d.ts +4 -4
- package/out/migrator/utils.js +42 -97
- package/out/migrator/utils.js.map +1 -1
- package/out/odata-metadata/odata-metadata-generator.js +7 -14
- package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
- package/out/passport-pinejs/mount-login-router.d.ts +1 -1
- package/out/passport-pinejs/mount-login-router.js +4 -41
- package/out/passport-pinejs/mount-login-router.js.map +1 -1
- package/out/passport-pinejs/passport-pinejs.d.ts +1 -1
- package/out/passport-pinejs/passport-pinejs.js +11 -46
- package/out/passport-pinejs/passport-pinejs.js.map +1 -1
- package/out/pinejs-session-store/pinejs-session-store.d.ts +1 -1
- package/out/pinejs-session-store/pinejs-session-store.js +11 -48
- 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 +22 -65
- package/out/sbvr-api/abstract-sql.js.map +1 -1
- package/out/sbvr-api/cached-compile.js +8 -15
- package/out/sbvr-api/cached-compile.js.map +1 -1
- package/out/sbvr-api/common-types.js +1 -2
- package/out/sbvr-api/control-flow.d.ts +1 -1
- package/out/sbvr-api/control-flow.js +12 -25
- package/out/sbvr-api/control-flow.js.map +1 -1
- package/out/sbvr-api/dev.js +1 -2
- package/out/sbvr-api/errors.d.ts +1 -1
- package/out/sbvr-api/errors.js +47 -95
- package/out/sbvr-api/errors.js.map +1 -1
- package/out/sbvr-api/express-extension.d.ts +2 -2
- package/out/sbvr-api/express-extension.js +1 -2
- package/out/sbvr-api/hooks.d.ts +4 -4
- package/out/sbvr-api/hooks.js +23 -35
- package/out/sbvr-api/hooks.js.map +1 -1
- package/out/sbvr-api/odata-response.d.ts +2 -2
- package/out/sbvr-api/odata-response.js +19 -29
- package/out/sbvr-api/odata-response.js.map +1 -1
- package/out/sbvr-api/permissions.d.ts +8 -8
- package/out/sbvr-api/permissions.js +106 -159
- package/out/sbvr-api/permissions.js.map +1 -1
- package/out/sbvr-api/sbvr-utils.d.ts +38 -16
- package/out/sbvr-api/sbvr-utils.js +222 -285
- package/out/sbvr-api/sbvr-utils.js.map +1 -1
- package/out/sbvr-api/translations.d.ts +1 -1
- package/out/sbvr-api/translations.js +10 -17
- package/out/sbvr-api/translations.js.map +1 -1
- package/out/sbvr-api/uri-parser.d.ts +6 -6
- package/out/sbvr-api/uri-parser.js +39 -84
- package/out/sbvr-api/uri-parser.js.map +1 -1
- package/out/sbvr-api/user.js +1 -2
- package/out/server-glue/global-ext.d.ts +0 -3
- package/out/server-glue/module.d.ts +15 -15
- package/out/server-glue/module.js +20 -59
- package/out/server-glue/module.js.map +1 -1
- package/out/server-glue/sbvr-loader.d.ts +2 -1
- package/out/server-glue/sbvr-loader.js +4 -11
- package/out/server-glue/sbvr-loader.js.map +1 -1
- package/out/server-glue/server.d.ts +2 -2
- package/out/server-glue/server.js +17 -59
- package/out/server-glue/server.js.map +1 -1
- package/out/tasks/common.d.ts +1 -1
- package/out/tasks/common.js +3 -9
- package/out/tasks/common.js.map +1 -1
- package/out/tasks/index.d.ts +8 -8
- package/out/tasks/index.js +25 -63
- package/out/tasks/index.js.map +1 -1
- package/out/tasks/tasks.js +1 -2
- package/out/tasks/worker.d.ts +3 -3
- package/out/tasks/worker.js +14 -51
- package/out/tasks/worker.js.map +1 -1
- package/out/webresource-handler/handlers/NoopHandler.d.ts +1 -1
- package/out/webresource-handler/handlers/NoopHandler.js +1 -5
- package/out/webresource-handler/handlers/NoopHandler.js.map +1 -1
- package/out/webresource-handler/handlers/S3Handler.d.ts +1 -1
- package/out/webresource-handler/handlers/S3Handler.js +17 -24
- package/out/webresource-handler/handlers/S3Handler.js.map +1 -1
- package/out/webresource-handler/handlers/index.d.ts +2 -2
- package/out/webresource-handler/handlers/index.js +2 -18
- package/out/webresource-handler/handlers/index.js.map +1 -1
- package/out/webresource-handler/index.d.ts +1 -1
- package/out/webresource-handler/index.js +35 -85
- package/out/webresource-handler/index.js.map +1 -1
- package/package.json +10 -10
- package/src/bin/abstract-sql-compiler.ts +3 -3
- package/src/bin/odata-compiler.ts +3 -3
- package/src/bin/sbvr-compiler.ts +2 -2
- package/src/bin/utils.ts +20 -15
- package/src/config-loader/config-loader.ts +11 -11
- package/src/config-loader/env.ts +3 -3
- package/src/data-server/sbvr-server.ts +4 -4
- package/src/database-layer/db.ts +18 -11
- package/src/extended-sbvr-parser/extended-sbvr-parser.ts +5 -5
- package/src/http-transactions/transactions.js +4 -4
- package/src/migrator/async.ts +5 -5
- package/src/migrator/sync.ts +8 -8
- package/src/migrator/utils.ts +7 -7
- package/src/odata-metadata/odata-metadata-generator.ts +3 -2
- package/src/passport-pinejs/mount-login-router.ts +3 -3
- package/src/passport-pinejs/passport-pinejs.ts +3 -3
- package/src/pinejs-session-store/pinejs-session-store.ts +4 -4
- package/src/sbvr-api/abstract-sql.ts +5 -5
- package/src/sbvr-api/cached-compile.ts +1 -1
- package/src/sbvr-api/control-flow.ts +1 -1
- package/src/sbvr-api/errors.ts +1 -1
- package/src/sbvr-api/express-extension.ts +2 -2
- package/src/sbvr-api/hooks.ts +5 -5
- package/src/sbvr-api/odata-response.ts +8 -4
- package/src/sbvr-api/permissions.ts +17 -17
- package/src/sbvr-api/sbvr-utils.ts +37 -31
- package/src/sbvr-api/translations.ts +1 -1
- package/src/sbvr-api/uri-parser.ts +9 -9
- package/src/server-glue/global-ext.d.ts +0 -3
- package/src/server-glue/module.ts +19 -19
- package/src/server-glue/sbvr-loader.ts +15 -27
- package/src/server-glue/server.ts +13 -24
- package/src/tasks/common.ts +1 -1
- package/src/tasks/index.ts +14 -14
- package/src/tasks/worker.ts +8 -8
- package/src/webresource-handler/handlers/NoopHandler.ts +5 -1
- package/src/webresource-handler/handlers/S3Handler.ts +1 -1
- package/src/webresource-handler/handlers/index.ts +2 -2
- package/src/webresource-handler/index.ts +8 -8
- package/tsconfig.dev.json +4 -2
- package/tsconfig.json +1 -1
- package/typings/lf-to-abstract-sql.d.ts +2 -2
- package/typings/memoizee.d.ts +1 -1
@@ -1,84 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
}
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
exports.postSetup = exports.setup = exports.executeStandardModels = exports.handleHttpErrors = exports.onHandleHttpError = exports.handleODataRequest = exports.getApiRoot = exports.getModel = exports.getAffectedIds = exports.getAbstractSqlModel = exports.runURI = exports.logger = exports.api = exports.PinejsClient = exports.runRule = exports.getID = exports.deleteModel = exports.postExecuteModels = exports.executeModels = exports.executeModel = exports.generateSqlModel = exports.generateAbstractSqlModel = exports.generateLfModel = exports.validateModel = exports.isModelNew = exports.resolveNavigationResource = exports.resolveSynonym = exports.resolveOdataBind = exports.sbvrTypes = exports.db = exports.addSideEffectHook = exports.addPureHook = void 0;
|
40
|
-
exports.generateModels = generateModels;
|
41
|
-
const lodash_1 = __importDefault(require("lodash"));
|
42
|
-
const cached_compile_1 = require("./cached-compile");
|
43
|
-
const AbstractSQLCompiler = __importStar(require("@balena/abstract-sql-compiler"));
|
44
|
-
const package_json_1 = require("@balena/abstract-sql-compiler/package.json");
|
45
|
-
const LF2AbstractSQL = __importStar(require("@balena/lf-to-abstract-sql"));
|
46
|
-
const odata_to_abstract_sql_1 = require("@balena/odata-to-abstract-sql");
|
47
|
-
const sbvr_types_1 = __importDefault(require("@balena/sbvr-types"));
|
48
|
-
exports.sbvrTypes = sbvr_types_1.default;
|
49
|
-
const deepFreeze = require("deep-freeze");
|
50
|
-
const pinejs_client_core_1 = require("pinejs-client-core");
|
51
|
-
const extended_sbvr_parser_1 = require("../extended-sbvr-parser/extended-sbvr-parser");
|
52
|
-
const asyncMigrator = __importStar(require("../migrator/async"));
|
53
|
-
const syncMigrator = __importStar(require("../migrator/sync"));
|
54
|
-
const odata_metadata_generator_1 = require("../odata-metadata/odata-metadata-generator");
|
55
|
-
const sbvr_loader_1 = require("../server-glue/sbvr-loader");
|
56
|
-
const devModel = (0, sbvr_loader_1.requireSBVR)('./dev.sbvr', require);
|
57
|
-
const permissions = __importStar(require("./permissions"));
|
58
|
-
const errors_1 = require("./errors");
|
59
|
-
const uriParser = __importStar(require("./uri-parser"));
|
60
|
-
const hooks_1 = require("./hooks");
|
61
|
-
var hooks_2 = require("./hooks");
|
62
|
-
Object.defineProperty(exports, "addPureHook", { enumerable: true, get: function () { return hooks_2.addPureHook; } });
|
63
|
-
Object.defineProperty(exports, "addSideEffectHook", { enumerable: true, get: function () { return hooks_2.addSideEffectHook; } });
|
64
|
-
const weak_1 = __importDefault(require("memoizee/weak"));
|
65
|
-
const controlFlow = __importStar(require("./control-flow"));
|
66
|
-
exports.db = undefined;
|
67
|
-
const package_json_2 = require("@balena/lf-to-abstract-sql/package.json");
|
68
|
-
const package_json_3 = require("@balena/sbvr-types/package.json");
|
69
|
-
const abstract_sql_1 = require("./abstract-sql");
|
70
|
-
var abstract_sql_2 = require("./abstract-sql");
|
71
|
-
Object.defineProperty(exports, "resolveOdataBind", { enumerable: true, get: function () { return abstract_sql_2.resolveOdataBind; } });
|
72
|
-
const odataResponse = __importStar(require("./odata-response"));
|
73
|
-
const module_1 = require("../server-glue/module");
|
74
|
-
const translations_1 = require("./translations");
|
75
|
-
const utils_1 = require("../migrator/utils");
|
76
|
-
const LF2AbstractSQLTranslator = LF2AbstractSQL.createTranslator(sbvr_types_1.default);
|
77
|
-
const LF2AbstractSQLTranslatorVersion = `${package_json_2.version}+${package_json_3.version}`;
|
1
|
+
import _ from 'lodash';
|
2
|
+
import { cachedCompile } from './cached-compile.js';
|
3
|
+
import * as AbstractSQLCompiler from '@balena/abstract-sql-compiler';
|
4
|
+
import AbstractSqlCompilerPackage from '@balena/abstract-sql-compiler/package.json' with { type: 'json' };
|
5
|
+
const { version: AbstractSQLCompilerVersion } = AbstractSqlCompilerPackage;
|
6
|
+
import LF2AbstractSQL from '@balena/lf-to-abstract-sql';
|
7
|
+
import { odataNameToSqlName, sqlNameToODataName, } from '@balena/odata-to-abstract-sql';
|
8
|
+
import $sbvrTypes from '@balena/sbvr-types';
|
9
|
+
const { default: sbvrTypes } = $sbvrTypes;
|
10
|
+
import deepFreeze from 'deep-freeze';
|
11
|
+
import { PinejsClientCore } from 'pinejs-client-core';
|
12
|
+
import { ExtendedSBVRParser } from '../extended-sbvr-parser/extended-sbvr-parser.js';
|
13
|
+
import * as asyncMigrator from '../migrator/async.js';
|
14
|
+
import * as syncMigrator from '../migrator/sync.js';
|
15
|
+
import { generateODataMetadata } from '../odata-metadata/odata-metadata-generator.js';
|
16
|
+
import { importSBVR } from '../server-glue/sbvr-loader.js';
|
17
|
+
const devModel = await importSBVR('./dev.sbvr', import.meta);
|
18
|
+
import * as permissions from './permissions.js';
|
19
|
+
import { BadRequestError, ConflictError, HttpError, InternalRequestError, MethodNotAllowedError, ParsingError, PermissionError, PermissionParsingError, SbvrValidationError, SqlCompilationError, statusCodeToError, TranslationError, UnauthorizedError, } from './errors.js';
|
20
|
+
import * as uriParser from './uri-parser.js';
|
21
|
+
import { rollbackRequestHooks, getHooks, runHooks, } from './hooks.js';
|
22
|
+
export { addPureHook, addSideEffectHook, } from './hooks.js';
|
23
|
+
import memoizeWeak from 'memoizee/weak.js';
|
24
|
+
import * as controlFlow from './control-flow.js';
|
25
|
+
export let db = undefined;
|
26
|
+
export { sbvrTypes };
|
27
|
+
import LF2AbstractSQLPackage from '@balena/lf-to-abstract-sql/package.json' with { type: 'json' };
|
28
|
+
const { version: LF2AbstractSQLVersion } = LF2AbstractSQLPackage;
|
29
|
+
import SbvrTypesPackage from '@balena/sbvr-types/package.json' with { type: 'json' };
|
30
|
+
const { version: sbvrTypesVersion } = SbvrTypesPackage;
|
31
|
+
import { compileRequest, getAndCheckBindValues, isRuleAffected, } from './abstract-sql.js';
|
32
|
+
export { resolveOdataBind } from './abstract-sql.js';
|
33
|
+
import * as odataResponse from './odata-response.js';
|
34
|
+
import { env } from '../server-glue/module.js';
|
35
|
+
import { translateAbstractSqlModel } from './translations.js';
|
36
|
+
import { setExecutedMigrations, } from '../migrator/utils.js';
|
37
|
+
const LF2AbstractSQLTranslator = LF2AbstractSQL.createTranslator(sbvrTypes);
|
38
|
+
const LF2AbstractSQLTranslatorVersion = `${LF2AbstractSQLVersion}+${sbvrTypesVersion}`;
|
78
39
|
const models = {};
|
79
|
-
const memoizedResolvedSynonym = (
|
80
|
-
const sqlName =
|
81
|
-
return (
|
40
|
+
const memoizedResolvedSynonym = memoizeWeak((abstractSqlModel, resourceName) => {
|
41
|
+
const sqlName = odataNameToSqlName(resourceName);
|
42
|
+
return _(sqlName)
|
82
43
|
.split('-')
|
83
44
|
.map((namePart) => {
|
84
45
|
const synonym = abstractSqlModel.synonyms[namePart];
|
@@ -89,43 +50,41 @@ const memoizedResolvedSynonym = (0, weak_1.default)((abstractSqlModel, resourceN
|
|
89
50
|
})
|
90
51
|
.join('-');
|
91
52
|
}, { primitive: true });
|
92
|
-
const resolveSynonym = (request) => {
|
93
|
-
const abstractSqlModel =
|
53
|
+
export const resolveSynonym = (request) => {
|
54
|
+
const abstractSqlModel = getAbstractSqlModel(request);
|
94
55
|
return memoizedResolvedSynonym(abstractSqlModel, request.resourceName);
|
95
56
|
};
|
96
|
-
|
97
|
-
const
|
98
|
-
const navigation = (0, odata_to_abstract_sql_1.odataNameToSqlName)(navigationName)
|
57
|
+
const memoizedResolveNavigationResource = memoizeWeak((abstractSqlModel, resourceName, navigationName) => {
|
58
|
+
const navigation = odataNameToSqlName(navigationName)
|
99
59
|
.split('-')
|
100
60
|
.flatMap((namePart) => memoizedResolvedSynonym(abstractSqlModel, namePart).split('-'));
|
101
61
|
navigation.push('$');
|
102
62
|
const resolvedResourceName = memoizedResolvedSynonym(abstractSqlModel, resourceName);
|
103
|
-
const mapping =
|
63
|
+
const mapping = _.get(abstractSqlModel.relationships[resolvedResourceName], navigation);
|
104
64
|
if (mapping == null) {
|
105
65
|
throw new Error(`Cannot navigate from '${resourceName}' to '${navigationName}'`);
|
106
66
|
}
|
107
67
|
if (mapping.length < 2) {
|
108
68
|
throw new Error(`'${resourceName}' to '${navigationName}' is a field not a navigation`);
|
109
69
|
}
|
110
|
-
return
|
70
|
+
return sqlNameToODataName(abstractSqlModel.tables[mapping[1][0]].name);
|
111
71
|
}, { primitive: true });
|
112
|
-
const resolveNavigationResource = (request, navigationName) => {
|
113
|
-
const abstractSqlModel =
|
72
|
+
export const resolveNavigationResource = (request, navigationName) => {
|
73
|
+
const abstractSqlModel = getAbstractSqlModel(request);
|
114
74
|
return memoizedResolveNavigationResource(abstractSqlModel, request.resourceName, navigationName);
|
115
75
|
};
|
116
|
-
exports.resolveNavigationResource = resolveNavigationResource;
|
117
76
|
const prettifyConstraintError = (err, request) => {
|
118
|
-
if (err instanceof
|
77
|
+
if (err instanceof db.ConstraintError) {
|
119
78
|
let keyMatches = null;
|
120
79
|
let violatedConstraintInfo;
|
121
|
-
if (err instanceof
|
122
|
-
switch (
|
80
|
+
if (err instanceof db.UniqueConstraintError) {
|
81
|
+
switch (db.engine) {
|
123
82
|
case 'mysql':
|
124
83
|
keyMatches =
|
125
84
|
/ER_DUP_ENTRY: Duplicate entry '.*?[^\\]' for key '(.*?[^\\])'/.exec(err.message);
|
126
85
|
break;
|
127
86
|
case 'postgres': {
|
128
|
-
const resourceName =
|
87
|
+
const resourceName = resolveSynonym(request);
|
129
88
|
const abstractSqlModel = getFinalAbstractSqlModel(request);
|
130
89
|
const table = abstractSqlModel.tables[resourceName];
|
131
90
|
keyMatches = new RegExp('"' + table.name + '_(.*?)_key"').exec(err.message);
|
@@ -143,30 +102,30 @@ const prettifyConstraintError = (err, request) => {
|
|
143
102
|
}
|
144
103
|
if (keyMatches != null) {
|
145
104
|
const columns = keyMatches[1].split('_');
|
146
|
-
throw new
|
147
|
-
columns.map(
|
105
|
+
throw new db.UniqueConstraintError('"' +
|
106
|
+
columns.map(sqlNameToODataName).join('" and "') +
|
148
107
|
'" must be unique.');
|
149
108
|
}
|
150
109
|
if (violatedConstraintInfo != null) {
|
151
110
|
const { table, name: violatedConstraintName } = violatedConstraintInfo;
|
152
111
|
const violatedUniqueIndex = table.indexes.find((idx) => idx.name === violatedConstraintName);
|
153
112
|
if (violatedUniqueIndex?.description != null) {
|
154
|
-
throw new
|
113
|
+
throw new BadRequestError(violatedUniqueIndex.description);
|
155
114
|
}
|
156
115
|
}
|
157
|
-
throw new
|
116
|
+
throw new db.UniqueConstraintError('Unique key constraint violated');
|
158
117
|
}
|
159
|
-
if (err instanceof
|
160
|
-
throw new
|
118
|
+
if (err instanceof db.ExclusionConstraintError) {
|
119
|
+
throw new db.ExclusionConstraintError('Exclusion constraint violated');
|
161
120
|
}
|
162
|
-
if (err instanceof
|
163
|
-
switch (
|
121
|
+
if (err instanceof db.ForeignKeyConstraintError) {
|
122
|
+
switch (db.engine) {
|
164
123
|
case 'mysql':
|
165
124
|
keyMatches =
|
166
125
|
/ER_ROW_IS_REFERENCED_: Cannot delete or update a parent row: a foreign key constraint fails \(".*?"\.(".*?").*/.exec(err.message);
|
167
126
|
break;
|
168
127
|
case 'postgres': {
|
169
|
-
const resourceName =
|
128
|
+
const resourceName = resolveSynonym(request);
|
170
129
|
const abstractSqlModel = getFinalAbstractSqlModel(request);
|
171
130
|
const tableName = abstractSqlModel.tables[resourceName].name;
|
172
131
|
keyMatches = new RegExp('"' +
|
@@ -183,16 +142,16 @@ const prettifyConstraintError = (err, request) => {
|
|
183
142
|
}
|
184
143
|
}
|
185
144
|
if (keyMatches == null) {
|
186
|
-
throw new
|
145
|
+
throw new db.ForeignKeyConstraintError('Foreign key constraint violated');
|
187
146
|
}
|
188
|
-
throw new
|
147
|
+
throw new db.ForeignKeyConstraintError('Data is referenced by ' + sqlNameToODataName(keyMatches[1]) + '.');
|
189
148
|
}
|
190
|
-
if (err instanceof
|
191
|
-
const resourceName =
|
149
|
+
if (err instanceof db.CheckConstraintError) {
|
150
|
+
const resourceName = resolveSynonym(request);
|
192
151
|
const abstractSqlModel = getFinalAbstractSqlModel(request);
|
193
152
|
const table = abstractSqlModel.tables[resourceName];
|
194
153
|
if (table.checks) {
|
195
|
-
switch (
|
154
|
+
switch (db.engine) {
|
196
155
|
case 'postgres':
|
197
156
|
keyMatches = new RegExp('new row for relation "' +
|
198
157
|
table.name +
|
@@ -204,17 +163,17 @@ const prettifyConstraintError = (err, request) => {
|
|
204
163
|
const checkName = keyMatches[1];
|
205
164
|
const check = table.checks.find((c) => c.name === checkName);
|
206
165
|
if (check?.description != null) {
|
207
|
-
throw new
|
166
|
+
throw new BadRequestError(check.description);
|
208
167
|
}
|
209
168
|
}
|
210
|
-
throw new
|
169
|
+
throw new BadRequestError('Check constraint violated');
|
211
170
|
}
|
212
171
|
err.message = 'Constraint failed';
|
213
172
|
throw err;
|
214
173
|
}
|
215
174
|
};
|
216
175
|
let cachedIsModelNew;
|
217
|
-
const isModelNew = async (tx, modelName) => {
|
176
|
+
export const isModelNew = async (tx, modelName) => {
|
218
177
|
const result = await tx.tableList("name = 'model'");
|
219
178
|
if (result.rows.length === 0) {
|
220
179
|
return true;
|
@@ -225,12 +184,11 @@ const isModelNew = async (tx, modelName) => {
|
|
225
184
|
}
|
226
185
|
return !cachedIsModelNew.has(modelName);
|
227
186
|
};
|
228
|
-
exports.isModelNew = isModelNew;
|
229
187
|
const bindsForAffectedIds = (bindings, request) => {
|
230
188
|
if (request?.affectedIds == null) {
|
231
189
|
return {};
|
232
190
|
}
|
233
|
-
const tableName =
|
191
|
+
const tableName = getAbstractSqlModel(request).tables[resolveSynonym(request)].name;
|
234
192
|
const isDelete = request.method === 'DELETE';
|
235
193
|
const odataBinds = {};
|
236
194
|
for (const bind of bindings) {
|
@@ -249,49 +207,45 @@ const bindsForAffectedIds = (bindings, request) => {
|
|
249
207
|
}
|
250
208
|
return odataBinds;
|
251
209
|
};
|
252
|
-
const validateModel = async (tx, modelName, request) => {
|
210
|
+
export const validateModel = async (tx, modelName, request) => {
|
253
211
|
const { sql } = models[modelName];
|
254
212
|
if (!sql) {
|
255
213
|
throw new Error(`Tried to validate a virtual model: '${modelName}'`);
|
256
214
|
}
|
257
215
|
await Promise.all(sql.rules.map(async (rule) => {
|
258
|
-
if (!
|
216
|
+
if (!isRuleAffected(rule, request)) {
|
259
217
|
return;
|
260
218
|
}
|
261
|
-
const values = await
|
219
|
+
const values = await getAndCheckBindValues({
|
262
220
|
vocabulary: modelName,
|
263
221
|
odataBinds: bindsForAffectedIds(rule.bindings, request),
|
264
222
|
values: {},
|
265
|
-
engine:
|
223
|
+
engine: db.engine,
|
266
224
|
}, rule.bindings);
|
267
225
|
const result = await tx.executeSql(rule.sql, values);
|
268
226
|
const v = result.rows[0].result;
|
269
227
|
if (v === false || v === 0 || v === '0') {
|
270
|
-
throw new
|
228
|
+
throw new SbvrValidationError(rule.structuredEnglish);
|
271
229
|
}
|
272
230
|
}));
|
273
231
|
};
|
274
|
-
|
275
|
-
const
|
276
|
-
|
277
|
-
|
278
|
-
exports.generateAbstractSqlModel = generateAbstractSqlModel;
|
279
|
-
const generateSqlModel = (abstractSql, targetDatabaseEngine) => (0, cached_compile_1.cachedCompile)('sqlModel', package_json_1.version + '+' + targetDatabaseEngine, abstractSql, () => AbstractSQLCompiler[targetDatabaseEngine].compileSchema(abstractSql));
|
280
|
-
exports.generateSqlModel = generateSqlModel;
|
281
|
-
function generateModels(model, targetDatabaseEngine) {
|
232
|
+
export const generateLfModel = (seModel) => cachedCompile('lfModel', ExtendedSBVRParser.version, seModel, () => ExtendedSBVRParser.matchAll(seModel, 'Process'));
|
233
|
+
export const generateAbstractSqlModel = (lfModel) => cachedCompile('abstractSqlModel', LF2AbstractSQLTranslatorVersion, lfModel, () => LF2AbstractSQLTranslator(lfModel, 'Process'));
|
234
|
+
export const generateSqlModel = (abstractSql, targetDatabaseEngine) => cachedCompile('sqlModel', AbstractSQLCompilerVersion + '+' + targetDatabaseEngine, abstractSql, () => AbstractSQLCompiler[targetDatabaseEngine].compileSchema(abstractSql));
|
235
|
+
export function generateModels(model, targetDatabaseEngine) {
|
282
236
|
const { apiRoot: vocab, modelText: se, translateTo, translations } = model;
|
283
237
|
let { abstractSql: maybeAbstractSql } = model;
|
284
238
|
let lf;
|
285
239
|
if (se) {
|
286
240
|
try {
|
287
|
-
lf =
|
241
|
+
lf = generateLfModel(se);
|
288
242
|
}
|
289
243
|
catch (e) {
|
290
244
|
console.error(`Error parsing model '${vocab}':`, e);
|
291
245
|
throw new Error(`Error parsing model '${vocab}': ${e}`);
|
292
246
|
}
|
293
247
|
try {
|
294
|
-
maybeAbstractSql =
|
248
|
+
maybeAbstractSql = generateAbstractSqlModel(lf);
|
295
249
|
}
|
296
250
|
catch (e) {
|
297
251
|
console.error(`Error translating model '${vocab}':`, e);
|
@@ -299,18 +253,18 @@ function generateModels(model, targetDatabaseEngine) {
|
|
299
253
|
}
|
300
254
|
}
|
301
255
|
const abstractSql = maybeAbstractSql;
|
302
|
-
const odataMetadata =
|
256
|
+
const odataMetadata = cachedCompile('metadata', generateODataMetadata.version, { vocab, abstractSqlModel: abstractSql }, () => generateODataMetadata(vocab, abstractSql));
|
303
257
|
let sql;
|
304
258
|
let resourceRenames;
|
305
259
|
if (translateTo != null) {
|
306
|
-
resourceRenames =
|
260
|
+
resourceRenames = translateAbstractSqlModel(abstractSql, models[translateTo].abstractSql, model.apiRoot, translateTo, translations);
|
307
261
|
}
|
308
262
|
else {
|
309
263
|
for (const [key, table] of Object.entries(abstractSql.tables)) {
|
310
264
|
abstractSql.tables[`${key}$${model.apiRoot}`] = { ...table };
|
311
265
|
}
|
312
266
|
try {
|
313
|
-
sql =
|
267
|
+
sql = generateSqlModel(abstractSql, targetDatabaseEngine);
|
314
268
|
}
|
315
269
|
catch (e) {
|
316
270
|
console.error(`Error compiling model '${vocab}':`, e);
|
@@ -328,18 +282,17 @@ function generateModels(model, targetDatabaseEngine) {
|
|
328
282
|
odataMetadata,
|
329
283
|
};
|
330
284
|
}
|
331
|
-
const executeModel = (tx, model) =>
|
332
|
-
|
333
|
-
const executeModels = async (tx, execModels) => {
|
285
|
+
export const executeModel = (tx, model) => executeModels(tx, [model]);
|
286
|
+
export const executeModels = async (tx, execModels) => {
|
334
287
|
try {
|
335
288
|
const compiledModels = await Promise.all(execModels.map(async (model) => {
|
336
289
|
const { apiRoot } = model;
|
337
290
|
const migrationExecutionResult = await syncMigrator.run(tx, model);
|
338
|
-
const compiledModel = generateModels(model,
|
291
|
+
const compiledModel = generateModels(model, db.engine);
|
339
292
|
if (compiledModel.sql) {
|
340
293
|
for (const createStatement of compiledModel.sql.createSchema) {
|
341
294
|
const promise = tx.executeSql(createStatement);
|
342
|
-
if (
|
295
|
+
if (db.engine === 'websql') {
|
343
296
|
promise.catch((err) => {
|
344
297
|
console.warn("Ignoring errors in the create table statements for websql as it doesn't support CREATE IF NOT EXISTS", err);
|
345
298
|
});
|
@@ -362,19 +315,19 @@ const executeModels = async (tx, execModels) => {
|
|
362
315
|
},
|
363
316
|
};
|
364
317
|
if (compiledModel.sql) {
|
365
|
-
await
|
318
|
+
await validateModel(tx, apiRoot);
|
366
319
|
}
|
367
|
-
|
368
|
-
|
320
|
+
api[apiRoot] = new PinejsClient('/' + apiRoot + '/');
|
321
|
+
logger[apiRoot] = { ...console };
|
369
322
|
if (model.logging != null) {
|
370
323
|
const defaultSetting = model.logging?.default ?? true;
|
371
|
-
const log =
|
324
|
+
const log = logger[apiRoot];
|
372
325
|
for (const k of Object.keys(model.logging)) {
|
373
326
|
const key = k;
|
374
327
|
if (key !== 'Console' &&
|
375
328
|
typeof log[key] === 'function' &&
|
376
329
|
!(model.logging?.[key] ?? defaultSetting)) {
|
377
|
-
log[key] =
|
330
|
+
log[key] = _.noop;
|
378
331
|
}
|
379
332
|
}
|
380
333
|
}
|
@@ -383,7 +336,7 @@ const executeModels = async (tx, execModels) => {
|
|
383
336
|
await Promise.all(compiledModels.map(async (model) => {
|
384
337
|
const updateModel = async (modelType) => {
|
385
338
|
if (model[modelType] == null) {
|
386
|
-
await
|
339
|
+
await api.dev.delete({
|
387
340
|
resource: 'model',
|
388
341
|
passthrough: {
|
389
342
|
tx,
|
@@ -398,7 +351,7 @@ const executeModels = async (tx, execModels) => {
|
|
398
351
|
});
|
399
352
|
return;
|
400
353
|
}
|
401
|
-
const result = await
|
354
|
+
const result = await api.dev.get({
|
402
355
|
resource: 'model',
|
403
356
|
passthrough: {
|
404
357
|
tx,
|
@@ -430,7 +383,7 @@ const executeModels = async (tx, execModels) => {
|
|
430
383
|
else {
|
431
384
|
uri += '?returnResource=false';
|
432
385
|
}
|
433
|
-
return await
|
386
|
+
return await runURI(method, uri, body, tx, permissions.root);
|
434
387
|
};
|
435
388
|
await Promise.all(['se', 'lf', 'abstractSql', 'sql', 'odataMetadata'].map(updateModel));
|
436
389
|
}));
|
@@ -445,28 +398,26 @@ const executeModels = async (tx, execModels) => {
|
|
445
398
|
throw err;
|
446
399
|
}
|
447
400
|
};
|
448
|
-
|
449
|
-
const postExecuteModels = async (tx) => {
|
401
|
+
export const postExecuteModels = async (tx) => {
|
450
402
|
for (const modelKey of Object.keys(models)) {
|
451
403
|
const pendingToSetExecutedMigrations = models[modelKey]?.modelExecutionResult?.migrationExecutionResult
|
452
404
|
?.pendingUnsetMigrations;
|
453
405
|
if (pendingToSetExecutedMigrations != null) {
|
454
|
-
await
|
406
|
+
await setExecutedMigrations(tx, modelKey, pendingToSetExecutedMigrations);
|
455
407
|
}
|
456
408
|
}
|
457
409
|
};
|
458
|
-
exports.postExecuteModels = postExecuteModels;
|
459
410
|
const cleanupModel = (vocab) => {
|
460
411
|
delete models[vocab];
|
461
|
-
delete
|
412
|
+
delete api[vocab];
|
462
413
|
};
|
463
|
-
const deleteModel = async (vocabulary) => {
|
414
|
+
export const deleteModel = async (vocabulary) => {
|
464
415
|
const { sql } = models[vocabulary];
|
465
416
|
if (sql) {
|
466
|
-
await
|
417
|
+
await db.transaction(async (tx) => {
|
467
418
|
const dropStatements = sql.dropSchema.map((dropStatement) => tx.executeSql(dropStatement));
|
468
419
|
await Promise.all(dropStatements.concat([
|
469
|
-
|
420
|
+
api.dev.delete({
|
470
421
|
resource: 'model',
|
471
422
|
passthrough: {
|
472
423
|
tx,
|
@@ -483,10 +434,9 @@ const deleteModel = async (vocabulary) => {
|
|
483
434
|
}
|
484
435
|
cleanupModel(vocabulary);
|
485
436
|
};
|
486
|
-
exports.deleteModel = deleteModel;
|
487
437
|
const isWhereNode = (x) => x[0] === 'Where';
|
488
438
|
const isEqualsNode = (x) => x[0] === 'Equals';
|
489
|
-
const getID = (vocab, request) => {
|
439
|
+
export const getID = (vocab, request) => {
|
490
440
|
if (request.abstractSqlQuery == null) {
|
491
441
|
throw new Error('Can only get the id if an abstractSqlQuery is provided');
|
492
442
|
}
|
@@ -507,23 +457,22 @@ const getID = (vocab, request) => {
|
|
507
457
|
}
|
508
458
|
return 0;
|
509
459
|
};
|
510
|
-
|
511
|
-
exports.runRule = (() => {
|
460
|
+
export const runRule = (() => {
|
512
461
|
const LF2AbstractSQLPrepHack = LF2AbstractSQL.LF2AbstractSQLPrep._extend({
|
513
462
|
CardinalityOptimisation() {
|
514
463
|
this._pred(false);
|
515
464
|
},
|
516
465
|
});
|
517
466
|
const translator = LF2AbstractSQL.LF2AbstractSQL.createInstance();
|
518
|
-
translator.addTypes(
|
467
|
+
translator.addTypes(sbvrTypes);
|
519
468
|
return async (vocab, rule) => {
|
520
469
|
const seModel = models[vocab].se;
|
521
|
-
const log =
|
470
|
+
const log = logger[vocab];
|
522
471
|
let lfModel;
|
523
472
|
let slfModel;
|
524
473
|
let abstractSqlModel;
|
525
474
|
try {
|
526
|
-
lfModel =
|
475
|
+
lfModel = ExtendedSBVRParser.matchAll(seModel + '\nRule: ' + rule, 'Process');
|
527
476
|
}
|
528
477
|
catch (e) {
|
529
478
|
log.error('Error parsing rule', rule, e);
|
@@ -550,7 +499,7 @@ exports.runRule = (() => {
|
|
550
499
|
resourceName = ruleLF[1][1][1][2][1];
|
551
500
|
}
|
552
501
|
let fetchingViolators = false;
|
553
|
-
const ruleAbs =
|
502
|
+
const ruleAbs = _.last(abstractSqlModel.rules);
|
554
503
|
if (ruleAbs == null) {
|
555
504
|
throw new Error('Unable to generate rule');
|
556
505
|
}
|
@@ -587,21 +536,21 @@ exports.runRule = (() => {
|
|
587
536
|
}
|
588
537
|
return ['Select', '*'];
|
589
538
|
});
|
590
|
-
const compiledRule = AbstractSQLCompiler[
|
539
|
+
const compiledRule = AbstractSQLCompiler[db.engine].compileRule(ruleBody);
|
591
540
|
if (Array.isArray(compiledRule)) {
|
592
541
|
throw new Error('Unexpected query generated');
|
593
542
|
}
|
594
|
-
const values = await
|
543
|
+
const values = await getAndCheckBindValues({
|
595
544
|
vocabulary: vocab,
|
596
545
|
odataBinds: [],
|
597
546
|
values: {},
|
598
|
-
engine:
|
547
|
+
engine: db.engine,
|
599
548
|
}, compiledRule.bindings);
|
600
|
-
const result = await
|
549
|
+
const result = await db.executeSql(compiledRule.query, values);
|
601
550
|
const table = models[vocab].abstractSql.tables[resourceName];
|
602
|
-
const odataIdField =
|
551
|
+
const odataIdField = sqlNameToODataName(table.idField);
|
603
552
|
let ids = result.rows.map((row) => row[table.idField]);
|
604
|
-
ids =
|
553
|
+
ids = _.uniq(ids);
|
605
554
|
ids = ids.map((id) => odataIdField + ' eq ' + id);
|
606
555
|
let filter;
|
607
556
|
if (ids.length > 0) {
|
@@ -610,10 +559,10 @@ exports.runRule = (() => {
|
|
610
559
|
else {
|
611
560
|
filter = '0 eq 1';
|
612
561
|
}
|
613
|
-
const odataResult = (await
|
562
|
+
const odataResult = (await runURI('GET', '/' +
|
614
563
|
vocab +
|
615
564
|
'/' +
|
616
|
-
|
565
|
+
sqlNameToODataName(table.resourceName) +
|
617
566
|
'?$filter=' +
|
618
567
|
filter, undefined, undefined, permissions.rootRead));
|
619
568
|
odataResult.__formulationType = formulationType;
|
@@ -621,9 +570,9 @@ exports.runRule = (() => {
|
|
621
570
|
return odataResult;
|
622
571
|
};
|
623
572
|
})();
|
624
|
-
class PinejsClient extends
|
573
|
+
export class PinejsClient extends PinejsClientCore {
|
625
574
|
async _request({ method, url, body, tx, req, custom, }) {
|
626
|
-
return (await
|
575
|
+
return (await runURI(method, url, body, tx, req, custom));
|
627
576
|
}
|
628
577
|
post(params) {
|
629
578
|
return super.post(params);
|
@@ -657,13 +606,12 @@ class PinejsClient extends pinejs_client_core_1.PinejsClientCore {
|
|
657
606
|
return `${authBase}?${compiledFilter}`;
|
658
607
|
}
|
659
608
|
}
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
const runURI = async (method, uri, body = {}, tx, req, custom) => {
|
609
|
+
export const api = {};
|
610
|
+
export const logger = {};
|
611
|
+
export const runURI = async (method, uri, body = {}, tx, req, custom) => {
|
664
612
|
const [, apiRoot] = uri.split('/', 2);
|
665
613
|
if (apiRoot == null || models[apiRoot] == null) {
|
666
|
-
throw new
|
614
|
+
throw new InternalRequestError();
|
667
615
|
}
|
668
616
|
let user;
|
669
617
|
let apiKey;
|
@@ -681,13 +629,13 @@ const runURI = async (method, uri, body = {}, tx, req, custom) => {
|
|
681
629
|
permissions: [],
|
682
630
|
};
|
683
631
|
}
|
684
|
-
|
632
|
+
_.forEach(body, (v, k) => {
|
685
633
|
if (v === undefined) {
|
686
634
|
delete body[k];
|
687
635
|
}
|
688
636
|
});
|
689
637
|
const emulatedReq = {
|
690
|
-
on:
|
638
|
+
on: _.noop,
|
691
639
|
custom,
|
692
640
|
user,
|
693
641
|
apiKey,
|
@@ -700,30 +648,28 @@ const runURI = async (method, uri, body = {}, tx, req, custom) => {
|
|
700
648
|
};
|
701
649
|
const { promise } = runODataRequest(emulatedReq, apiRoot);
|
702
650
|
const [response] = await promise;
|
703
|
-
if (
|
651
|
+
if (_.isError(response)) {
|
704
652
|
throw response;
|
705
653
|
}
|
706
654
|
const { body: responseBody, statusCode, headers } = response;
|
707
655
|
if (statusCode != null && statusCode >= 400) {
|
708
|
-
const ErrorClass =
|
656
|
+
const ErrorClass = statusCodeToError[statusCode];
|
709
657
|
if (ErrorClass != null) {
|
710
658
|
throw new ErrorClass(undefined, responseBody, headers);
|
711
659
|
}
|
712
|
-
throw new
|
660
|
+
throw new HttpError(statusCode, undefined, responseBody, headers);
|
713
661
|
}
|
714
662
|
return responseBody;
|
715
663
|
};
|
716
|
-
|
717
|
-
const getAbstractSqlModel = (request) => {
|
664
|
+
export const getAbstractSqlModel = (request) => {
|
718
665
|
return (request.abstractSqlModel ??= models[request.vocabulary].abstractSql);
|
719
666
|
};
|
720
|
-
exports.getAbstractSqlModel = getAbstractSqlModel;
|
721
667
|
const getFinalAbstractSqlModel = (request) => {
|
722
|
-
const finalModel =
|
668
|
+
const finalModel = _.last(request.translateVersions);
|
723
669
|
return (request.finalAbstractSqlModel ??= models[finalModel].abstractSql);
|
724
670
|
};
|
725
|
-
const getIdField = (request) => getFinalAbstractSqlModel(request).tables[
|
726
|
-
const getAffectedIds = async (args) => {
|
671
|
+
const getIdField = (request) => getFinalAbstractSqlModel(request).tables[resolveSynonym(request)].idField;
|
672
|
+
export const getAffectedIds = async (args) => {
|
727
673
|
const { request } = args;
|
728
674
|
if (request.affectedIds) {
|
729
675
|
return request.affectedIds;
|
@@ -736,7 +682,6 @@ const getAffectedIds = async (args) => {
|
|
736
682
|
delete request.pendingAffectedIds;
|
737
683
|
return request.affectedIds;
|
738
684
|
};
|
739
|
-
exports.getAffectedIds = getAffectedIds;
|
740
685
|
const $getAffectedIds = async ({ req, request, tx, }) => {
|
741
686
|
if (!['PATCH', 'DELETE'].includes(request.method)) {
|
742
687
|
throw new Error('Can only call `getAffectedIds` with PATCH/DELETE requests');
|
@@ -748,8 +693,8 @@ const $getAffectedIds = async ({ req, request, tx, }) => {
|
|
748
693
|
parsedRequest.engine = request.engine;
|
749
694
|
parsedRequest.translateVersions = request.translateVersions;
|
750
695
|
let affectedRequest = parsedRequest;
|
751
|
-
const abstractSqlModel =
|
752
|
-
const resourceName =
|
696
|
+
const abstractSqlModel = getAbstractSqlModel(affectedRequest);
|
697
|
+
const resourceName = resolveSynonym(affectedRequest);
|
753
698
|
const resourceTable = abstractSqlModel.tables[resourceName];
|
754
699
|
if (resourceTable == null) {
|
755
700
|
throw new Error('Unknown resource: ' + affectedRequest.resourceName);
|
@@ -763,7 +708,7 @@ const $getAffectedIds = async ({ req, request, tx, }) => {
|
|
763
708
|
await permissions.addPermissions(req, affectedRequest);
|
764
709
|
affectedRequest.method = 'GET';
|
765
710
|
affectedRequest = uriParser.translateUri(affectedRequest);
|
766
|
-
affectedRequest =
|
711
|
+
affectedRequest = compileRequest(affectedRequest);
|
767
712
|
let result;
|
768
713
|
if (tx != null) {
|
769
714
|
result = await runQuery(tx, affectedRequest);
|
@@ -773,18 +718,17 @@ const $getAffectedIds = async ({ req, request, tx, }) => {
|
|
773
718
|
}
|
774
719
|
return result.rows.map((row) => row[idField]);
|
775
720
|
};
|
776
|
-
const getModel = (vocabulary) => {
|
721
|
+
export const getModel = (vocabulary) => {
|
777
722
|
return models[vocabulary];
|
778
723
|
};
|
779
|
-
exports.getModel = getModel;
|
780
724
|
const runODataRequest = (req, vocabulary) => {
|
781
|
-
if (
|
782
|
-
|
725
|
+
if (env.DEBUG) {
|
726
|
+
logger[vocabulary].log('Parsing', req.method, req.url);
|
783
727
|
}
|
784
728
|
const { versions } = models[vocabulary];
|
785
729
|
const reqHooks = versions.map((version) => [
|
786
730
|
version,
|
787
|
-
|
731
|
+
getHooks({
|
788
732
|
method: req.method,
|
789
733
|
vocabulary: version,
|
790
734
|
}, version === versions[0]),
|
@@ -802,7 +746,7 @@ const runODataRequest = (req, vocabulary) => {
|
|
802
746
|
}
|
803
747
|
});
|
804
748
|
transactions.length = 0;
|
805
|
-
|
749
|
+
rollbackRequestHooks(reqHooks);
|
806
750
|
};
|
807
751
|
req.on('close', tryCancelRequest);
|
808
752
|
if (req.tx != null) {
|
@@ -813,7 +757,7 @@ const runODataRequest = (req, vocabulary) => {
|
|
813
757
|
return {
|
814
758
|
tryCancelRequest,
|
815
759
|
promise: (async () => {
|
816
|
-
await
|
760
|
+
await runHooks('PREPARSE', reqHooks, { req, tx: req.tx });
|
817
761
|
let requests;
|
818
762
|
if (req.batch != null && req.batch.length > 0) {
|
819
763
|
requests = req.batch;
|
@@ -823,51 +767,51 @@ const runODataRequest = (req, vocabulary) => {
|
|
823
767
|
requests = [{ method, url, data: body }];
|
824
768
|
}
|
825
769
|
const prepareRequest = async (parsedRequest) => {
|
826
|
-
const abstractSqlModel =
|
770
|
+
const abstractSqlModel = getAbstractSqlModel(parsedRequest);
|
827
771
|
if (abstractSqlModel == null) {
|
828
|
-
throw new
|
772
|
+
throw new BadRequestError('Unknown vocabulary: ' + parsedRequest.vocabulary);
|
829
773
|
}
|
830
|
-
parsedRequest.engine =
|
774
|
+
parsedRequest.engine = db.engine;
|
831
775
|
parsedRequest.translateVersions = [...versions];
|
832
776
|
const $request = parsedRequest;
|
833
777
|
try {
|
834
|
-
let resolvedResourceName =
|
778
|
+
let resolvedResourceName = resolveSynonym($request);
|
835
779
|
if (resolvedResourceName.endsWith('#canAccess')) {
|
836
780
|
resolvedResourceName = resolvedResourceName.slice(0, -'#canAccess'.length);
|
837
781
|
}
|
838
782
|
if (abstractSqlModel.tables[resolvedResourceName] == null) {
|
839
|
-
throw new
|
783
|
+
throw new UnauthorizedError();
|
840
784
|
}
|
841
785
|
$request.hooks = [];
|
842
786
|
for (const version of versions) {
|
843
787
|
const hooks = [
|
844
788
|
version,
|
845
|
-
|
789
|
+
getHooks({
|
846
790
|
resourceName: $request.resourceName,
|
847
791
|
vocabulary: version,
|
848
792
|
method: $request.method,
|
849
793
|
}, version === versions[0]),
|
850
794
|
];
|
851
795
|
$request.hooks.push(hooks);
|
852
|
-
await
|
796
|
+
await runHooks('POSTPARSE', [hooks], {
|
853
797
|
req,
|
854
798
|
request: $request,
|
855
799
|
tx: req.tx,
|
856
800
|
});
|
857
801
|
const { resourceRenames } = models[version];
|
858
802
|
if (resourceRenames) {
|
859
|
-
const resourceName =
|
803
|
+
const resourceName = resolveSynonym($request);
|
860
804
|
if (resourceRenames[resourceName]) {
|
861
|
-
$request.resourceName =
|
805
|
+
$request.resourceName = sqlNameToODataName(resourceRenames[resourceName]);
|
862
806
|
}
|
863
807
|
}
|
864
808
|
}
|
865
809
|
const translatedRequest = uriParser.translateUri($request);
|
866
|
-
return
|
810
|
+
return compileRequest(translatedRequest);
|
867
811
|
}
|
868
812
|
catch (err) {
|
869
|
-
|
870
|
-
|
813
|
+
rollbackRequestHooks(reqHooks);
|
814
|
+
rollbackRequestHooks($request.hooks);
|
871
815
|
throw err;
|
872
816
|
}
|
873
817
|
};
|
@@ -883,14 +827,14 @@ const runODataRequest = (req, vocabulary) => {
|
|
883
827
|
return await runTransaction(req, request, async (tx) => {
|
884
828
|
transactions.push(tx);
|
885
829
|
tx.on('rollback', () => {
|
886
|
-
|
830
|
+
rollbackRequestHooks(reqHooks);
|
887
831
|
if (Array.isArray(request)) {
|
888
832
|
for (const { hooks } of request) {
|
889
|
-
|
833
|
+
rollbackRequestHooks(hooks);
|
890
834
|
}
|
891
835
|
}
|
892
836
|
else {
|
893
|
-
|
837
|
+
rollbackRequestHooks(request.hooks);
|
894
838
|
}
|
895
839
|
});
|
896
840
|
if (Array.isArray(request)) {
|
@@ -907,7 +851,7 @@ const runODataRequest = (req, vocabulary) => {
|
|
907
851
|
});
|
908
852
|
});
|
909
853
|
const responses = results.map((result) => {
|
910
|
-
if (
|
854
|
+
if (_.isError(result)) {
|
911
855
|
return convertToHttpError(result);
|
912
856
|
}
|
913
857
|
else {
|
@@ -915,7 +859,7 @@ const runODataRequest = (req, vocabulary) => {
|
|
915
859
|
result.body == null &&
|
916
860
|
result.statusCode == null) {
|
917
861
|
console.error('No status or body set', req.url, responses);
|
918
|
-
return new
|
862
|
+
return new InternalRequestError();
|
919
863
|
}
|
920
864
|
return result;
|
921
865
|
}
|
@@ -924,13 +868,12 @@ const runODataRequest = (req, vocabulary) => {
|
|
924
868
|
})(),
|
925
869
|
};
|
926
870
|
};
|
927
|
-
const getApiRoot = (req) => {
|
871
|
+
export const getApiRoot = (req) => {
|
928
872
|
const [, apiRoot] = req.url.split('/', 2);
|
929
873
|
return apiRoot;
|
930
874
|
};
|
931
|
-
|
932
|
-
const
|
933
|
-
const apiRoot = (0, exports.getApiRoot)(req);
|
875
|
+
export const handleODataRequest = async (req, res, next) => {
|
876
|
+
const apiRoot = getApiRoot(req);
|
934
877
|
if (apiRoot == null || models[apiRoot] == null) {
|
935
878
|
next('route');
|
936
879
|
return;
|
@@ -942,14 +885,14 @@ const handleODataRequest = async (req, res, next) => {
|
|
942
885
|
res.set('Cache-Control', 'no-cache');
|
943
886
|
if (req.batch == null || req.batch.length === 0) {
|
944
887
|
let [response] = responses;
|
945
|
-
if (response instanceof
|
888
|
+
if (response instanceof HttpError) {
|
946
889
|
response = httpErrorToResponse(response);
|
947
890
|
}
|
948
891
|
handleResponse(res, response);
|
949
892
|
}
|
950
893
|
else {
|
951
894
|
res.status(200).sendMulti(responses.map((response) => {
|
952
|
-
if (response instanceof
|
895
|
+
if (response instanceof HttpError) {
|
953
896
|
return httpErrorToResponse(response);
|
954
897
|
}
|
955
898
|
else {
|
@@ -959,21 +902,19 @@ const handleODataRequest = async (req, res, next) => {
|
|
959
902
|
}
|
960
903
|
}
|
961
904
|
catch (e) {
|
962
|
-
if (
|
905
|
+
if (handleHttpErrors(req, res, e)) {
|
963
906
|
return;
|
964
907
|
}
|
965
908
|
console.error('An error occurred while constructing the response', e);
|
966
909
|
res.status(500).end();
|
967
910
|
}
|
968
911
|
};
|
969
|
-
exports.handleODataRequest = handleODataRequest;
|
970
912
|
const handleErrorFns = [];
|
971
|
-
const onHandleHttpError = (fn) => {
|
913
|
+
export const onHandleHttpError = (fn) => {
|
972
914
|
handleErrorFns.push(fn);
|
973
915
|
};
|
974
|
-
|
975
|
-
|
976
|
-
if (err instanceof errors_1.HttpError) {
|
916
|
+
export const handleHttpErrors = (req, res, err) => {
|
917
|
+
if (err instanceof HttpError) {
|
977
918
|
for (const handleErrorFn of handleErrorFns) {
|
978
919
|
handleErrorFn(req, err);
|
979
920
|
}
|
@@ -983,7 +924,6 @@ const handleHttpErrors = (req, res, err) => {
|
|
983
924
|
}
|
984
925
|
return false;
|
985
926
|
};
|
986
|
-
exports.handleHttpErrors = handleHttpErrors;
|
987
927
|
const handleResponse = (res, response) => {
|
988
928
|
const { body, headers, statusCode } = response;
|
989
929
|
res.set(headers);
|
@@ -1003,38 +943,38 @@ const httpErrorToResponse = (err) => {
|
|
1003
943
|
};
|
1004
944
|
};
|
1005
945
|
const convertToHttpError = (err) => {
|
1006
|
-
if (err instanceof
|
946
|
+
if (err instanceof HttpError) {
|
1007
947
|
return err;
|
1008
948
|
}
|
1009
|
-
if (err instanceof
|
1010
|
-
return new
|
949
|
+
if (err instanceof SbvrValidationError) {
|
950
|
+
return new BadRequestError(err);
|
1011
951
|
}
|
1012
|
-
if (err instanceof
|
1013
|
-
return new
|
952
|
+
if (err instanceof PermissionError) {
|
953
|
+
return new UnauthorizedError(err);
|
1014
954
|
}
|
1015
|
-
if (err instanceof
|
1016
|
-
return new
|
955
|
+
if (err instanceof db.ConstraintError) {
|
956
|
+
return new ConflictError(err);
|
1017
957
|
}
|
1018
|
-
if (err instanceof
|
1019
|
-
err instanceof
|
1020
|
-
err instanceof
|
1021
|
-
err instanceof
|
1022
|
-
err instanceof
|
1023
|
-
return new
|
958
|
+
if (err instanceof SqlCompilationError ||
|
959
|
+
err instanceof TranslationError ||
|
960
|
+
err instanceof ParsingError ||
|
961
|
+
err instanceof PermissionParsingError ||
|
962
|
+
err instanceof db.DatabaseError) {
|
963
|
+
return new InternalRequestError();
|
1024
964
|
}
|
1025
965
|
console.error('Unexpected response error type', err);
|
1026
|
-
return new
|
966
|
+
return new InternalRequestError();
|
1027
967
|
};
|
1028
968
|
const runRequest = async (req, tx, request) => {
|
1029
|
-
const log =
|
1030
|
-
if (
|
969
|
+
const log = logger[request.vocabulary];
|
970
|
+
if (env.DEBUG) {
|
1031
971
|
log.log('Running', req.method, req.url);
|
1032
972
|
}
|
1033
973
|
let resultGet;
|
1034
974
|
let resultPost;
|
1035
975
|
try {
|
1036
976
|
try {
|
1037
|
-
await
|
977
|
+
await runHooks('PRERUN', request.hooks, { req, request, tx });
|
1038
978
|
switch (request.method) {
|
1039
979
|
case 'GET':
|
1040
980
|
resultGet = await runGet(req, request, tx);
|
@@ -1053,7 +993,7 @@ const runRequest = async (req, tx, request) => {
|
|
1053
993
|
}
|
1054
994
|
}
|
1055
995
|
catch (err) {
|
1056
|
-
if (err instanceof
|
996
|
+
if (err instanceof db.DatabaseError) {
|
1057
997
|
prettifyConstraintError(err, request);
|
1058
998
|
log.error(err);
|
1059
999
|
err.message = 'Database error';
|
@@ -1067,11 +1007,11 @@ const runRequest = async (req, tx, request) => {
|
|
1067
1007
|
err instanceof TypeError ||
|
1068
1008
|
err instanceof URIError) {
|
1069
1009
|
log.error(err);
|
1070
|
-
throw new
|
1010
|
+
throw new InternalRequestError();
|
1071
1011
|
}
|
1072
1012
|
throw err;
|
1073
1013
|
}
|
1074
|
-
await
|
1014
|
+
await runHooks('POSTRUN', request.hooks, {
|
1075
1015
|
req,
|
1076
1016
|
request,
|
1077
1017
|
result: resultGet ?? resultPost,
|
@@ -1079,7 +1019,7 @@ const runRequest = async (req, tx, request) => {
|
|
1079
1019
|
});
|
1080
1020
|
}
|
1081
1021
|
catch (err) {
|
1082
|
-
await
|
1022
|
+
await runHooks('POSTRUN-ERROR', request.hooks, {
|
1083
1023
|
req,
|
1084
1024
|
request,
|
1085
1025
|
tx,
|
@@ -1101,7 +1041,7 @@ const runRequest = async (req, tx, request) => {
|
|
1101
1041
|
case 'OPTIONS':
|
1102
1042
|
return await respondOptions(req, request, tx);
|
1103
1043
|
default:
|
1104
|
-
throw new
|
1044
|
+
throw new MethodNotAllowedError();
|
1105
1045
|
}
|
1106
1046
|
};
|
1107
1047
|
const runChangeSet = (req, tx) => async (changeSetResults, request) => {
|
@@ -1123,7 +1063,7 @@ const updateBinds = (changeSetResults, request) => {
|
|
1123
1063
|
if (ref?.body == null ||
|
1124
1064
|
typeof ref.body === 'string' ||
|
1125
1065
|
ref.body.id === undefined) {
|
1126
|
-
throw new
|
1066
|
+
throw new BadRequestError('Reference to a non existing resource in Changeset');
|
1127
1067
|
}
|
1128
1068
|
request.odataBinds[i] = uriParser.parseId(ref.body.id);
|
1129
1069
|
}
|
@@ -1149,33 +1089,33 @@ const runTransaction = async (req, request, callback) => {
|
|
1149
1089
|
}
|
1150
1090
|
if (Array.isArray(request)) {
|
1151
1091
|
if (request.every(checkReadOnlyRequests)) {
|
1152
|
-
return await
|
1092
|
+
return await db.readTransaction(callback);
|
1153
1093
|
}
|
1154
1094
|
}
|
1155
1095
|
else if (checkReadOnlyRequests(request)) {
|
1156
|
-
return await
|
1096
|
+
return await db.readTransaction(callback);
|
1157
1097
|
}
|
1158
|
-
return await
|
1098
|
+
return await db.transaction(callback);
|
1159
1099
|
};
|
1160
1100
|
const runQuery = async (tx, request, queryIndex, addReturning = false) => {
|
1161
1101
|
const { vocabulary } = request;
|
1162
1102
|
let { sqlQuery } = request;
|
1163
1103
|
if (sqlQuery == null) {
|
1164
|
-
throw new
|
1104
|
+
throw new InternalRequestError('No SQL query available to run');
|
1165
1105
|
}
|
1166
1106
|
if (request.engine == null) {
|
1167
|
-
throw new
|
1107
|
+
throw new InternalRequestError('No database engine specified');
|
1168
1108
|
}
|
1169
1109
|
if (Array.isArray(sqlQuery)) {
|
1170
1110
|
if (queryIndex == null) {
|
1171
|
-
throw new
|
1111
|
+
throw new InternalRequestError('Received a query index to run but the query is not an array');
|
1172
1112
|
}
|
1173
1113
|
sqlQuery = sqlQuery[queryIndex];
|
1174
1114
|
}
|
1175
1115
|
const { query, bindings } = sqlQuery;
|
1176
|
-
const values = await
|
1177
|
-
if (
|
1178
|
-
|
1116
|
+
const values = await getAndCheckBindValues(request, bindings);
|
1117
|
+
if (env.DEBUG) {
|
1118
|
+
logger[vocabulary].log(query, values);
|
1179
1119
|
}
|
1180
1120
|
const returningIdField = addReturning && request.affectedIds == null ? getIdField(request) : false;
|
1181
1121
|
const sqlResult = await tx.executeSql(query, values, returningIdField);
|
@@ -1199,13 +1139,13 @@ const respondGet = async (req, request, result, tx) => {
|
|
1199
1139
|
const metadata = format != null && typeof format === 'object'
|
1200
1140
|
? format.metadata
|
1201
1141
|
: undefined;
|
1202
|
-
const d = await odataResponse.process(vocab,
|
1142
|
+
const d = await odataResponse.process(vocab, getAbstractSqlModel(request), request.originalResourceName, result.rows, { includeMetadata: metadata === 'full' });
|
1203
1143
|
const response = {
|
1204
1144
|
statusCode: 200,
|
1205
1145
|
body: { d },
|
1206
1146
|
headers: { 'content-type': 'application/json' },
|
1207
1147
|
};
|
1208
|
-
await
|
1148
|
+
await runHooks('PRERESPOND', request.hooks, {
|
1209
1149
|
req,
|
1210
1150
|
request,
|
1211
1151
|
result,
|
@@ -1232,22 +1172,22 @@ const respondGet = async (req, request, result, tx) => {
|
|
1232
1172
|
const runPost = async (_req, request, tx) => {
|
1233
1173
|
const { rowsAffected, insertId } = await runQuery(tx, request, undefined, true);
|
1234
1174
|
if (rowsAffected === 0) {
|
1235
|
-
throw new
|
1175
|
+
throw new PermissionError();
|
1236
1176
|
}
|
1237
|
-
await
|
1177
|
+
await validateModel(tx, _.last(request.translateVersions), request);
|
1238
1178
|
return insertId;
|
1239
1179
|
};
|
1240
1180
|
const respondPost = async (req, request, id, tx) => {
|
1241
1181
|
const vocab = request.vocabulary;
|
1242
1182
|
const location = odataResponse.resourceURI(vocab, request.originalResourceName, id);
|
1243
|
-
if (
|
1244
|
-
|
1183
|
+
if (env.DEBUG) {
|
1184
|
+
logger[vocab].log('Insert ID: ', request.resourceName, id);
|
1245
1185
|
}
|
1246
1186
|
let result = { d: [{ id }] };
|
1247
1187
|
if (location != null &&
|
1248
1188
|
!['0', 'false'].includes(request?.odataQuery?.options?.returnResource)) {
|
1249
1189
|
try {
|
1250
|
-
result = (await
|
1190
|
+
result = (await runURI('GET', location, undefined, tx, req));
|
1251
1191
|
}
|
1252
1192
|
catch {
|
1253
1193
|
}
|
@@ -1260,7 +1200,7 @@ const respondPost = async (req, request, id, tx) => {
|
|
1260
1200
|
location,
|
1261
1201
|
},
|
1262
1202
|
};
|
1263
|
-
await
|
1203
|
+
await runHooks('PRERESPOND', request.hooks, {
|
1264
1204
|
req,
|
1265
1205
|
request,
|
1266
1206
|
result,
|
@@ -1281,14 +1221,14 @@ const runPut = async (_req, request, tx) => {
|
|
1281
1221
|
({ rowsAffected } = await runQuery(tx, request, undefined, true));
|
1282
1222
|
}
|
1283
1223
|
if (rowsAffected > 0) {
|
1284
|
-
await
|
1224
|
+
await validateModel(tx, _.last(request.translateVersions), request);
|
1285
1225
|
}
|
1286
1226
|
};
|
1287
1227
|
const respondPut = async (req, request, tx) => {
|
1288
1228
|
const response = {
|
1289
1229
|
statusCode: 200,
|
1290
1230
|
};
|
1291
|
-
await
|
1231
|
+
await runHooks('PRERESPOND', request.hooks, {
|
1292
1232
|
req,
|
1293
1233
|
request,
|
1294
1234
|
response,
|
@@ -1301,7 +1241,7 @@ const respondOptions = respondPut;
|
|
1301
1241
|
const runDelete = async (_req, request, tx) => {
|
1302
1242
|
const { rowsAffected } = await runQuery(tx, request, undefined, true);
|
1303
1243
|
if (rowsAffected > 0) {
|
1304
|
-
await
|
1244
|
+
await validateModel(tx, _.last(request.translateVersions), request);
|
1305
1245
|
}
|
1306
1246
|
};
|
1307
1247
|
const devModelConfig = {
|
@@ -1341,10 +1281,10 @@ const devModelConfig = {
|
|
1341
1281
|
},
|
1342
1282
|
},
|
1343
1283
|
};
|
1344
|
-
const executeStandardModels = async (tx) => {
|
1284
|
+
export const executeStandardModels = async (tx) => {
|
1345
1285
|
try {
|
1346
|
-
await
|
1347
|
-
await
|
1286
|
+
await executeModel(tx, devModelConfig);
|
1287
|
+
await executeModels(tx, permissions.config.models);
|
1348
1288
|
console.info('Successfully executed standard models.');
|
1349
1289
|
}
|
1350
1290
|
catch (err) {
|
@@ -1352,12 +1292,11 @@ const executeStandardModels = async (tx) => {
|
|
1352
1292
|
throw err;
|
1353
1293
|
}
|
1354
1294
|
};
|
1355
|
-
|
1356
|
-
|
1357
|
-
exports.db = $db;
|
1295
|
+
export const setup = async (_app, $db) => {
|
1296
|
+
db = $db;
|
1358
1297
|
try {
|
1359
|
-
await
|
1360
|
-
await
|
1298
|
+
await db.transaction(async (tx) => {
|
1299
|
+
await executeStandardModels(tx);
|
1361
1300
|
permissions.setup();
|
1362
1301
|
});
|
1363
1302
|
}
|
@@ -1366,17 +1305,16 @@ const setup = async (_app, $db) => {
|
|
1366
1305
|
process.exit(1);
|
1367
1306
|
}
|
1368
1307
|
try {
|
1369
|
-
await
|
1308
|
+
await db.executeSql('CREATE UNIQUE INDEX "uniq_model_model_type_vocab" ON "model" ("is of-vocabulary", "model type");');
|
1370
1309
|
}
|
1371
1310
|
catch {
|
1372
1311
|
}
|
1373
1312
|
};
|
1374
|
-
|
1375
|
-
|
1376
|
-
exports.db = $db;
|
1313
|
+
export const postSetup = async (_app, $db) => {
|
1314
|
+
db = $db;
|
1377
1315
|
try {
|
1378
|
-
await
|
1379
|
-
await
|
1316
|
+
await db.transaction(async (tx) => {
|
1317
|
+
await postExecuteModels(tx);
|
1380
1318
|
});
|
1381
1319
|
}
|
1382
1320
|
catch (err) {
|
@@ -1384,5 +1322,4 @@ const postSetup = async (_app, $db) => {
|
|
1384
1322
|
process.exit(1);
|
1385
1323
|
}
|
1386
1324
|
};
|
1387
|
-
exports.postSetup = postSetup;
|
1388
1325
|
//# sourceMappingURL=sbvr-utils.js.map
|