@balena/pinejs 15.0.0-true-boolean-911aca4062d3132ad3c34712014739b6849fa13a → 15.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.dockerignore +4 -0
- package/.github/workflows/flowzone.yml +21 -0
- package/.husky/pre-commit +4 -0
- package/.pinejs-cache.json +1 -0
- package/.resinci.yml +1 -0
- package/.versionbot/CHANGELOG.yml +9678 -2001
- package/CHANGELOG.md +2975 -2
- package/Dockerfile +14 -0
- package/Gruntfile.ts +3 -6
- package/README.md +10 -1
- package/VERSION +1 -0
- package/build/browser.ts +1 -1
- package/build/config.ts +0 -1
- package/docker-compose.npm-test.yml +11 -0
- package/docs/AdvancedUsage.md +77 -63
- package/docs/GettingStarted.md +90 -41
- package/docs/Migrations.md +102 -1
- package/docs/ProjectConfig.md +12 -21
- package/docs/Testing.md +7 -0
- package/out/bin/abstract-sql-compiler.js +17 -17
- package/out/bin/abstract-sql-compiler.js.map +1 -1
- package/out/bin/odata-compiler.js +23 -20
- package/out/bin/odata-compiler.js.map +1 -1
- package/out/bin/sbvr-compiler.js +22 -22
- package/out/bin/sbvr-compiler.js.map +1 -1
- package/out/bin/utils.d.ts +2 -2
- package/out/bin/utils.js +3 -3
- package/out/bin/utils.js.map +1 -1
- package/out/config-loader/config-loader.d.ts +9 -8
- package/out/config-loader/config-loader.js +135 -78
- package/out/config-loader/config-loader.js.map +1 -1
- package/out/config-loader/env.d.ts +41 -16
- package/out/config-loader/env.js +46 -2
- package/out/config-loader/env.js.map +1 -1
- package/out/data-server/sbvr-server.d.ts +2 -19
- package/out/data-server/sbvr-server.js +44 -38
- package/out/data-server/sbvr-server.js.map +1 -1
- package/out/database-layer/db.d.ts +32 -14
- package/out/database-layer/db.js +120 -41
- package/out/database-layer/db.js.map +1 -1
- package/out/express-emulator/express.js +10 -11
- package/out/express-emulator/express.js.map +1 -1
- package/out/http-transactions/transactions.d.ts +2 -18
- package/out/http-transactions/transactions.js +29 -21
- package/out/http-transactions/transactions.js.map +1 -1
- package/out/migrator/async.d.ts +7 -0
- package/out/migrator/async.js +168 -0
- package/out/migrator/async.js.map +1 -0
- package/out/migrator/migrations.sbvr +43 -0
- package/out/migrator/sync.d.ts +9 -0
- package/out/migrator/sync.js +106 -0
- package/out/migrator/sync.js.map +1 -0
- package/out/migrator/utils.d.ts +78 -0
- package/out/migrator/utils.js +283 -0
- package/out/migrator/utils.js.map +1 -0
- package/out/odata-metadata/odata-metadata-generator.js +10 -13
- package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
- package/out/passport-pinejs/passport-pinejs.d.ts +1 -1
- package/out/passport-pinejs/passport-pinejs.js +8 -7
- 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 +20 -6
- package/out/pinejs-session-store/pinejs-session-store.js.map +1 -1
- package/out/sbvr-api/abstract-sql.d.ts +3 -2
- package/out/sbvr-api/abstract-sql.js +9 -9
- package/out/sbvr-api/abstract-sql.js.map +1 -1
- package/out/sbvr-api/cached-compile.js +1 -1
- package/out/sbvr-api/cached-compile.js.map +1 -1
- package/out/sbvr-api/common-types.d.ts +6 -5
- package/out/sbvr-api/control-flow.d.ts +8 -1
- package/out/sbvr-api/control-flow.js +36 -9
- package/out/sbvr-api/control-flow.js.map +1 -1
- package/out/sbvr-api/errors.d.ts +47 -40
- package/out/sbvr-api/errors.js +78 -77
- package/out/sbvr-api/errors.js.map +1 -1
- package/out/sbvr-api/express-extension.d.ts +4 -0
- package/out/sbvr-api/hooks.d.ts +16 -15
- package/out/sbvr-api/hooks.js +74 -48
- 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 +28 -30
- package/out/sbvr-api/odata-response.js.map +1 -1
- package/out/sbvr-api/permissions.d.ts +17 -16
- package/out/sbvr-api/permissions.js +369 -304
- package/out/sbvr-api/permissions.js.map +1 -1
- package/out/sbvr-api/sbvr-utils.d.ts +33 -15
- package/out/sbvr-api/sbvr-utils.js +397 -235
- package/out/sbvr-api/sbvr-utils.js.map +1 -1
- package/out/sbvr-api/translations.d.ts +6 -0
- package/out/sbvr-api/translations.js +150 -0
- package/out/sbvr-api/translations.js.map +1 -0
- package/out/sbvr-api/uri-parser.d.ts +23 -17
- package/out/sbvr-api/uri-parser.js +33 -27
- package/out/sbvr-api/uri-parser.js.map +1 -1
- package/out/sbvr-api/user.sbvr +2 -0
- package/out/server-glue/module.d.ts +6 -6
- package/out/server-glue/module.js +4 -2
- package/out/server-glue/module.js.map +1 -1
- package/out/server-glue/server.js +5 -5
- package/out/server-glue/server.js.map +1 -1
- package/package.json +89 -73
- package/pinejs.png +0 -0
- package/repo.yml +9 -9
- package/src/bin/abstract-sql-compiler.ts +5 -7
- package/src/bin/odata-compiler.ts +11 -13
- package/src/bin/sbvr-compiler.ts +11 -17
- package/src/bin/utils.ts +3 -5
- package/src/config-loader/config-loader.ts +167 -53
- package/src/config-loader/env.ts +106 -6
- package/src/data-server/sbvr-server.js +44 -38
- package/src/database-layer/db.ts +205 -64
- package/src/express-emulator/express.js +10 -11
- package/src/http-transactions/transactions.js +29 -21
- package/src/migrator/async.ts +323 -0
- package/src/migrator/migrations.sbvr +43 -0
- package/src/migrator/sync.ts +152 -0
- package/src/migrator/utils.ts +458 -0
- package/src/odata-metadata/odata-metadata-generator.ts +12 -15
- package/src/passport-pinejs/passport-pinejs.ts +9 -7
- package/src/pinejs-session-store/pinejs-session-store.ts +15 -1
- package/src/sbvr-api/abstract-sql.ts +17 -14
- package/src/sbvr-api/common-types.ts +2 -1
- package/src/sbvr-api/control-flow.ts +45 -11
- package/src/sbvr-api/errors.ts +82 -77
- package/src/sbvr-api/express-extension.ts +6 -1
- package/src/sbvr-api/hooks.ts +123 -50
- package/src/sbvr-api/odata-response.ts +23 -28
- package/src/sbvr-api/permissions.ts +548 -415
- package/src/sbvr-api/sbvr-utils.ts +581 -259
- package/src/sbvr-api/translations.ts +248 -0
- package/src/sbvr-api/uri-parser.ts +63 -49
- package/src/sbvr-api/user.sbvr +2 -0
- package/src/server-glue/module.ts +16 -10
- package/src/server-glue/server.ts +5 -5
- package/tsconfig.dev.json +1 -0
- package/tsconfig.json +1 -2
- package/typings/lf-to-abstract-sql.d.ts +6 -9
- package/typings/memoizee.d.ts +1 -1
- package/.github/CODEOWNERS +0 -1
- package/circle.yml +0 -37
- package/docs/todo.txt +0 -22
- package/out/migrator/migrator.d.ts +0 -20
- package/out/migrator/migrator.js +0 -188
- package/out/migrator/migrator.js.map +0 -1
- package/src/migrator/migrator.ts +0 -286
@@ -1,19 +1,19 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.setup = exports.executeStandardModels = exports.handleODataRequest = exports.getAffectedIds = exports.getAbstractSqlModel = exports.runURI = exports.api = exports.PinejsClient = exports.runRule = exports.getID = exports.deleteModel = exports.executeModels = exports.executeModel = exports.generateModels = exports.generateSqlModel = exports.generateAbstractSqlModel = exports.generateLfModel = exports.validateModel = exports.resolveNavigationResource = exports.resolveSynonym = exports.resolveOdataBind = exports.sbvrTypes = exports.db = exports.addSideEffectHook = exports.addPureHook = void 0;
|
4
|
-
const Bluebird = require("bluebird");
|
3
|
+
exports.setup = exports.executeStandardModels = exports.handleHttpErrors = exports.onHandleHttpError = exports.handleODataRequest = exports.getAffectedIds = exports.getAbstractSqlModel = exports.runURI = exports.api = exports.PinejsClient = exports.runRule = exports.getID = exports.deleteModel = exports.executeModels = exports.executeModel = exports.generateModels = 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;
|
5
4
|
const _ = require("lodash");
|
6
5
|
const cached_compile_1 = require("./cached-compile");
|
7
6
|
const AbstractSQLCompiler = require("@balena/abstract-sql-compiler");
|
8
7
|
const package_json_1 = require("@balena/abstract-sql-compiler/package.json");
|
9
8
|
const LF2AbstractSQL = require("@balena/lf-to-abstract-sql");
|
10
9
|
const odata_to_abstract_sql_1 = require("@balena/odata-to-abstract-sql");
|
11
|
-
const
|
12
|
-
exports.sbvrTypes =
|
10
|
+
const sbvr_types_1 = require("@balena/sbvr-types");
|
11
|
+
exports.sbvrTypes = sbvr_types_1.default;
|
13
12
|
const deepFreeze = require("deep-freeze");
|
14
13
|
const pinejs_client_core_1 = require("pinejs-client-core");
|
15
14
|
const extended_sbvr_parser_1 = require("../extended-sbvr-parser/extended-sbvr-parser");
|
16
|
-
const
|
15
|
+
const asyncMigrator = require("../migrator/async");
|
16
|
+
const syncMigrator = require("../migrator/sync");
|
17
17
|
const odata_metadata_generator_1 = require("../odata-metadata/odata-metadata-generator");
|
18
18
|
const devModel = require('./dev.sbvr');
|
19
19
|
const permissions = require("./permissions");
|
@@ -33,11 +33,12 @@ var abstract_sql_2 = require("./abstract-sql");
|
|
33
33
|
Object.defineProperty(exports, "resolveOdataBind", { enumerable: true, get: function () { return abstract_sql_2.resolveOdataBind; } });
|
34
34
|
const odataResponse = require("./odata-response");
|
35
35
|
const module_1 = require("../server-glue/module");
|
36
|
-
const
|
36
|
+
const translations_1 = require("./translations");
|
37
|
+
const LF2AbstractSQLTranslator = LF2AbstractSQL.createTranslator(sbvr_types_1.default);
|
37
38
|
const LF2AbstractSQLTranslatorVersion = `${package_json_2.version}+${package_json_3.version}`;
|
38
39
|
const models = {};
|
39
40
|
const memoizedResolvedSynonym = memoizeWeak((abstractSqlModel, resourceName) => {
|
40
|
-
const sqlName = odata_to_abstract_sql_1.odataNameToSqlName(resourceName);
|
41
|
+
const sqlName = (0, odata_to_abstract_sql_1.odataNameToSqlName)(resourceName);
|
41
42
|
return _(sqlName)
|
42
43
|
.split('-')
|
43
44
|
.map((namePart) => {
|
@@ -50,16 +51,15 @@ const memoizedResolvedSynonym = memoizeWeak((abstractSqlModel, resourceName) =>
|
|
50
51
|
.join('-');
|
51
52
|
}, { primitive: true });
|
52
53
|
const resolveSynonym = (request) => {
|
53
|
-
const abstractSqlModel = exports.getAbstractSqlModel(request);
|
54
|
+
const abstractSqlModel = (0, exports.getAbstractSqlModel)(request);
|
54
55
|
return memoizedResolvedSynonym(abstractSqlModel, request.resourceName);
|
55
56
|
};
|
56
57
|
exports.resolveSynonym = resolveSynonym;
|
57
58
|
const memoizedResolveNavigationResource = memoizeWeak((abstractSqlModel, resourceName, navigationName) => {
|
58
|
-
const navigation =
|
59
|
+
const navigation = (0, odata_to_abstract_sql_1.odataNameToSqlName)(navigationName)
|
59
60
|
.split('-')
|
60
|
-
.flatMap((namePart) => memoizedResolvedSynonym(abstractSqlModel, namePart).split('-'))
|
61
|
-
|
62
|
-
.value();
|
61
|
+
.flatMap((namePart) => memoizedResolvedSynonym(abstractSqlModel, namePart).split('-'));
|
62
|
+
navigation.push('$');
|
63
63
|
const resolvedResourceName = memoizedResolvedSynonym(abstractSqlModel, resourceName);
|
64
64
|
const mapping = _.get(abstractSqlModel.relationships[resolvedResourceName], navigation);
|
65
65
|
if (mapping == null) {
|
@@ -68,10 +68,10 @@ const memoizedResolveNavigationResource = memoizeWeak((abstractSqlModel, resourc
|
|
68
68
|
if (mapping.length < 2) {
|
69
69
|
throw new Error(`'${resourceName}' to '${navigationName}' is a field not a navigation`);
|
70
70
|
}
|
71
|
-
return odata_to_abstract_sql_1.sqlNameToODataName(abstractSqlModel.tables[mapping[1][0]].name);
|
71
|
+
return (0, odata_to_abstract_sql_1.sqlNameToODataName)(abstractSqlModel.tables[mapping[1][0]].name);
|
72
72
|
}, { primitive: true });
|
73
73
|
const resolveNavigationResource = (request, navigationName) => {
|
74
|
-
const abstractSqlModel = exports.getAbstractSqlModel(request);
|
74
|
+
const abstractSqlModel = (0, exports.getAbstractSqlModel)(request);
|
75
75
|
return memoizedResolveNavigationResource(abstractSqlModel, request.resourceName, navigationName);
|
76
76
|
};
|
77
77
|
exports.resolveNavigationResource = resolveNavigationResource;
|
@@ -81,11 +81,12 @@ const prettifyConstraintError = (err, request) => {
|
|
81
81
|
if (err instanceof exports.db.UniqueConstraintError) {
|
82
82
|
switch (exports.db.engine) {
|
83
83
|
case 'mysql':
|
84
|
-
matches =
|
84
|
+
matches =
|
85
|
+
/ER_DUP_ENTRY: Duplicate entry '.*?[^\\]' for key '(.*?[^\\])'/.exec(err.message);
|
85
86
|
break;
|
86
87
|
case 'postgres':
|
87
|
-
const resourceName = exports.resolveSynonym(request);
|
88
|
-
const abstractSqlModel =
|
88
|
+
const resourceName = (0, exports.resolveSynonym)(request);
|
89
|
+
const abstractSqlModel = getFinalAbstractSqlModel(request);
|
89
90
|
matches = new RegExp('"' + abstractSqlModel.tables[resourceName].name + '_(.*?)_key"').exec(err.message);
|
90
91
|
break;
|
91
92
|
}
|
@@ -103,11 +104,12 @@ const prettifyConstraintError = (err, request) => {
|
|
103
104
|
if (err instanceof exports.db.ForeignKeyConstraintError) {
|
104
105
|
switch (exports.db.engine) {
|
105
106
|
case 'mysql':
|
106
|
-
matches =
|
107
|
+
matches =
|
108
|
+
/ER_ROW_IS_REFERENCED_: Cannot delete or update a parent row: a foreign key constraint fails \(".*?"\.(".*?").*/.exec(err.message);
|
107
109
|
break;
|
108
110
|
case 'postgres':
|
109
|
-
const resourceName = exports.resolveSynonym(request);
|
110
|
-
const abstractSqlModel =
|
111
|
+
const resourceName = (0, exports.resolveSynonym)(request);
|
112
|
+
const abstractSqlModel = getFinalAbstractSqlModel(request);
|
111
113
|
const tableName = abstractSqlModel.tables[resourceName].name;
|
112
114
|
matches = new RegExp('"' +
|
113
115
|
tableName +
|
@@ -124,11 +126,11 @@ const prettifyConstraintError = (err, request) => {
|
|
124
126
|
if (matches == null) {
|
125
127
|
throw new exports.db.ForeignKeyConstraintError('Foreign key constraint violated');
|
126
128
|
}
|
127
|
-
throw new exports.db.ForeignKeyConstraintError('Data is referenced by ' + odata_to_abstract_sql_1.sqlNameToODataName(matches[1]) + '.');
|
129
|
+
throw new exports.db.ForeignKeyConstraintError('Data is referenced by ' + (0, odata_to_abstract_sql_1.sqlNameToODataName)(matches[1]) + '.');
|
128
130
|
}
|
129
131
|
if (err instanceof exports.db.CheckConstraintError) {
|
130
|
-
const resourceName = exports.resolveSynonym(request);
|
131
|
-
const abstractSqlModel =
|
132
|
+
const resourceName = (0, exports.resolveSynonym)(request);
|
133
|
+
const abstractSqlModel = getFinalAbstractSqlModel(request);
|
132
134
|
const table = abstractSqlModel.tables[resourceName];
|
133
135
|
if (table.checks) {
|
134
136
|
switch (exports.db.engine) {
|
@@ -142,7 +144,7 @@ const prettifyConstraintError = (err, request) => {
|
|
142
144
|
if (matches != null) {
|
143
145
|
const checkName = matches[1];
|
144
146
|
const check = table.checks.find((c) => c.name === checkName);
|
145
|
-
if (
|
147
|
+
if (check?.description != null) {
|
146
148
|
throw new errors_1.BadRequestError(check.description);
|
147
149
|
}
|
148
150
|
}
|
@@ -152,14 +154,54 @@ const prettifyConstraintError = (err, request) => {
|
|
152
154
|
throw err;
|
153
155
|
}
|
154
156
|
};
|
157
|
+
let cachedIsModelNew;
|
158
|
+
const isModelNew = async (tx, modelName) => {
|
159
|
+
const result = await tx.tableList("name = 'model'");
|
160
|
+
if (result.rows.length === 0) {
|
161
|
+
return true;
|
162
|
+
}
|
163
|
+
if (cachedIsModelNew == null) {
|
164
|
+
const { rows } = await tx.executeSql(`SELECT "is of-vocabulary" FROM "model";`);
|
165
|
+
cachedIsModelNew = new Set(rows.map((row) => row['is of-vocabulary']));
|
166
|
+
}
|
167
|
+
return !cachedIsModelNew.has(modelName);
|
168
|
+
};
|
169
|
+
exports.isModelNew = isModelNew;
|
170
|
+
const bindsForAffectedIds = (bindings, request) => {
|
171
|
+
if (request?.affectedIds == null) {
|
172
|
+
return {};
|
173
|
+
}
|
174
|
+
const tableName = (0, exports.getAbstractSqlModel)(request).tables[(0, exports.resolveSynonym)(request)].name;
|
175
|
+
const isDelete = request.method === 'DELETE';
|
176
|
+
const odataBinds = {};
|
177
|
+
for (const bind of bindings) {
|
178
|
+
if (bind.length !== 2 ||
|
179
|
+
bind[0] !== 'Bind' ||
|
180
|
+
typeof bind[1] !== 'string') {
|
181
|
+
continue;
|
182
|
+
}
|
183
|
+
const bindName = bind[1];
|
184
|
+
if (!isDelete && bindName === tableName) {
|
185
|
+
odataBinds[bindName] = ['Text', `{${request.affectedIds}}`];
|
186
|
+
}
|
187
|
+
else {
|
188
|
+
odataBinds[bindName] = ['Text', '{}'];
|
189
|
+
}
|
190
|
+
}
|
191
|
+
return odataBinds;
|
192
|
+
};
|
155
193
|
const validateModel = async (tx, modelName, request) => {
|
156
|
-
|
157
|
-
|
194
|
+
const { sql } = models[modelName];
|
195
|
+
if (!sql) {
|
196
|
+
throw new Error(`Tried to validate a virtual model: '${modelName}'`);
|
197
|
+
}
|
198
|
+
await Promise.all(sql.rules.map(async (rule) => {
|
199
|
+
if (!(0, abstract_sql_1.isRuleAffected)(rule, request)) {
|
158
200
|
return;
|
159
201
|
}
|
160
|
-
const values = await abstract_sql_1.getAndCheckBindValues({
|
202
|
+
const values = await (0, abstract_sql_1.getAndCheckBindValues)({
|
161
203
|
vocabulary: modelName,
|
162
|
-
odataBinds:
|
204
|
+
odataBinds: bindsForAffectedIds(rule.bindings, request),
|
163
205
|
values: {},
|
164
206
|
engine: exports.db.engine,
|
165
207
|
}, rule.bindings);
|
@@ -171,26 +213,26 @@ const validateModel = async (tx, modelName, request) => {
|
|
171
213
|
}));
|
172
214
|
};
|
173
215
|
exports.validateModel = validateModel;
|
174
|
-
const generateLfModel = (seModel) => cached_compile_1.cachedCompile('lfModel', extended_sbvr_parser_1.ExtendedSBVRParser.version, seModel, () => extended_sbvr_parser_1.ExtendedSBVRParser.matchAll(seModel, 'Process'));
|
216
|
+
const generateLfModel = (seModel) => (0, cached_compile_1.cachedCompile)('lfModel', extended_sbvr_parser_1.ExtendedSBVRParser.version, seModel, () => extended_sbvr_parser_1.ExtendedSBVRParser.matchAll(seModel, 'Process'));
|
175
217
|
exports.generateLfModel = generateLfModel;
|
176
|
-
const generateAbstractSqlModel = (lfModel) => cached_compile_1.cachedCompile('abstractSqlModel', LF2AbstractSQLTranslatorVersion, lfModel, () => LF2AbstractSQLTranslator(lfModel, 'Process'));
|
218
|
+
const generateAbstractSqlModel = (lfModel) => (0, cached_compile_1.cachedCompile)('abstractSqlModel', LF2AbstractSQLTranslatorVersion, lfModel, () => LF2AbstractSQLTranslator(lfModel, 'Process'));
|
177
219
|
exports.generateAbstractSqlModel = generateAbstractSqlModel;
|
178
|
-
const generateSqlModel = (abstractSql, targetDatabaseEngine) => cached_compile_1.cachedCompile('sqlModel', package_json_1.version + '+' + targetDatabaseEngine, abstractSql, () => AbstractSQLCompiler[targetDatabaseEngine].compileSchema(abstractSql));
|
220
|
+
const generateSqlModel = (abstractSql, targetDatabaseEngine) => (0, cached_compile_1.cachedCompile)('sqlModel', package_json_1.version + '+' + targetDatabaseEngine, abstractSql, () => AbstractSQLCompiler[targetDatabaseEngine].compileSchema(abstractSql));
|
179
221
|
exports.generateSqlModel = generateSqlModel;
|
180
|
-
|
181
|
-
const { apiRoot: vocab, modelText: se } = model;
|
222
|
+
function generateModels(model, targetDatabaseEngine) {
|
223
|
+
const { apiRoot: vocab, modelText: se, translateTo, translations } = model;
|
182
224
|
let { abstractSql: maybeAbstractSql } = model;
|
183
225
|
let lf;
|
184
226
|
if (se) {
|
185
227
|
try {
|
186
|
-
lf = exports.generateLfModel(se);
|
228
|
+
lf = (0, exports.generateLfModel)(se);
|
187
229
|
}
|
188
230
|
catch (e) {
|
189
231
|
console.error(`Error parsing model '${vocab}':`, e);
|
190
232
|
throw new Error(`Error parsing model '${vocab}': ` + e);
|
191
233
|
}
|
192
234
|
try {
|
193
|
-
maybeAbstractSql = exports.generateAbstractSqlModel(lf);
|
235
|
+
maybeAbstractSql = (0, exports.generateAbstractSqlModel)(lf);
|
194
236
|
}
|
195
237
|
catch (e) {
|
196
238
|
console.error(`Error translating model '${vocab}':`, e);
|
@@ -198,50 +240,80 @@ const generateModels = (model, targetDatabaseEngine) => {
|
|
198
240
|
}
|
199
241
|
}
|
200
242
|
const abstractSql = maybeAbstractSql;
|
201
|
-
const odataMetadata = cached_compile_1.cachedCompile('metadata', odata_metadata_generator_1.generateODataMetadata.version, { vocab, abstractSqlModel: abstractSql }, () => odata_metadata_generator_1.generateODataMetadata(vocab, abstractSql));
|
243
|
+
const odataMetadata = (0, cached_compile_1.cachedCompile)('metadata', odata_metadata_generator_1.generateODataMetadata.version, { vocab, abstractSqlModel: abstractSql }, () => (0, odata_metadata_generator_1.generateODataMetadata)(vocab, abstractSql));
|
202
244
|
let sql;
|
203
|
-
|
204
|
-
|
245
|
+
let resourceRenames;
|
246
|
+
if (translateTo != null) {
|
247
|
+
resourceRenames = (0, translations_1.translateAbstractSqlModel)(abstractSql, models[translateTo].abstractSql, model.apiRoot, translateTo, translations);
|
205
248
|
}
|
206
|
-
|
207
|
-
|
208
|
-
|
249
|
+
else {
|
250
|
+
for (const [key, table] of Object.entries(abstractSql.tables)) {
|
251
|
+
abstractSql.tables[`${key}$${model.apiRoot}`] = { ...table };
|
252
|
+
}
|
253
|
+
try {
|
254
|
+
sql = (0, exports.generateSqlModel)(abstractSql, targetDatabaseEngine);
|
255
|
+
}
|
256
|
+
catch (e) {
|
257
|
+
console.error(`Error compiling model '${vocab}':`, e);
|
258
|
+
throw new Error(`Error compiling model '${vocab}': ` + e);
|
259
|
+
}
|
209
260
|
}
|
210
|
-
return {
|
211
|
-
|
261
|
+
return {
|
262
|
+
vocab,
|
263
|
+
translateTo,
|
264
|
+
resourceRenames,
|
265
|
+
se,
|
266
|
+
lf,
|
267
|
+
abstractSql,
|
268
|
+
sql,
|
269
|
+
odataMetadata,
|
270
|
+
};
|
271
|
+
}
|
212
272
|
exports.generateModels = generateModels;
|
213
|
-
const executeModel = (tx, model) => exports.executeModels(tx, [model]);
|
273
|
+
const executeModel = (tx, model) => (0, exports.executeModels)(tx, [model]);
|
214
274
|
exports.executeModel = executeModel;
|
215
275
|
const executeModels = async (tx, execModels) => {
|
216
276
|
try {
|
217
277
|
const compiledModels = await Promise.all(execModels.map(async (model) => {
|
218
|
-
var _a, _b, _c, _d;
|
219
278
|
const { apiRoot } = model;
|
220
|
-
await
|
221
|
-
const compiledModel =
|
222
|
-
|
223
|
-
const
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
279
|
+
await syncMigrator.run(tx, model);
|
280
|
+
const compiledModel = generateModels(model, exports.db.engine);
|
281
|
+
if (compiledModel.sql) {
|
282
|
+
for (const createStatement of compiledModel.sql.createSchema) {
|
283
|
+
const promise = tx.executeSql(createStatement);
|
284
|
+
if (exports.db.engine === 'websql') {
|
285
|
+
promise.catch((err) => {
|
286
|
+
console.warn("Ignoring errors in the create table statements for websql as it doesn't support CREATE IF NOT EXISTS", err);
|
287
|
+
});
|
288
|
+
}
|
289
|
+
await promise;
|
228
290
|
}
|
229
|
-
await promise;
|
230
291
|
}
|
231
|
-
await
|
292
|
+
await syncMigrator.postRun(tx, model);
|
232
293
|
odataResponse.prepareModel(compiledModel.abstractSql);
|
233
294
|
deepFreeze(compiledModel.abstractSql);
|
234
|
-
|
235
|
-
|
295
|
+
const versions = [apiRoot];
|
296
|
+
if (compiledModel.translateTo != null) {
|
297
|
+
versions.push(...models[compiledModel.translateTo].versions);
|
298
|
+
}
|
299
|
+
models[apiRoot] = {
|
300
|
+
...compiledModel,
|
301
|
+
versions,
|
302
|
+
};
|
303
|
+
if (compiledModel.sql) {
|
304
|
+
await (0, exports.validateModel)(tx, apiRoot);
|
305
|
+
}
|
236
306
|
exports.api[apiRoot] = new PinejsClient('/' + apiRoot + '/');
|
237
307
|
exports.api[apiRoot].logger = { ...console };
|
238
308
|
if (model.logging != null) {
|
239
|
-
const defaultSetting =
|
309
|
+
const defaultSetting = model.logging?.default ?? true;
|
310
|
+
const { logger } = exports.api[apiRoot];
|
240
311
|
for (const k of Object.keys(model.logging)) {
|
241
312
|
const key = k;
|
242
|
-
if (
|
243
|
-
|
244
|
-
|
313
|
+
if (key !== 'Console' &&
|
314
|
+
typeof logger[key] === 'function' &&
|
315
|
+
!(model.logging?.[key] ?? defaultSetting)) {
|
316
|
+
logger[key] = _.noop;
|
245
317
|
}
|
246
318
|
}
|
247
319
|
}
|
@@ -249,7 +321,6 @@ const executeModels = async (tx, execModels) => {
|
|
249
321
|
}));
|
250
322
|
await Promise.all(compiledModels.map(async (model) => {
|
251
323
|
const updateModel = async (modelType) => {
|
252
|
-
var _a;
|
253
324
|
if (model[modelType] == null) {
|
254
325
|
return await exports.api.dev.delete({
|
255
326
|
resource: 'model',
|
@@ -283,10 +354,12 @@ const executeModels = async (tx, execModels) => {
|
|
283
354
|
let uri = '/dev/model';
|
284
355
|
const body = {
|
285
356
|
is_of__vocabulary: model.vocab,
|
286
|
-
model_value: model[modelType]
|
357
|
+
model_value: typeof model[modelType] === 'string'
|
358
|
+
? { value: model[modelType] }
|
359
|
+
: model[modelType],
|
287
360
|
model_type: modelType,
|
288
361
|
};
|
289
|
-
const id =
|
362
|
+
const id = result?.[0]?.id;
|
290
363
|
if (id != null) {
|
291
364
|
uri += '(' + id + ')';
|
292
365
|
method = 'PATCH';
|
@@ -295,10 +368,13 @@ const executeModels = async (tx, execModels) => {
|
|
295
368
|
else {
|
296
369
|
uri += '?returnResource=false';
|
297
370
|
}
|
298
|
-
return await exports.runURI(method, uri, body, tx, permissions.root);
|
371
|
+
return await (0, exports.runURI)(method, uri, body, tx, permissions.root);
|
299
372
|
};
|
300
373
|
await Promise.all(['se', 'lf', 'abstractSql', 'sql', 'odataMetadata'].map(updateModel));
|
301
374
|
}));
|
375
|
+
await Promise.all(execModels.map(async (model) => {
|
376
|
+
await asyncMigrator.run(tx, model);
|
377
|
+
}));
|
302
378
|
}
|
303
379
|
catch (err) {
|
304
380
|
await Promise.all(execModels.map(async ({ apiRoot }) => {
|
@@ -313,23 +389,26 @@ const cleanupModel = (vocab) => {
|
|
313
389
|
delete exports.api[vocab];
|
314
390
|
};
|
315
391
|
const deleteModel = async (vocabulary) => {
|
316
|
-
|
317
|
-
|
318
|
-
await
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
$filter: {
|
327
|
-
is_of__vocabulary: vocabulary,
|
392
|
+
const { sql } = models[vocabulary];
|
393
|
+
if (sql) {
|
394
|
+
await exports.db.transaction(async (tx) => {
|
395
|
+
const dropStatements = sql.dropSchema.map((dropStatement) => tx.executeSql(dropStatement));
|
396
|
+
await Promise.all(dropStatements.concat([
|
397
|
+
exports.api.dev.delete({
|
398
|
+
resource: 'model',
|
399
|
+
passthrough: {
|
400
|
+
tx,
|
401
|
+
req: permissions.root,
|
328
402
|
},
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
403
|
+
options: {
|
404
|
+
$filter: {
|
405
|
+
is_of__vocabulary: vocabulary,
|
406
|
+
},
|
407
|
+
},
|
408
|
+
}),
|
409
|
+
]));
|
410
|
+
});
|
411
|
+
}
|
333
412
|
await cleanupModel(vocabulary);
|
334
413
|
};
|
335
414
|
exports.deleteModel = deleteModel;
|
@@ -364,7 +443,7 @@ exports.runRule = (() => {
|
|
364
443
|
},
|
365
444
|
});
|
366
445
|
const translator = LF2AbstractSQL.LF2AbstractSQL.createInstance();
|
367
|
-
translator.addTypes(
|
446
|
+
translator.addTypes(sbvr_types_1.default);
|
368
447
|
return async (vocab, rule) => {
|
369
448
|
const seModel = models[vocab].se;
|
370
449
|
const { logger } = exports.api[vocab];
|
@@ -440,7 +519,7 @@ exports.runRule = (() => {
|
|
440
519
|
if (Array.isArray(compiledRule)) {
|
441
520
|
throw new Error('Unexpected query generated');
|
442
521
|
}
|
443
|
-
const values = await abstract_sql_1.getAndCheckBindValues({
|
522
|
+
const values = await (0, abstract_sql_1.getAndCheckBindValues)({
|
444
523
|
vocabulary: vocab,
|
445
524
|
odataBinds: [],
|
446
525
|
values: {},
|
@@ -448,7 +527,7 @@ exports.runRule = (() => {
|
|
448
527
|
}, compiledRule.bindings);
|
449
528
|
const result = await exports.db.executeSql(compiledRule.query, values);
|
450
529
|
const table = models[vocab].abstractSql.tables[resourceName];
|
451
|
-
const odataIdField = odata_to_abstract_sql_1.sqlNameToODataName(table.idField);
|
530
|
+
const odataIdField = (0, odata_to_abstract_sql_1.sqlNameToODataName)(table.idField);
|
452
531
|
let ids = result.rows.map((row) => row[table.idField]);
|
453
532
|
ids = _.uniq(ids);
|
454
533
|
ids = ids.map((id) => odataIdField + ' eq ' + id);
|
@@ -459,10 +538,10 @@ exports.runRule = (() => {
|
|
459
538
|
else {
|
460
539
|
filter = '0 eq 1';
|
461
540
|
}
|
462
|
-
const odataResult = (await exports.runURI('GET', '/' +
|
541
|
+
const odataResult = (await (0, exports.runURI)('GET', '/' +
|
463
542
|
vocab +
|
464
543
|
'/' +
|
465
|
-
odata_to_abstract_sql_1.sqlNameToODataName(table.resourceName) +
|
544
|
+
(0, odata_to_abstract_sql_1.sqlNameToODataName)(table.resourceName) +
|
466
545
|
'?$filter=' +
|
467
546
|
filter, undefined, undefined, permissions.rootRead));
|
468
547
|
odataResult.__formulationType = formulationType;
|
@@ -472,7 +551,7 @@ exports.runRule = (() => {
|
|
472
551
|
})();
|
473
552
|
class PinejsClient extends pinejs_client_core_1.PinejsClientCore {
|
474
553
|
async _request({ method, url, body, tx, req, custom, }) {
|
475
|
-
return (await exports.runURI(method, url, body, tx, req, custom));
|
554
|
+
return (await (0, exports.runURI)(method, url, body, tx, req, custom));
|
476
555
|
}
|
477
556
|
}
|
478
557
|
exports.PinejsClient = PinejsClient;
|
@@ -484,7 +563,7 @@ const runURI = async (method, uri, body = {}, tx, req, custom) => {
|
|
484
563
|
}
|
485
564
|
let user;
|
486
565
|
let apiKey;
|
487
|
-
if (req != null &&
|
566
|
+
if (req != null && typeof req === 'object') {
|
488
567
|
user = req.user;
|
489
568
|
apiKey = req.apiKey;
|
490
569
|
}
|
@@ -520,23 +599,26 @@ const runURI = async (method, uri, body = {}, tx, req, custom) => {
|
|
520
599
|
if (_.isError(response)) {
|
521
600
|
throw response;
|
522
601
|
}
|
523
|
-
const { body: responseBody,
|
524
|
-
if (
|
525
|
-
const ErrorClass = errors_1.statusCodeToError[
|
602
|
+
const { body: responseBody, statusCode, headers } = response;
|
603
|
+
if (statusCode != null && statusCode >= 400) {
|
604
|
+
const ErrorClass = errors_1.statusCodeToError[statusCode];
|
526
605
|
if (ErrorClass != null) {
|
527
|
-
throw new ErrorClass(undefined, responseBody);
|
606
|
+
throw new ErrorClass(undefined, responseBody, headers);
|
528
607
|
}
|
529
|
-
throw new errors_1.HttpError(
|
608
|
+
throw new errors_1.HttpError(statusCode, undefined, responseBody, headers);
|
530
609
|
}
|
531
610
|
return responseBody;
|
532
611
|
};
|
533
612
|
exports.runURI = runURI;
|
534
613
|
const getAbstractSqlModel = (request) => {
|
535
|
-
|
536
|
-
return ((_a = request.abstractSqlModel) !== null && _a !== void 0 ? _a : (request.abstractSqlModel = models[request.vocabulary].abstractSql));
|
614
|
+
return (request.abstractSqlModel ??= models[request.vocabulary].abstractSql);
|
537
615
|
};
|
538
616
|
exports.getAbstractSqlModel = getAbstractSqlModel;
|
539
|
-
const
|
617
|
+
const getFinalAbstractSqlModel = (request) => {
|
618
|
+
const finalModel = _.last(request.translateVersions);
|
619
|
+
return (request.finalAbstractSqlModel ??= models[finalModel].abstractSql);
|
620
|
+
};
|
621
|
+
const getIdField = (request) => getFinalAbstractSqlModel(request).tables[(0, exports.resolveSynonym)(request)].idField;
|
540
622
|
const getAffectedIds = async (args) => {
|
541
623
|
const { request } = args;
|
542
624
|
if (request.affectedIds) {
|
@@ -552,38 +634,38 @@ const getAffectedIds = async (args) => {
|
|
552
634
|
};
|
553
635
|
exports.getAffectedIds = getAffectedIds;
|
554
636
|
const $getAffectedIds = async ({ req, request, tx, }) => {
|
555
|
-
var _a;
|
556
|
-
var _b;
|
557
637
|
if (!['PATCH', 'DELETE'].includes(request.method)) {
|
558
638
|
throw new Error('Can only call `getAffectedIds` with PATCH/DELETE requests');
|
559
639
|
}
|
560
|
-
|
640
|
+
const parsedRequest = await uriParser.parseOData({
|
561
641
|
method: request.method,
|
562
642
|
url: `/${request.vocabulary}${request.url}`,
|
563
643
|
});
|
564
|
-
|
565
|
-
|
566
|
-
|
644
|
+
parsedRequest.engine = request.engine;
|
645
|
+
parsedRequest.translateVersions = request.translateVersions;
|
646
|
+
let affectedRequest = parsedRequest;
|
647
|
+
const abstractSqlModel = (0, exports.getAbstractSqlModel)(affectedRequest);
|
648
|
+
const resourceName = (0, exports.resolveSynonym)(affectedRequest);
|
567
649
|
const resourceTable = abstractSqlModel.tables[resourceName];
|
568
650
|
if (resourceTable == null) {
|
569
|
-
throw new Error('Unknown resource: ' +
|
651
|
+
throw new Error('Unknown resource: ' + affectedRequest.resourceName);
|
570
652
|
}
|
571
653
|
const { idField } = resourceTable;
|
572
|
-
|
573
|
-
|
654
|
+
affectedRequest.odataQuery.options ??= {};
|
655
|
+
affectedRequest.odataQuery.options.$select = {
|
574
656
|
properties: [{ name: idField }],
|
575
657
|
};
|
576
|
-
delete
|
577
|
-
await permissions.addPermissions(req,
|
578
|
-
|
579
|
-
|
580
|
-
|
658
|
+
delete affectedRequest.odataQuery.options.$expand;
|
659
|
+
await permissions.addPermissions(req, affectedRequest);
|
660
|
+
affectedRequest.method = 'GET';
|
661
|
+
affectedRequest = uriParser.translateUri(affectedRequest);
|
662
|
+
affectedRequest = (0, abstract_sql_1.compileRequest)(affectedRequest);
|
581
663
|
let result;
|
582
664
|
if (tx != null) {
|
583
|
-
result = await runQuery(tx,
|
665
|
+
result = await runQuery(tx, affectedRequest);
|
584
666
|
}
|
585
667
|
else {
|
586
|
-
result = await runTransaction(req, (newTx) => runQuery(newTx,
|
668
|
+
result = await runTransaction(req, affectedRequest, (newTx) => runQuery(newTx, affectedRequest));
|
587
669
|
}
|
588
670
|
return result.rows.map((row) => row[idField]);
|
589
671
|
};
|
@@ -591,10 +673,14 @@ const runODataRequest = (req, vocabulary) => {
|
|
591
673
|
if (module_1.env.DEBUG) {
|
592
674
|
exports.api[vocabulary].logger.log('Parsing', req.method, req.url);
|
593
675
|
}
|
594
|
-
const
|
595
|
-
|
596
|
-
|
597
|
-
|
676
|
+
const { versions } = models[vocabulary];
|
677
|
+
const reqHooks = versions.map((version) => [
|
678
|
+
version,
|
679
|
+
(0, hooks_1.getHooks)({
|
680
|
+
method: req.method,
|
681
|
+
vocabulary: version,
|
682
|
+
}, version === versions[0]),
|
683
|
+
]);
|
598
684
|
const transactions = [];
|
599
685
|
const tryCancelRequest = () => {
|
600
686
|
transactions.forEach(async (tx) => {
|
@@ -604,22 +690,22 @@ const runODataRequest = (req, vocabulary) => {
|
|
604
690
|
try {
|
605
691
|
await tx.rollback();
|
606
692
|
}
|
607
|
-
catch
|
693
|
+
catch {
|
608
694
|
}
|
609
695
|
});
|
610
696
|
transactions.length = 0;
|
611
|
-
hooks_1.rollbackRequestHooks(reqHooks);
|
697
|
+
(0, hooks_1.rollbackRequestHooks)(reqHooks);
|
612
698
|
};
|
613
699
|
req.on('close', tryCancelRequest);
|
614
700
|
if (req.tx != null) {
|
615
701
|
transactions.push(req.tx);
|
616
702
|
req.tx.on('rollback', tryCancelRequest);
|
617
703
|
}
|
618
|
-
const
|
704
|
+
const mappingFn = controlFlow.getMappingFn(req.headers);
|
619
705
|
return {
|
620
706
|
tryCancelRequest,
|
621
707
|
promise: (async () => {
|
622
|
-
await hooks_1.runHooks('PREPARSE', reqHooks, { req, tx: req.tx });
|
708
|
+
await (0, hooks_1.runHooks)('PREPARSE', reqHooks, { req, tx: req.tx });
|
623
709
|
let requests;
|
624
710
|
if (req.batch != null && req.batch.length > 0) {
|
625
711
|
requests = req.batch;
|
@@ -628,43 +714,64 @@ const runODataRequest = (req, vocabulary) => {
|
|
628
714
|
const { method, url, body } = req;
|
629
715
|
requests = [{ method, url, data: body }];
|
630
716
|
}
|
631
|
-
const prepareRequest = async (
|
632
|
-
|
633
|
-
|
717
|
+
const prepareRequest = async (parsedRequest) => {
|
718
|
+
parsedRequest.engine = exports.db.engine;
|
719
|
+
parsedRequest.translateVersions = [...versions];
|
720
|
+
const $request = parsedRequest;
|
634
721
|
try {
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
722
|
+
$request.hooks = [];
|
723
|
+
for (const version of versions) {
|
724
|
+
const hooks = [
|
725
|
+
version,
|
726
|
+
(0, hooks_1.getHooks)({
|
727
|
+
resourceName: $request.resourceName,
|
728
|
+
vocabulary: version,
|
729
|
+
method: $request.method,
|
730
|
+
}, version === versions[0]),
|
731
|
+
];
|
732
|
+
$request.hooks.push(hooks);
|
733
|
+
await (0, hooks_1.runHooks)('POSTPARSE', [hooks], {
|
734
|
+
req,
|
735
|
+
request: $request,
|
736
|
+
tx: req.tx,
|
737
|
+
});
|
738
|
+
const { resourceRenames } = models[version];
|
739
|
+
if (resourceRenames) {
|
740
|
+
const resourceName = (0, exports.resolveSynonym)($request);
|
741
|
+
if (resourceRenames[resourceName]) {
|
742
|
+
$request.resourceName = (0, odata_to_abstract_sql_1.sqlNameToODataName)(resourceRenames[resourceName]);
|
743
|
+
}
|
744
|
+
}
|
745
|
+
}
|
640
746
|
const translatedRequest = await uriParser.translateUri($request);
|
641
|
-
return await abstract_sql_1.compileRequest(translatedRequest);
|
747
|
+
return await (0, abstract_sql_1.compileRequest)(translatedRequest);
|
642
748
|
}
|
643
749
|
catch (err) {
|
644
|
-
hooks_1.rollbackRequestHooks(reqHooks);
|
645
|
-
hooks_1.rollbackRequestHooks($request.hooks);
|
750
|
+
(0, hooks_1.rollbackRequestHooks)(reqHooks);
|
751
|
+
(0, hooks_1.rollbackRequestHooks)($request.hooks);
|
646
752
|
throw err;
|
647
753
|
}
|
648
754
|
};
|
649
|
-
const results = await
|
650
|
-
|
651
|
-
|
652
|
-
|
755
|
+
const results = await mappingFn(requests, async (requestPart) => {
|
756
|
+
const parsedRequest = await uriParser.parseOData(requestPart);
|
757
|
+
let request;
|
758
|
+
if (Array.isArray(parsedRequest)) {
|
759
|
+
request = await controlFlow.mapSeries(parsedRequest, prepareRequest);
|
653
760
|
}
|
654
761
|
else {
|
655
|
-
request = await prepareRequest(
|
762
|
+
request = await prepareRequest(parsedRequest);
|
656
763
|
}
|
657
|
-
return await runTransaction(req, async (tx) => {
|
764
|
+
return await runTransaction(req, request, async (tx) => {
|
658
765
|
transactions.push(tx);
|
659
766
|
tx.on('rollback', () => {
|
660
|
-
hooks_1.rollbackRequestHooks(reqHooks);
|
767
|
+
(0, hooks_1.rollbackRequestHooks)(reqHooks);
|
661
768
|
if (Array.isArray(request)) {
|
662
|
-
|
663
|
-
hooks_1.rollbackRequestHooks(hooks);
|
664
|
-
}
|
769
|
+
for (const { hooks } of request) {
|
770
|
+
(0, hooks_1.rollbackRequestHooks)(hooks);
|
771
|
+
}
|
665
772
|
}
|
666
773
|
else {
|
667
|
-
hooks_1.rollbackRequestHooks(request.hooks);
|
774
|
+
(0, hooks_1.rollbackRequestHooks)(request.hooks);
|
668
775
|
}
|
669
776
|
});
|
670
777
|
if (Array.isArray(request)) {
|
@@ -687,7 +794,7 @@ const runODataRequest = (req, vocabulary) => {
|
|
687
794
|
else {
|
688
795
|
if (!Array.isArray(result) &&
|
689
796
|
result.body == null &&
|
690
|
-
result.
|
797
|
+
result.statusCode == null) {
|
691
798
|
console.error('No status or body set', req.url, responses);
|
692
799
|
return new errors_1.InternalRequestError();
|
693
800
|
}
|
@@ -710,36 +817,15 @@ const handleODataRequest = async (req, res, next) => {
|
|
710
817
|
res.set('Cache-Control', 'no-cache');
|
711
818
|
if (req.batch == null || req.batch.length === 0) {
|
712
819
|
let [response] = responses;
|
713
|
-
if (
|
714
|
-
response =
|
715
|
-
status: response.status,
|
716
|
-
body: response.getResponseBody(),
|
717
|
-
};
|
718
|
-
}
|
719
|
-
const { body, headers, status } = response;
|
720
|
-
if (status) {
|
721
|
-
res.status(status);
|
722
|
-
}
|
723
|
-
_.forEach(headers, (headerValue, headerName) => {
|
724
|
-
res.set(headerName, headerValue);
|
725
|
-
});
|
726
|
-
if (!body) {
|
727
|
-
res.sendStatus(status);
|
728
|
-
}
|
729
|
-
else {
|
730
|
-
if (status != null) {
|
731
|
-
res.status(status);
|
732
|
-
}
|
733
|
-
res.json(body);
|
820
|
+
if (response instanceof errors_1.HttpError) {
|
821
|
+
response = httpErrorToResponse(response);
|
734
822
|
}
|
823
|
+
handleResponse(res, response);
|
735
824
|
}
|
736
825
|
else {
|
737
826
|
res.status(200).sendMulti(responses.map((response) => {
|
738
|
-
if (
|
739
|
-
|
740
|
-
status: response.status,
|
741
|
-
body: response.getResponseBody(),
|
742
|
-
};
|
827
|
+
if (response instanceof errors_1.HttpError) {
|
828
|
+
response = httpErrorToResponse(response);
|
743
829
|
}
|
744
830
|
else {
|
745
831
|
return response;
|
@@ -748,21 +834,49 @@ const handleODataRequest = async (req, res, next) => {
|
|
748
834
|
}
|
749
835
|
}
|
750
836
|
catch (e) {
|
751
|
-
if (
|
752
|
-
const body = e.getResponseBody();
|
753
|
-
if (body) {
|
754
|
-
res.status(e.status).send(body);
|
755
|
-
}
|
756
|
-
else {
|
757
|
-
res.sendStatus(e.status);
|
758
|
-
}
|
837
|
+
if ((0, exports.handleHttpErrors)(req, res, e)) {
|
759
838
|
return;
|
760
839
|
}
|
761
840
|
console.error('An error occurred while constructing the response', e);
|
762
|
-
res.
|
841
|
+
res.status(500).end();
|
763
842
|
}
|
764
843
|
};
|
765
844
|
exports.handleODataRequest = handleODataRequest;
|
845
|
+
const handleErrorFns = [];
|
846
|
+
const onHandleHttpError = (fn) => {
|
847
|
+
handleErrorFns.push(fn);
|
848
|
+
};
|
849
|
+
exports.onHandleHttpError = onHandleHttpError;
|
850
|
+
const handleHttpErrors = (req, res, err) => {
|
851
|
+
if (err instanceof errors_1.HttpError) {
|
852
|
+
for (const handleErrorFn of handleErrorFns) {
|
853
|
+
handleErrorFn(req, err);
|
854
|
+
}
|
855
|
+
const response = httpErrorToResponse(err);
|
856
|
+
handleResponse(res, response);
|
857
|
+
return true;
|
858
|
+
}
|
859
|
+
return false;
|
860
|
+
};
|
861
|
+
exports.handleHttpErrors = handleHttpErrors;
|
862
|
+
const handleResponse = (res, response) => {
|
863
|
+
const { body, headers, statusCode } = response;
|
864
|
+
res.set(headers);
|
865
|
+
res.status(statusCode);
|
866
|
+
if (!body) {
|
867
|
+
res.end();
|
868
|
+
}
|
869
|
+
else {
|
870
|
+
res.json(body);
|
871
|
+
}
|
872
|
+
};
|
873
|
+
const httpErrorToResponse = (err) => {
|
874
|
+
return {
|
875
|
+
statusCode: err.status,
|
876
|
+
body: err.getResponseBody(),
|
877
|
+
headers: err.headers,
|
878
|
+
};
|
879
|
+
};
|
766
880
|
const convertToHttpError = (err) => {
|
767
881
|
if (err instanceof errors_1.HttpError) {
|
768
882
|
return err;
|
@@ -794,7 +908,7 @@ const runRequest = async (req, tx, request) => {
|
|
794
908
|
let result;
|
795
909
|
try {
|
796
910
|
try {
|
797
|
-
await hooks_1.runHooks('PRERUN', request.hooks, { req, request, tx });
|
911
|
+
await (0, hooks_1.runHooks)('PRERUN', request.hooks, { req, request, tx });
|
798
912
|
switch (request.method) {
|
799
913
|
case 'GET':
|
800
914
|
result = await runGet(req, request, tx);
|
@@ -831,10 +945,10 @@ const runRequest = async (req, tx, request) => {
|
|
831
945
|
}
|
832
946
|
throw err;
|
833
947
|
}
|
834
|
-
await hooks_1.runHooks('POSTRUN', request.hooks, { req, request, result, tx });
|
948
|
+
await (0, hooks_1.runHooks)('POSTRUN', request.hooks, { req, request, result, tx });
|
835
949
|
}
|
836
950
|
catch (err) {
|
837
|
-
await hooks_1.runHooks('POSTRUN-ERROR', request.hooks, {
|
951
|
+
await (0, hooks_1.runHooks)('POSTRUN-ERROR', request.hooks, {
|
838
952
|
req,
|
839
953
|
request,
|
840
954
|
tx,
|
@@ -845,30 +959,29 @@ const runRequest = async (req, tx, request) => {
|
|
845
959
|
return await prepareResponse(req, request, result, tx);
|
846
960
|
};
|
847
961
|
const runChangeSet = (req, tx) => async (changeSetResults, request) => {
|
848
|
-
var _a;
|
849
962
|
request = updateBinds(changeSetResults, request);
|
850
963
|
const result = await runRequest(req, tx, request);
|
851
964
|
if (request.id == null) {
|
852
965
|
throw new Error('No request id');
|
853
966
|
}
|
854
|
-
|
855
|
-
result.headers['
|
967
|
+
result.headers ??= {};
|
968
|
+
result.headers['content-id'] = request.id;
|
856
969
|
changeSetResults.set(request.id, result);
|
857
970
|
};
|
858
971
|
const updateBinds = (changeSetResults, request) => {
|
859
972
|
if (request._defer) {
|
860
|
-
|
973
|
+
for (let i = 0; i < request.odataBinds.length; i++) {
|
974
|
+
const [tag, id] = request.odataBinds[i];
|
861
975
|
if (tag === 'ContentReference') {
|
862
976
|
const ref = changeSetResults.get(id);
|
863
|
-
if (
|
977
|
+
if (ref?.body == null ||
|
864
978
|
typeof ref.body === 'string' ||
|
865
979
|
ref.body.id === undefined) {
|
866
980
|
throw new errors_1.BadRequestError('Reference to a non existing resource in Changeset');
|
867
981
|
}
|
868
|
-
|
982
|
+
request.odataBinds[i] = uriParser.parseId(ref.body.id);
|
869
983
|
}
|
870
|
-
|
871
|
-
});
|
984
|
+
}
|
872
985
|
}
|
873
986
|
return request;
|
874
987
|
};
|
@@ -890,15 +1003,33 @@ const prepareResponse = async (req, request, result, tx) => {
|
|
890
1003
|
throw new errors_1.MethodNotAllowedError();
|
891
1004
|
}
|
892
1005
|
};
|
893
|
-
const
|
1006
|
+
const checkReadOnlyRequests = (request) => {
|
1007
|
+
if (request.method !== 'GET') {
|
1008
|
+
return false;
|
1009
|
+
}
|
1010
|
+
const { hooks } = request;
|
1011
|
+
if (hooks == null) {
|
1012
|
+
return true;
|
1013
|
+
}
|
1014
|
+
return hooks.every(([, versionedHooks]) => Object.values(versionedHooks).every((hookTypeHooks) => {
|
1015
|
+
return (hookTypeHooks == null || hookTypeHooks.every((hook) => hook.readOnlyTx));
|
1016
|
+
}));
|
1017
|
+
};
|
1018
|
+
const runTransaction = async (req, request, callback) => {
|
894
1019
|
if (req.tx != null) {
|
895
1020
|
return await callback(req.tx);
|
896
1021
|
}
|
897
|
-
|
898
|
-
|
1022
|
+
if (Array.isArray(request)) {
|
1023
|
+
if (request.every(checkReadOnlyRequests)) {
|
1024
|
+
return await exports.db.readTransaction(callback);
|
1025
|
+
}
|
1026
|
+
}
|
1027
|
+
else if (checkReadOnlyRequests(request)) {
|
1028
|
+
return await exports.db.readTransaction(callback);
|
899
1029
|
}
|
1030
|
+
return await exports.db.transaction(callback);
|
900
1031
|
};
|
901
|
-
const runQuery = async (tx, request, queryIndex,
|
1032
|
+
const runQuery = async (tx, request, queryIndex, addReturning = false) => {
|
902
1033
|
const { vocabulary } = request;
|
903
1034
|
let { sqlQuery } = request;
|
904
1035
|
if (sqlQuery == null) {
|
@@ -914,13 +1045,14 @@ const runQuery = async (tx, request, queryIndex, returningIdField) => {
|
|
914
1045
|
sqlQuery = sqlQuery[queryIndex];
|
915
1046
|
}
|
916
1047
|
const { query, bindings } = sqlQuery;
|
917
|
-
const values = await abstract_sql_1.getAndCheckBindValues(request, bindings);
|
1048
|
+
const values = await (0, abstract_sql_1.getAndCheckBindValues)(request, bindings);
|
918
1049
|
if (module_1.env.DEBUG) {
|
919
1050
|
exports.api[vocabulary].logger.log(query, values);
|
920
1051
|
}
|
1052
|
+
const returningIdField = addReturning && request.affectedIds == null ? getIdField(request) : false;
|
921
1053
|
const sqlResult = await tx.executeSql(query, values, returningIdField);
|
922
1054
|
if (returningIdField) {
|
923
|
-
request.affectedIds
|
1055
|
+
request.affectedIds ??= sqlResult.rows.map((row) => row[returningIdField]);
|
924
1056
|
}
|
925
1057
|
return sqlResult;
|
926
1058
|
};
|
@@ -930,117 +1062,123 @@ const runGet = async (_req, request, tx) => {
|
|
930
1062
|
}
|
931
1063
|
};
|
932
1064
|
const respondGet = async (req, request, result, tx) => {
|
933
|
-
var _a;
|
934
1065
|
const vocab = request.vocabulary;
|
935
1066
|
if (request.sqlQuery != null) {
|
936
|
-
const format =
|
1067
|
+
const format = request.odataQuery.options?.$format;
|
937
1068
|
const metadata = format != null && typeof format === 'object'
|
938
1069
|
? format.metadata
|
939
1070
|
: undefined;
|
940
|
-
const d = await odataResponse.process(vocab, exports.getAbstractSqlModel(request), request.
|
941
|
-
|
1071
|
+
const d = await odataResponse.process(vocab, (0, exports.getAbstractSqlModel)(request), request.originalResourceName, result.rows, { includeMetadata: metadata === 'full' });
|
1072
|
+
const response = {
|
1073
|
+
statusCode: 200,
|
1074
|
+
body: { d },
|
1075
|
+
headers: { 'content-type': 'application/json' },
|
1076
|
+
};
|
1077
|
+
await (0, hooks_1.runHooks)('PRERESPOND', request.hooks, {
|
942
1078
|
req,
|
943
1079
|
request,
|
944
1080
|
result,
|
945
|
-
|
1081
|
+
response,
|
946
1082
|
tx,
|
947
1083
|
});
|
948
|
-
return
|
1084
|
+
return response;
|
949
1085
|
}
|
950
1086
|
else {
|
951
1087
|
if (request.resourceName === '$metadata') {
|
952
1088
|
return {
|
1089
|
+
statusCode: 200,
|
953
1090
|
body: models[vocab].odataMetadata,
|
954
|
-
headers: {
|
1091
|
+
headers: { 'content-type': 'xml' },
|
955
1092
|
};
|
956
1093
|
}
|
957
1094
|
else {
|
958
1095
|
return {
|
959
|
-
|
1096
|
+
statusCode: 404,
|
960
1097
|
};
|
961
1098
|
}
|
962
1099
|
}
|
963
1100
|
};
|
964
1101
|
const runPost = async (_req, request, tx) => {
|
965
|
-
const { rowsAffected, insertId } = await runQuery(tx, request, undefined,
|
1102
|
+
const { rowsAffected, insertId } = await runQuery(tx, request, undefined, true);
|
966
1103
|
if (rowsAffected === 0) {
|
967
1104
|
throw new errors_1.PermissionError();
|
968
1105
|
}
|
969
|
-
await exports.validateModel(tx, request.
|
1106
|
+
await (0, exports.validateModel)(tx, _.last(request.translateVersions), request);
|
970
1107
|
return insertId;
|
971
1108
|
};
|
972
1109
|
const respondPost = async (req, request, id, tx) => {
|
973
|
-
var _a, _b;
|
974
1110
|
const vocab = request.vocabulary;
|
975
|
-
const location = odataResponse.resourceURI(vocab, request.
|
1111
|
+
const location = odataResponse.resourceURI(vocab, request.originalResourceName, id);
|
976
1112
|
if (module_1.env.DEBUG) {
|
977
1113
|
exports.api[vocab].logger.log('Insert ID: ', request.resourceName, id);
|
978
1114
|
}
|
979
1115
|
let result = { d: [{ id }] };
|
980
1116
|
if (location != null &&
|
981
|
-
!['0', 'false'].includes(
|
1117
|
+
!['0', 'false'].includes(request?.odataQuery?.options?.returnResource)) {
|
982
1118
|
try {
|
983
|
-
result = (await exports.runURI('GET', location, undefined, tx, req));
|
1119
|
+
result = (await (0, exports.runURI)('GET', location, undefined, tx, req));
|
984
1120
|
}
|
985
|
-
catch
|
1121
|
+
catch {
|
986
1122
|
}
|
987
1123
|
}
|
988
|
-
|
1124
|
+
const response = {
|
1125
|
+
statusCode: 201,
|
1126
|
+
body: result.d[0],
|
1127
|
+
headers: {
|
1128
|
+
'content-type': 'application/json',
|
1129
|
+
location,
|
1130
|
+
},
|
1131
|
+
};
|
1132
|
+
await (0, hooks_1.runHooks)('PRERESPOND', request.hooks, {
|
989
1133
|
req,
|
990
1134
|
request,
|
991
1135
|
result,
|
1136
|
+
response,
|
992
1137
|
tx,
|
993
1138
|
});
|
994
|
-
return
|
995
|
-
status: 201,
|
996
|
-
body: result.d[0],
|
997
|
-
headers: {
|
998
|
-
contentType: 'application/json',
|
999
|
-
Location: location,
|
1000
|
-
},
|
1001
|
-
};
|
1139
|
+
return response;
|
1002
1140
|
};
|
1003
1141
|
const runPut = async (_req, request, tx) => {
|
1004
|
-
const idField = getIdField(request);
|
1005
1142
|
let rowsAffected;
|
1006
1143
|
if (Array.isArray(request.sqlQuery)) {
|
1007
|
-
({ rowsAffected } = await runQuery(tx, request, 1,
|
1144
|
+
({ rowsAffected } = await runQuery(tx, request, 1, true));
|
1008
1145
|
if (rowsAffected === 0) {
|
1009
|
-
({ rowsAffected } = await runQuery(tx, request, 0,
|
1146
|
+
({ rowsAffected } = await runQuery(tx, request, 0, true));
|
1010
1147
|
}
|
1011
1148
|
}
|
1012
1149
|
else {
|
1013
|
-
({ rowsAffected } = await runQuery(tx, request, undefined,
|
1150
|
+
({ rowsAffected } = await runQuery(tx, request, undefined, true));
|
1014
1151
|
}
|
1015
1152
|
if (rowsAffected > 0) {
|
1016
|
-
await exports.validateModel(tx, request.
|
1153
|
+
await (0, exports.validateModel)(tx, _.last(request.translateVersions), request);
|
1017
1154
|
}
|
1018
1155
|
return undefined;
|
1019
1156
|
};
|
1020
1157
|
const respondPut = async (req, request, result, tx) => {
|
1021
|
-
|
1158
|
+
const response = {
|
1159
|
+
statusCode: 200,
|
1160
|
+
};
|
1161
|
+
await (0, hooks_1.runHooks)('PRERESPOND', request.hooks, {
|
1022
1162
|
req,
|
1023
1163
|
request,
|
1024
1164
|
result,
|
1165
|
+
response,
|
1025
1166
|
tx,
|
1026
1167
|
});
|
1027
|
-
return
|
1028
|
-
status: 200,
|
1029
|
-
headers: {},
|
1030
|
-
};
|
1168
|
+
return response;
|
1031
1169
|
};
|
1032
1170
|
const respondDelete = respondPut;
|
1033
1171
|
const respondOptions = respondPut;
|
1034
1172
|
const runDelete = async (_req, request, tx) => {
|
1035
|
-
const { rowsAffected } = await runQuery(tx, request, undefined,
|
1173
|
+
const { rowsAffected } = await runQuery(tx, request, undefined, true);
|
1036
1174
|
if (rowsAffected > 0) {
|
1037
|
-
await exports.validateModel(tx, request.
|
1175
|
+
await (0, exports.validateModel)(tx, _.last(request.translateVersions), request);
|
1038
1176
|
}
|
1039
1177
|
return undefined;
|
1040
1178
|
};
|
1041
1179
|
const executeStandardModels = async (tx) => {
|
1042
1180
|
try {
|
1043
|
-
await exports.executeModel(tx, {
|
1181
|
+
await (0, exports.executeModel)(tx, {
|
1044
1182
|
apiRoot: 'dev',
|
1045
1183
|
modelText: devModel,
|
1046
1184
|
logging: {
|
@@ -1051,9 +1189,33 @@ const executeStandardModels = async (tx) => {
|
|
1051
1189
|
ALTER TABLE "model"
|
1052
1190
|
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1053
1191
|
`,
|
1192
|
+
'15.0.0-data-types': async ($tx, sbvrUtils) => {
|
1193
|
+
switch (sbvrUtils.db.engine) {
|
1194
|
+
case 'mysql':
|
1195
|
+
await $tx.executeSql(`\
|
1196
|
+
ALTER TABLE "model"
|
1197
|
+
MODIFY "model value" JSON NOT NULL;
|
1198
|
+
|
1199
|
+
UPDATE "model"
|
1200
|
+
SET "model value" = CAST('{"value":' || CAST("model value" AS CHAR) || '}' AS JSON)
|
1201
|
+
WHERE "model type" IN ('se', 'odataMetadata')
|
1202
|
+
AND CAST("model value" AS CHAR) LIKE '"%';`);
|
1203
|
+
break;
|
1204
|
+
case 'postgres':
|
1205
|
+
await $tx.executeSql(`\
|
1206
|
+
ALTER TABLE "model"
|
1207
|
+
ALTER COLUMN "model value" SET DATA TYPE JSONB USING "model value"::JSONB;
|
1208
|
+
|
1209
|
+
UPDATE "model"
|
1210
|
+
SET "model value" = CAST('{"value":' || CAST("model value" AS TEXT) || '}' AS JSON)
|
1211
|
+
WHERE "model type" IN ('se', 'odataMetadata')
|
1212
|
+
AND CAST("model value" AS TEXT) LIKE '"%';`);
|
1213
|
+
break;
|
1214
|
+
}
|
1215
|
+
},
|
1054
1216
|
},
|
1055
1217
|
});
|
1056
|
-
await exports.executeModels(tx, permissions.config.models);
|
1218
|
+
await (0, exports.executeModels)(tx, permissions.config.models);
|
1057
1219
|
console.info('Successfully executed standard models.');
|
1058
1220
|
}
|
1059
1221
|
catch (err) {
|
@@ -1066,7 +1228,7 @@ const setup = async (_app, $db) => {
|
|
1066
1228
|
exports.db = exports.db = $db;
|
1067
1229
|
try {
|
1068
1230
|
await exports.db.transaction(async (tx) => {
|
1069
|
-
await exports.executeStandardModels(tx);
|
1231
|
+
await (0, exports.executeStandardModels)(tx);
|
1070
1232
|
await permissions.setup();
|
1071
1233
|
});
|
1072
1234
|
}
|
@@ -1077,7 +1239,7 @@ const setup = async (_app, $db) => {
|
|
1077
1239
|
try {
|
1078
1240
|
await exports.db.executeSql('CREATE UNIQUE INDEX "uniq_model_model_type_vocab" ON "model" ("is of-vocabulary", "model type");');
|
1079
1241
|
}
|
1080
|
-
catch
|
1242
|
+
catch {
|
1081
1243
|
}
|
1082
1244
|
};
|
1083
1245
|
exports.setup = setup;
|