@balena/pinejs 17.1.0-build-model-based-typings-437bb06f44567532aec78e550f3d545732466411-1 → 17.1.0-build-joshbwlng-tasks-61ce10e444abec6afea3fec43e9a5c37c7cedea6-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/.pinejs-cache.json +1 -1
- package/.versionbot/CHANGELOG.yml +13 -239
- package/CHANGELOG.md +5 -69
- package/out/config-loader/env.d.ts +4 -0
- package/out/config-loader/env.js +5 -1
- package/out/config-loader/env.js.map +1 -1
- package/out/data-server/sbvr-server.js +2 -3
- package/out/data-server/sbvr-server.js.map +1 -1
- package/out/database-layer/db.d.ts +3 -0
- package/out/database-layer/db.js +17 -0
- package/out/database-layer/db.js.map +1 -1
- package/out/migrator/sync.d.ts +0 -17
- package/out/migrator/sync.js +40 -39
- package/out/migrator/sync.js.map +1 -1
- package/out/sbvr-api/hooks.d.ts +33 -33
- package/out/sbvr-api/hooks.js.map +1 -1
- package/out/sbvr-api/odata-response.d.ts +2 -1
- package/out/sbvr-api/odata-response.js +4 -4
- package/out/sbvr-api/odata-response.js.map +1 -1
- package/out/sbvr-api/permissions.d.ts +2 -26
- package/out/sbvr-api/permissions.js +40 -39
- package/out/sbvr-api/permissions.js.map +1 -1
- package/out/sbvr-api/sbvr-utils.d.ts +6 -46
- package/out/sbvr-api/sbvr-utils.js +76 -73
- package/out/sbvr-api/sbvr-utils.js.map +1 -1
- package/out/server-glue/module.d.ts +1 -0
- package/out/server-glue/module.js +4 -1
- package/out/server-glue/module.js.map +1 -1
- package/out/tasks/common.d.ts +4 -0
- package/out/tasks/common.js +13 -0
- package/out/tasks/common.js.map +1 -0
- package/out/tasks/index.d.ts +8 -0
- package/out/tasks/index.js +142 -0
- package/out/tasks/index.js.map +1 -0
- package/out/tasks/tasks.sbvr +60 -0
- package/out/tasks/types.d.ts +38 -0
- package/out/tasks/types.js +10 -0
- package/out/tasks/types.js.map +1 -0
- package/out/tasks/worker.d.ts +16 -0
- package/out/tasks/worker.js +228 -0
- package/out/tasks/worker.js.map +1 -0
- package/package.json +20 -19
- package/src/config-loader/env.ts +6 -1
- package/src/data-server/sbvr-server.js +2 -3
- package/src/database-layer/db.ts +25 -0
- package/src/migrator/sync.ts +41 -46
- package/src/sbvr-api/hooks.ts +20 -21
- package/src/sbvr-api/odata-response.ts +13 -3
- package/src/sbvr-api/permissions.ts +48 -54
- package/src/sbvr-api/sbvr-utils.ts +92 -133
- package/src/server-glue/module.ts +3 -0
- package/src/tasks/common.ts +14 -0
- package/src/tasks/index.ts +158 -0
- package/src/tasks/tasks.sbvr +60 -0
- package/src/tasks/types.ts +58 -0
- package/src/tasks/worker.ts +278 -0
- package/out/migrator/migrations.d.ts +0 -58
- package/out/migrator/migrations.js +0 -3
- package/out/migrator/migrations.js.map +0 -1
- package/out/sbvr-api/dev.d.ts +0 -22
- package/out/sbvr-api/dev.js +0 -3
- package/out/sbvr-api/dev.js.map +0 -1
- package/out/sbvr-api/user.d.ts +0 -236
- package/out/sbvr-api/user.js +0 -3
- package/out/sbvr-api/user.js.map +0 -1
- package/src/migrator/migrations.ts +0 -64
- package/src/sbvr-api/dev.ts +0 -26
- package/src/sbvr-api/user.ts +0 -216
@@ -0,0 +1,228 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
19
|
+
if (mod && mod.__esModule) return mod;
|
20
|
+
var result = {};
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
22
|
+
__setModuleDefault(result, mod);
|
23
|
+
return result;
|
24
|
+
};
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
26
|
+
exports.Worker = void 0;
|
27
|
+
const promises_1 = require("node:timers/promises");
|
28
|
+
const env_1 = require("../config-loader/env");
|
29
|
+
const db_1 = require("../database-layer/db");
|
30
|
+
const permissions = __importStar(require("../sbvr-api/permissions"));
|
31
|
+
const sbvr_utils_1 = require("../sbvr-api/sbvr-utils");
|
32
|
+
const module_1 = require("../server-glue/module");
|
33
|
+
const common_1 = require("./common");
|
34
|
+
const selectColumns = Object.entries({
|
35
|
+
id: 'id',
|
36
|
+
'is executed by-handler': 'is_executed_by__handler',
|
37
|
+
'is executed with-parameter set': 'is_executed_with__parameter_set',
|
38
|
+
'is scheduled with-cron expression': 'is_scheduled_with__cron_expression',
|
39
|
+
'attempt count': 'attempt_count',
|
40
|
+
'attempt limit': 'attempt_limit',
|
41
|
+
priority: 'priority',
|
42
|
+
'is created by-actor': 'is_created_by__actor',
|
43
|
+
})
|
44
|
+
.map(([key, value]) => `t."${key}" AS "${value}"`)
|
45
|
+
.join(', ');
|
46
|
+
class Worker {
|
47
|
+
client;
|
48
|
+
handlers = {};
|
49
|
+
concurrency;
|
50
|
+
interval;
|
51
|
+
executing = 0;
|
52
|
+
constructor(client) {
|
53
|
+
this.client = client;
|
54
|
+
this.concurrency = env_1.tasks.queueConcurrency;
|
55
|
+
this.interval = env_1.tasks.queueIntervalMS;
|
56
|
+
}
|
57
|
+
canExecute() {
|
58
|
+
return (this.executing < this.concurrency && Object.keys(this.handlers).length > 0);
|
59
|
+
}
|
60
|
+
async execute(task, tx) {
|
61
|
+
this.executing++;
|
62
|
+
try {
|
63
|
+
const handler = this.handlers[task.is_executed_by__handler];
|
64
|
+
const startedOnTime = new Date();
|
65
|
+
if (handler == null) {
|
66
|
+
await this.finalize(tx, task, startedOnTime, 'failed', 'Matching task handler not found');
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
if (handler.validate != null &&
|
70
|
+
!handler.validate(task.is_executed_with__parameter_set)) {
|
71
|
+
await this.finalize(tx, task, startedOnTime, 'failed', `Invalid parameter set: ${common_1.ajv.errorsText(handler.validate.errors)}`);
|
72
|
+
return;
|
73
|
+
}
|
74
|
+
let status = 'queued';
|
75
|
+
let error;
|
76
|
+
try {
|
77
|
+
await module_1.sbvrUtils.db.transaction(async (handlerTx) => {
|
78
|
+
const results = await handler.fn({
|
79
|
+
api: new sbvr_utils_1.PinejsClient({
|
80
|
+
passthrough: {
|
81
|
+
tx: handlerTx,
|
82
|
+
},
|
83
|
+
}),
|
84
|
+
params: task.is_executed_with__parameter_set ?? {},
|
85
|
+
tx: handlerTx,
|
86
|
+
});
|
87
|
+
status = results.status;
|
88
|
+
error = results.error;
|
89
|
+
if (results.status !== 'succeeded' && !handlerTx.isClosed()) {
|
90
|
+
await handlerTx.rollback();
|
91
|
+
}
|
92
|
+
});
|
93
|
+
}
|
94
|
+
catch (err) {
|
95
|
+
if (!(err instanceof db_1.TransactionClosedError)) {
|
96
|
+
throw err;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
finally {
|
100
|
+
await this.finalize(tx, task, startedOnTime, status, error);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
catch (err) {
|
104
|
+
console.error(`Failed to execute task ${task.id} with handler ${task.is_executed_by__handler}:`, err);
|
105
|
+
process.exit(1);
|
106
|
+
}
|
107
|
+
finally {
|
108
|
+
this.executing--;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
async finalize(tx, task, startedOnTime, status, errorMessage) {
|
112
|
+
const attemptCount = task.attempt_count + 1;
|
113
|
+
const body = {
|
114
|
+
started_on__time: startedOnTime,
|
115
|
+
ended_on__time: new Date(),
|
116
|
+
status,
|
117
|
+
attempt_count: attemptCount,
|
118
|
+
...(errorMessage != null && { error_message: errorMessage }),
|
119
|
+
};
|
120
|
+
if (status === 'failed' && attemptCount < task.attempt_limit) {
|
121
|
+
body.status = 'queued';
|
122
|
+
body.is_scheduled_to_execute_on__time =
|
123
|
+
this.getNextAttemptTime(attemptCount);
|
124
|
+
}
|
125
|
+
await this.client.patch({
|
126
|
+
resource: 'task',
|
127
|
+
passthrough: {
|
128
|
+
tx,
|
129
|
+
req: permissions.root,
|
130
|
+
},
|
131
|
+
id: task.id,
|
132
|
+
body,
|
133
|
+
});
|
134
|
+
if (['failed', 'succeeded'].includes(body.status) &&
|
135
|
+
task.is_scheduled_with__cron_expression != null) {
|
136
|
+
await this.client.post({
|
137
|
+
resource: 'task',
|
138
|
+
passthrough: {
|
139
|
+
tx,
|
140
|
+
req: permissions.root,
|
141
|
+
},
|
142
|
+
options: {
|
143
|
+
returnResource: false,
|
144
|
+
},
|
145
|
+
body: {
|
146
|
+
attempt_limit: task.attempt_limit,
|
147
|
+
is_created_by__actor: task.is_created_by__actor,
|
148
|
+
is_executed_by__handler: task.is_executed_by__handler,
|
149
|
+
is_executed_with__parameter_set: task.is_executed_with__parameter_set,
|
150
|
+
is_scheduled_with__cron_expression: task.is_scheduled_with__cron_expression,
|
151
|
+
priority: task.priority,
|
152
|
+
},
|
153
|
+
});
|
154
|
+
}
|
155
|
+
}
|
156
|
+
getNextAttemptTime(attempt) {
|
157
|
+
const delay = Math.ceil(Math.exp(Math.min(10, attempt)));
|
158
|
+
return new Date(Date.now() + delay);
|
159
|
+
}
|
160
|
+
poll() {
|
161
|
+
let executed = false;
|
162
|
+
void (async () => {
|
163
|
+
try {
|
164
|
+
const handlerNames = Object.keys(this.handlers);
|
165
|
+
const binds = handlerNames
|
166
|
+
.map((_, index) => `$${index + 1}`)
|
167
|
+
.join(', ');
|
168
|
+
if (!this.canExecute()) {
|
169
|
+
return;
|
170
|
+
}
|
171
|
+
await module_1.sbvrUtils.db.transaction(async (tx) => {
|
172
|
+
const result = await tx.executeSql(`SELECT ${selectColumns}
|
173
|
+
FROM task AS t
|
174
|
+
WHERE
|
175
|
+
t."is executed by-handler" IN (${binds}) AND
|
176
|
+
t."status" = 'queued' AND
|
177
|
+
t."attempt count" <= t."attempt limit" AND
|
178
|
+
(
|
179
|
+
t."is scheduled to execute on-time" IS NULL OR
|
180
|
+
t."is scheduled to execute on-time" <= CURRENT_TIMESTAMP + INTERVAL '${Math.ceil(this.interval / 1000)} second'
|
181
|
+
)
|
182
|
+
ORDER BY
|
183
|
+
t."is scheduled to execute on-time" ASC,
|
184
|
+
t."priority" DESC,
|
185
|
+
t."id" ASC
|
186
|
+
LIMIT ${Math.max(this.concurrency - this.executing, 0)}
|
187
|
+
FOR UPDATE SKIP LOCKED`, handlerNames);
|
188
|
+
if (result.rows.length === 0) {
|
189
|
+
return;
|
190
|
+
}
|
191
|
+
await Promise.all(result.rows.map(async (row) => {
|
192
|
+
await this.execute(row, tx);
|
193
|
+
}));
|
194
|
+
executed = true;
|
195
|
+
});
|
196
|
+
}
|
197
|
+
catch (err) {
|
198
|
+
console.error('Failed polling for tasks:', err);
|
199
|
+
}
|
200
|
+
finally {
|
201
|
+
if (!executed) {
|
202
|
+
await (0, promises_1.setTimeout)(this.interval);
|
203
|
+
}
|
204
|
+
this.poll();
|
205
|
+
}
|
206
|
+
})();
|
207
|
+
}
|
208
|
+
start() {
|
209
|
+
if (module_1.sbvrUtils.db.engine !== 'postgres' || module_1.sbvrUtils.db.on == null) {
|
210
|
+
throw new Error('Database does not support tasks, giving up on starting worker');
|
211
|
+
}
|
212
|
+
module_1.sbvrUtils.db.on('notification', async (msg) => {
|
213
|
+
if (this.canExecute()) {
|
214
|
+
await module_1.sbvrUtils.db.transaction(async (tx) => {
|
215
|
+
const result = await tx.executeSql(`SELECT ${selectColumns} FROM task AS t WHERE id = $1 FOR UPDATE SKIP LOCKED`, [msg.payload]);
|
216
|
+
if (result.rows.length > 0) {
|
217
|
+
await this.execute(result.rows[0], tx);
|
218
|
+
}
|
219
|
+
});
|
220
|
+
}
|
221
|
+
}, {
|
222
|
+
channel: common_1.channel,
|
223
|
+
});
|
224
|
+
this.poll();
|
225
|
+
}
|
226
|
+
}
|
227
|
+
exports.Worker = Worker;
|
228
|
+
//# sourceMappingURL=worker.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/tasks/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@balena/pinejs",
|
3
|
-
"version": "17.1.0-build-
|
3
|
+
"version": "17.1.0-build-joshbwlng-tasks-61ce10e444abec6afea3fec43e9a5c37c7cedea6-1",
|
4
4
|
"main": "out/server-glue/module",
|
5
5
|
"type": "commonjs",
|
6
6
|
"repository": "git@github.com:balena-io/pinejs.git",
|
@@ -19,48 +19,48 @@
|
|
19
19
|
"webpack-server": "grunt server",
|
20
20
|
"webpack-build": "npm run webpack-browser && npm run webpack-module && npm run webpack-server",
|
21
21
|
"lint": "balena-lint -t tsconfig.dev.json -e js -e ts src build typings Gruntfile.ts && npx tsc --project tsconfig.dev.json --noEmit",
|
22
|
-
"test": "npm run lint && npm run build && npm run webpack-build && npm run test:compose
|
23
|
-
"test:compose": "trap 'docker compose -f docker-compose.npm-test.yml down ; echo Stopped ; exit 0' INT; docker compose -f docker-compose.npm-test.yml up -d && sleep 2 && DATABASE_URL=postgres://docker:docker@localhost:5431/postgres PINEJS_WEBRESOURCE_MAXFILESIZE=1000000000 S3_ENDPOINT=http://localhost:43680 S3_ACCESS_KEY=USERNAME S3_SECRET_KEY=PASSWORD S3_STORAGE_ADAPTER_BUCKET=balena-pine-web-resources S3_REGION=us-east-1 npm run mocha",
|
24
|
-
"test:generated-types": "npm run generate-types && git diff --exit-code ./src/sbvr-api/user.ts ./src/migrator/migrations.ts ./src/sbvr-api/dev.ts",
|
22
|
+
"test": "npm run lint && npm run build && npm run webpack-build && npm run test:compose",
|
23
|
+
"test:compose": "trap 'docker compose -f docker-compose.npm-test.yml down ; echo Stopped ; exit 0' INT; docker compose -f docker-compose.npm-test.yml up -d && sleep 2 && DATABASE_URL=postgres://docker:docker@localhost:5431/postgres PINEJS_WEBRESOURCE_MAXFILESIZE=1000000000 S3_ENDPOINT=http://localhost:43680 S3_ACCESS_KEY=USERNAME S3_SECRET_KEY=PASSWORD S3_STORAGE_ADAPTER_BUCKET=balena-pine-web-resources S3_REGION=us-east-1 PINEJS_QUEUE_CONCURRENCY=1 npm run mocha",
|
25
24
|
"mocha": "TS_NODE_FILES=true mocha",
|
26
|
-
"prettify": "balena-lint -t tsconfig.dev.json -e js -e ts --fix src test build typings Gruntfile.ts"
|
27
|
-
"generate-types": "node ./bin/sbvr-compiler.js generate-types ./src/sbvr-api/user.sbvr ./src/sbvr-api/user.ts && node ./bin/sbvr-compiler.js generate-types ./src/migrator/migrations.sbvr ./src/migrator/migrations.ts && node ./bin/sbvr-compiler.js generate-types ./src/sbvr-api/dev.sbvr ./src/sbvr-api/dev.ts && balena-lint -t tsconfig.dev.json --fix ./src/sbvr-api/user.ts ./src/migrator/migrations.ts ./src/sbvr-api/dev.ts"
|
25
|
+
"prettify": "balena-lint -t tsconfig.dev.json -e js -e ts --fix src test build typings Gruntfile.ts"
|
28
26
|
},
|
29
27
|
"dependencies": {
|
30
|
-
"@balena/abstract-sql-compiler": "^9.
|
31
|
-
"@balena/abstract-sql-to-typescript": "^3.
|
28
|
+
"@balena/abstract-sql-compiler": "^9.1.4",
|
29
|
+
"@balena/abstract-sql-to-typescript": "^3.1.1",
|
32
30
|
"@balena/env-parsing": "^1.1.12",
|
33
31
|
"@balena/lf-to-abstract-sql": "^5.0.2",
|
34
32
|
"@balena/odata-parser": "^3.0.8",
|
35
33
|
"@balena/odata-to-abstract-sql": "^6.2.7",
|
36
34
|
"@balena/sbvr-parser": "^1.4.4",
|
37
|
-
"@balena/sbvr-types": "^7.1.
|
35
|
+
"@balena/sbvr-types": "^7.1.1",
|
38
36
|
"@types/body-parser": "^1.19.5",
|
39
37
|
"@types/compression": "^1.7.5",
|
40
38
|
"@types/cookie-parser": "^1.4.7",
|
41
39
|
"@types/deep-freeze": "^0.1.5",
|
42
40
|
"@types/express": "^4.17.21",
|
43
41
|
"@types/express-session": "^1.18.0",
|
44
|
-
"@types/lodash": "^4.17.
|
42
|
+
"@types/lodash": "^4.17.4",
|
45
43
|
"@types/memoizee": "^0.4.11",
|
46
44
|
"@types/method-override": "^0.0.35",
|
47
45
|
"@types/multer": "^1.4.11",
|
48
46
|
"@types/mysql": "^2.15.26",
|
49
|
-
"@types/node": "^20.14.
|
47
|
+
"@types/node": "^20.14.2",
|
50
48
|
"@types/passport": "^1.0.16",
|
51
49
|
"@types/passport-local": "^1.0.38",
|
52
50
|
"@types/passport-strategy": "^0.2.38",
|
53
51
|
"@types/pg": "^8.11.6",
|
54
52
|
"@types/randomstring": "^1.3.0",
|
55
53
|
"@types/websql": "^0.0.30",
|
54
|
+
"ajv": "^8.12.0",
|
56
55
|
"busboy": "^1.6.0",
|
57
56
|
"commander": "^11.1.0",
|
57
|
+
"cron-parser": "^4.9.0",
|
58
58
|
"deep-freeze": "^0.0.1",
|
59
59
|
"eventemitter3": "^5.0.1",
|
60
60
|
"express-session": "^1.18.0",
|
61
61
|
"lodash": "^4.17.21",
|
62
62
|
"memoizee": "^0.4.17",
|
63
|
-
"pinejs-client-core": "^6.
|
63
|
+
"pinejs-client-core": "^6.14.6",
|
64
64
|
"randomstring": "^1.3.0",
|
65
65
|
"typed-error": "^3.2.2"
|
66
66
|
},
|
@@ -91,11 +91,12 @@
|
|
91
91
|
"grunt-ts": "^6.0.0-beta.22",
|
92
92
|
"grunt-webpack": "^6.0.0",
|
93
93
|
"husky": "^9.0.11",
|
94
|
-
"
|
94
|
+
"json-schema-to-ts": "^3.1.0",
|
95
|
+
"lint-staged": "^15.2.5",
|
95
96
|
"load-grunt-tasks": "^5.1.0",
|
96
97
|
"mocha": "^10.4.0",
|
97
98
|
"on-finished": "^2.4.1",
|
98
|
-
"pinejs-client-supertest": "^2.0.
|
99
|
+
"pinejs-client-supertest": "^2.0.3",
|
99
100
|
"raw-loader": "^4.0.2",
|
100
101
|
"request": "^2.88.2",
|
101
102
|
"require-npm4-to-publish": "^1.0.0",
|
@@ -104,13 +105,13 @@
|
|
104
105
|
"ts-loader": "^9.5.1",
|
105
106
|
"ts-node": "^10.9.2",
|
106
107
|
"typescript": "^5.4.5",
|
107
|
-
"webpack": "^5.
|
108
|
+
"webpack": "^5.91.0",
|
108
109
|
"webpack-dev-server": "^4.15.2"
|
109
110
|
},
|
110
111
|
"optionalDependencies": {
|
111
|
-
"@aws-sdk/client-s3": "^3.
|
112
|
-
"@aws-sdk/lib-storage": "^3.
|
113
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
112
|
+
"@aws-sdk/client-s3": "^3.590.0",
|
113
|
+
"@aws-sdk/lib-storage": "^3.590.0",
|
114
|
+
"@aws-sdk/s3-request-presigner": "^3.590.0",
|
114
115
|
"bcrypt": "^5.1.1",
|
115
116
|
"body-parser": "^1.20.2",
|
116
117
|
"compression": "^1.7.4",
|
@@ -146,6 +147,6 @@
|
|
146
147
|
"recursive": true
|
147
148
|
},
|
148
149
|
"versionist": {
|
149
|
-
"publishedAt": "2024-06-
|
150
|
+
"publishedAt": "2024-06-19T04:57:00.974Z"
|
150
151
|
}
|
151
152
|
}
|
package/src/config-loader/env.ts
CHANGED
@@ -49,7 +49,7 @@ export const cache = {
|
|
49
49
|
apiKeyActorId: false as CacheOpts,
|
50
50
|
};
|
51
51
|
|
52
|
-
import { boolVar } from '@balena/env-parsing';
|
52
|
+
import { boolVar, intVar } from '@balena/env-parsing';
|
53
53
|
import memoize from 'memoizee';
|
54
54
|
import memoizeWeak = require('memoizee/weak');
|
55
55
|
export const createCache = <T extends (...args: any[]) => any>(
|
@@ -146,3 +146,8 @@ export const migrator = {
|
|
146
146
|
*/
|
147
147
|
asyncMigrationIsEnabled: boolVar('PINEJS_ASYNC_MIGRATION_ENABLED', true),
|
148
148
|
};
|
149
|
+
|
150
|
+
export const tasks = {
|
151
|
+
queueConcurrency: intVar('PINEJS_QUEUE_CONCURRENCY', 0),
|
152
|
+
queueIntervalMS: intVar('PINEJS_QUEUE_INTERVAL_MS', 1000),
|
153
|
+
};
|
@@ -133,15 +133,14 @@ export async function setup(app, sbvrUtils, db) {
|
|
133
133
|
},
|
134
134
|
},
|
135
135
|
})
|
136
|
-
.then(async (result) => {
|
136
|
+
.then(async (/** @type { Array<{ [key: string]: any }> } */ result) => {
|
137
137
|
if (result.length === 0) {
|
138
138
|
throw new Error('No SE data model found');
|
139
139
|
}
|
140
140
|
const instance = result[0];
|
141
141
|
await sbvrUtils.executeModel(tx, {
|
142
142
|
apiRoot: instance.is_of__vocabulary,
|
143
|
-
|
144
|
-
modelText: /** @type { string } */ (instance.model_value.value),
|
143
|
+
modelText: instance.model_value.value,
|
145
144
|
});
|
146
145
|
});
|
147
146
|
await isServerOnAir(true);
|
package/src/database-layer/db.ts
CHANGED
@@ -98,6 +98,13 @@ export interface Database extends BaseDatabase {
|
|
98
98
|
) => Promise<Result>;
|
99
99
|
transaction: TransactionFn;
|
100
100
|
readTransaction: TransactionFn;
|
101
|
+
on?: (
|
102
|
+
name: 'notification',
|
103
|
+
fn: (...args: any[]) => Promise<void>,
|
104
|
+
options?: {
|
105
|
+
channel?: string;
|
106
|
+
},
|
107
|
+
) => void;
|
101
108
|
}
|
102
109
|
|
103
110
|
interface EngineParams {
|
@@ -689,6 +696,24 @@ if (maybePg != null) {
|
|
689
696
|
return {
|
690
697
|
engine: Engines.postgres,
|
691
698
|
executeSql: atomicExecuteSql,
|
699
|
+
on: async (name, fn, options) => {
|
700
|
+
if (name === 'notification' && options?.channel === undefined) {
|
701
|
+
throw new Error('Missing channel option for notification listener');
|
702
|
+
}
|
703
|
+
|
704
|
+
const client = await pool.connect();
|
705
|
+
client.on(name, async (msg) => {
|
706
|
+
try {
|
707
|
+
await fn(msg);
|
708
|
+
} catch (error) {
|
709
|
+
console.error('Error handling message:', error);
|
710
|
+
}
|
711
|
+
});
|
712
|
+
|
713
|
+
if (name === 'notification' && options?.channel !== undefined) {
|
714
|
+
await client.query(`LISTEN "${options.channel}";`);
|
715
|
+
}
|
716
|
+
},
|
692
717
|
transaction: createTransaction(async (stackTraceErr) => {
|
693
718
|
const client = await pool.connect();
|
694
719
|
const tx = new PostgresTx(client, false, stackTraceErr);
|
package/src/migrator/sync.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import type MigrationsModel from './migrations';
|
2
1
|
import {
|
3
2
|
type MigrationTuple,
|
4
3
|
MigrationError,
|
@@ -17,7 +16,7 @@ import _ from 'lodash';
|
|
17
16
|
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
18
17
|
|
19
18
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
20
|
-
const
|
19
|
+
const modelText = require('./migrations.sbvr');
|
21
20
|
|
22
21
|
type ApiRootModel = Model & { apiRoot: string };
|
23
22
|
|
@@ -137,49 +136,45 @@ const executeMigration = async (
|
|
137
136
|
}
|
138
137
|
};
|
139
138
|
|
140
|
-
declare module '../sbvr-api/sbvr-utils' {
|
141
|
-
export interface API {
|
142
|
-
[migrationModelConfig.apiRoot]: PinejsClient<MigrationsModel>;
|
143
|
-
}
|
144
|
-
}
|
145
|
-
const migrationModelConfig = {
|
146
|
-
modelName: 'migrations',
|
147
|
-
apiRoot: 'migrations',
|
148
|
-
modelText: migrationsModel,
|
149
|
-
migrations: {
|
150
|
-
'11.0.0-modified-at': `
|
151
|
-
ALTER TABLE "migration"
|
152
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
153
|
-
`,
|
154
|
-
'11.0.1-modified-at': `
|
155
|
-
ALTER TABLE "migration lock"
|
156
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
157
|
-
`,
|
158
|
-
'15.0.0-data-types': async (tx, { db }) => {
|
159
|
-
switch (db.engine) {
|
160
|
-
case 'mysql':
|
161
|
-
await tx.executeSql(`\
|
162
|
-
ALTER TABLE "migration"
|
163
|
-
MODIFY "executed migrations" JSON NOT NULL;`);
|
164
|
-
await tx.executeSql(`\
|
165
|
-
ALTER TABLE "migration status"
|
166
|
-
MODIFY "is backing off" BOOLEAN NOT NULL;`);
|
167
|
-
break;
|
168
|
-
case 'postgres':
|
169
|
-
await tx.executeSql(`\
|
170
|
-
ALTER TABLE "migration"
|
171
|
-
ALTER COLUMN "executed migrations" SET DATA TYPE JSONB USING "executed migrations"::JSONB;`);
|
172
|
-
await tx.executeSql(`\
|
173
|
-
ALTER TABLE "migration status"
|
174
|
-
ALTER COLUMN "is backing off" DROP DEFAULT,
|
175
|
-
ALTER COLUMN "is backing off" SET DATA TYPE BOOLEAN USING "is backing off"::BOOLEAN,
|
176
|
-
ALTER COLUMN "is backing off" SET DEFAULT FALSE;`);
|
177
|
-
break;
|
178
|
-
// No need to migrate for websql
|
179
|
-
}
|
180
|
-
},
|
181
|
-
},
|
182
|
-
} as const satisfies sbvrUtils.ExecutableModel;
|
183
139
|
export const config: Config = {
|
184
|
-
models: [
|
140
|
+
models: [
|
141
|
+
{
|
142
|
+
modelName: 'migrations',
|
143
|
+
apiRoot: 'migrations',
|
144
|
+
modelText,
|
145
|
+
migrations: {
|
146
|
+
'11.0.0-modified-at': `
|
147
|
+
ALTER TABLE "migration"
|
148
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
149
|
+
`,
|
150
|
+
'11.0.1-modified-at': `
|
151
|
+
ALTER TABLE "migration lock"
|
152
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
153
|
+
`,
|
154
|
+
'15.0.0-data-types': async (tx, { db }) => {
|
155
|
+
switch (db.engine) {
|
156
|
+
case 'mysql':
|
157
|
+
await tx.executeSql(`\
|
158
|
+
ALTER TABLE "migration"
|
159
|
+
MODIFY "executed migrations" JSON NOT NULL;`);
|
160
|
+
await tx.executeSql(`\
|
161
|
+
ALTER TABLE "migration status"
|
162
|
+
MODIFY "is backing off" BOOLEAN NOT NULL;`);
|
163
|
+
break;
|
164
|
+
case 'postgres':
|
165
|
+
await tx.executeSql(`\
|
166
|
+
ALTER TABLE "migration"
|
167
|
+
ALTER COLUMN "executed migrations" SET DATA TYPE JSONB USING "executed migrations"::JSONB;`);
|
168
|
+
await tx.executeSql(`\
|
169
|
+
ALTER TABLE "migration status"
|
170
|
+
ALTER COLUMN "is backing off" DROP DEFAULT,
|
171
|
+
ALTER COLUMN "is backing off" SET DATA TYPE BOOLEAN USING "is backing off"::BOOLEAN,
|
172
|
+
ALTER COLUMN "is backing off" SET DEFAULT FALSE;`);
|
173
|
+
break;
|
174
|
+
// No need to migrate for websql
|
175
|
+
}
|
176
|
+
},
|
177
|
+
},
|
178
|
+
},
|
179
|
+
],
|
185
180
|
};
|
package/src/sbvr-api/hooks.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import type { OptionalField, Resolvable } from './common-types';
|
2
|
-
import type { Tx } from '../database-layer/db';
|
2
|
+
import type { Result, Tx } from '../database-layer/db';
|
3
3
|
import type { ODataRequest, ParsedODataRequest } from './uri-parser';
|
4
4
|
import type { AnyObject } from 'pinejs-client-core';
|
5
5
|
import type { TypedError } from 'typed-error';
|
@@ -9,6 +9,7 @@ import _ from 'lodash';
|
|
9
9
|
import { settleMapSeries } from './control-flow';
|
10
10
|
import memoize from 'memoizee';
|
11
11
|
import {
|
12
|
+
type PinejsClient,
|
12
13
|
type User,
|
13
14
|
type ApiKey,
|
14
15
|
resolveSynonym,
|
@@ -30,36 +31,34 @@ export interface HookReq {
|
|
30
31
|
hooks?: InstantiatedHooks;
|
31
32
|
is?: (type: string | string[]) => string | false | null;
|
32
33
|
}
|
33
|
-
export interface HookArgs
|
34
|
+
export interface HookArgs {
|
34
35
|
req: HookReq;
|
35
36
|
request: ODataRequest;
|
36
|
-
api:
|
37
|
+
api: PinejsClient;
|
37
38
|
tx?: Tx | undefined;
|
38
39
|
}
|
39
40
|
export type HookResponse = PromiseLike<any> | null | void;
|
40
41
|
|
41
|
-
export interface Hooks
|
42
|
-
PREPARSE?: (
|
43
|
-
|
44
|
-
) => HookResponse;
|
45
|
-
POSTPARSE?: (options: HookArgs<Vocab>) => HookResponse;
|
46
|
-
PRERUN?: (options: HookArgs<Vocab> & { tx: Tx }) => HookResponse;
|
42
|
+
export interface Hooks {
|
43
|
+
PREPARSE?: (options: Omit<HookArgs, 'request' | 'api'>) => HookResponse;
|
44
|
+
POSTPARSE?: (options: HookArgs) => HookResponse;
|
45
|
+
PRERUN?: (options: HookArgs & { tx: Tx }) => HookResponse;
|
47
46
|
/** These are run in reverse translation order from newest to oldest */
|
48
47
|
POSTRUN?: (
|
49
|
-
options: HookArgs
|
48
|
+
options: HookArgs & { tx: Tx; result: Result | number | undefined },
|
50
49
|
) => HookResponse;
|
51
50
|
/** These are run in reverse translation order from newest to oldest */
|
52
51
|
PRERESPOND?: (
|
53
|
-
options: HookArgs
|
52
|
+
options: HookArgs & {
|
54
53
|
tx: Tx;
|
55
|
-
result
|
54
|
+
result?: Result | number | AnyObject;
|
56
55
|
/** This can be mutated to modify the response sent to the client */
|
57
56
|
response: Response;
|
58
57
|
},
|
59
58
|
) => HookResponse;
|
60
59
|
/** These are run in reverse translation order from newest to oldest */
|
61
60
|
'POSTRUN-ERROR'?: (
|
62
|
-
options: HookArgs
|
61
|
+
options: HookArgs & { tx: Tx; error: TypedError | any },
|
63
62
|
) => HookResponse;
|
64
63
|
}
|
65
64
|
export type HookBlueprints = {
|
@@ -265,9 +264,9 @@ const apiHooks = {
|
|
265
264
|
// Share hooks between merge and patch since they are the same operation,
|
266
265
|
// just MERGE was the OData intermediary until the HTTP spec added PATCH.
|
267
266
|
apiHooks.MERGE = apiHooks.PATCH;
|
268
|
-
export const addHook =
|
267
|
+
export const addHook = (
|
269
268
|
method: keyof typeof apiHooks,
|
270
|
-
vocabulary:
|
269
|
+
vocabulary: string,
|
271
270
|
resourceName: string,
|
272
271
|
hooks:
|
273
272
|
| { [key in keyof Hooks]: HookBlueprint<NonNullable<Hooks[key]>> }
|
@@ -346,11 +345,11 @@ export const addHook = <Vocab extends string>(
|
|
346
345
|
getHooks.clear();
|
347
346
|
};
|
348
347
|
|
349
|
-
export const addSideEffectHook =
|
348
|
+
export const addSideEffectHook = (
|
350
349
|
method: HookMethod,
|
351
|
-
apiRoot:
|
350
|
+
apiRoot: string,
|
352
351
|
resourceName: string,
|
353
|
-
hooks: Hooks
|
352
|
+
hooks: Hooks,
|
354
353
|
): void => {
|
355
354
|
addHook(method, apiRoot, resourceName, {
|
356
355
|
...hooks,
|
@@ -359,11 +358,11 @@ export const addSideEffectHook = <Vocab extends string>(
|
|
359
358
|
});
|
360
359
|
};
|
361
360
|
|
362
|
-
export const addPureHook =
|
361
|
+
export const addPureHook = (
|
363
362
|
method: HookMethod,
|
364
|
-
apiRoot:
|
363
|
+
apiRoot: string,
|
365
364
|
resourceName: string,
|
366
|
-
hooks: Hooks
|
365
|
+
hooks: Hooks,
|
367
366
|
): void => {
|
368
367
|
addHook(method, apiRoot, resourceName, {
|
369
368
|
...hooks,
|
@@ -76,11 +76,21 @@ const checkForExpansion = async (
|
|
76
76
|
}
|
77
77
|
};
|
78
78
|
|
79
|
-
export
|
79
|
+
export function resourceURI(
|
80
80
|
vocab: string,
|
81
81
|
resourceName: string,
|
82
82
|
id: string | number,
|
83
|
-
): string
|
83
|
+
): string;
|
84
|
+
export function resourceURI(
|
85
|
+
vocab: string,
|
86
|
+
resourceName: string,
|
87
|
+
id: string | number | null | undefined,
|
88
|
+
): string | undefined;
|
89
|
+
export function resourceURI(
|
90
|
+
vocab: string,
|
91
|
+
resourceName: string,
|
92
|
+
id: string | number | null | undefined,
|
93
|
+
): string | undefined {
|
84
94
|
if (id == null) {
|
85
95
|
return;
|
86
96
|
}
|
@@ -88,7 +98,7 @@ export const resourceURI = (
|
|
88
98
|
id = "'" + encodeURIComponent(id) + "'";
|
89
99
|
}
|
90
100
|
return `/${vocab}/${resourceName}(@id)?@id=${id}`;
|
91
|
-
}
|
101
|
+
}
|
92
102
|
|
93
103
|
const getLocalFields = (table: AbstractSqlTable) => {
|
94
104
|
if (table.localFields == null) {
|