@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
@@ -1,4 +1,3 @@
|
|
1
|
-
import type AuthModel from './user';
|
2
1
|
import type {
|
3
2
|
AbstractSqlModel,
|
4
3
|
AbstractSqlQuery,
|
@@ -52,7 +51,6 @@ import {
|
|
52
51
|
type ODataRequest,
|
53
52
|
} from './uri-parser';
|
54
53
|
import memoizeWeak = require('memoizee/weak');
|
55
|
-
import type { Config } from '../config-loader/config-loader';
|
56
54
|
|
57
55
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
58
56
|
const userModel: string = require('./user.sbvr');
|
@@ -1120,7 +1118,7 @@ const memoizedGetConstrainedModel = (
|
|
1120
1118
|
getBoundConstrainedMemoizer(abstractSqlModel)(permissionsLookup, vocabulary);
|
1121
1119
|
|
1122
1120
|
const getCheckPasswordQuery = _.once(() =>
|
1123
|
-
sbvrUtils.api.Auth.prepare<{ username: string }
|
1121
|
+
sbvrUtils.api.Auth.prepare<{ username: string }>({
|
1124
1122
|
resource: 'user',
|
1125
1123
|
passthrough: {
|
1126
1124
|
req: rootRead,
|
@@ -1166,7 +1164,7 @@ export const checkPassword = async (
|
|
1166
1164
|
|
1167
1165
|
const $getUserPermissions = (() => {
|
1168
1166
|
const getUserPermissionsQuery = _.once(() =>
|
1169
|
-
sbvrUtils.api.Auth.prepare<{ userId: number }
|
1167
|
+
sbvrUtils.api.Auth.prepare<{ userId: number }>({
|
1170
1168
|
resource: 'permission',
|
1171
1169
|
passthrough: {
|
1172
1170
|
req: rootRead,
|
@@ -1277,7 +1275,7 @@ export const getUserPermissions = async (
|
|
1277
1275
|
|
1278
1276
|
const $getApiKeyPermissions = (() => {
|
1279
1277
|
const getApiKeyPermissionsQuery = _.once(() =>
|
1280
|
-
sbvrUtils.api.Auth.prepare<{ apiKey: string }
|
1278
|
+
sbvrUtils.api.Auth.prepare<{ apiKey: string }>({
|
1281
1279
|
resource: 'permission',
|
1282
1280
|
passthrough: {
|
1283
1281
|
req: rootRead,
|
@@ -1405,7 +1403,7 @@ export const getApiKeyPermissions = async (
|
|
1405
1403
|
|
1406
1404
|
const getApiKeyActorId = (() => {
|
1407
1405
|
const getApiKeyActorIdQuery = _.once(() =>
|
1408
|
-
sbvrUtils.api.Auth.prepare<{ apiKey: string }
|
1406
|
+
sbvrUtils.api.Auth.prepare<{ apiKey: string }>({
|
1409
1407
|
resource: 'api_key',
|
1410
1408
|
passthrough: {
|
1411
1409
|
req: rootRead,
|
@@ -1588,7 +1586,7 @@ let guestPermissionsInitialized = false;
|
|
1588
1586
|
const getGuestPermissions = memoize(
|
1589
1587
|
async () => {
|
1590
1588
|
// Get guest user
|
1591
|
-
const result = await sbvrUtils.api.Auth.get({
|
1589
|
+
const result = (await sbvrUtils.api.Auth.get({
|
1592
1590
|
resource: 'user',
|
1593
1591
|
passthrough: {
|
1594
1592
|
req: rootRead,
|
@@ -1599,7 +1597,7 @@ const getGuestPermissions = memoize(
|
|
1599
1597
|
options: {
|
1600
1598
|
$select: 'id',
|
1601
1599
|
},
|
1602
|
-
});
|
1600
|
+
})) as { id: number } | undefined;
|
1603
1601
|
if (result == null) {
|
1604
1602
|
throw new Error('No guest user');
|
1605
1603
|
}
|
@@ -1688,53 +1686,49 @@ export const addPermissions = async (
|
|
1688
1686
|
}
|
1689
1687
|
};
|
1690
1688
|
|
1691
|
-
declare module './sbvr-utils' {
|
1692
|
-
export interface API {
|
1693
|
-
[authModelConfig.apiRoot]: PinejsClient<AuthModel>;
|
1694
|
-
}
|
1695
|
-
}
|
1696
|
-
const authModelConfig = {
|
1697
|
-
apiRoot: 'Auth',
|
1698
|
-
modelText: userModel,
|
1699
|
-
customServerCode: exports,
|
1700
|
-
migrations: {
|
1701
|
-
'11.0.0-modified-at': `
|
1702
|
-
ALTER TABLE "actor"
|
1703
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1704
|
-
|
1705
|
-
ALTER TABLE "api key"
|
1706
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1707
|
-
ALTER TABLE "api key-has-permission"
|
1708
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1709
|
-
ALTER TABLE "api key-has-role"
|
1710
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1711
|
-
|
1712
|
-
ALTER TABLE "permission"
|
1713
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1714
|
-
|
1715
|
-
ALTER TABLE "role"
|
1716
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1717
|
-
|
1718
|
-
ALTER TABLE "user"
|
1719
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1720
|
-
ALTER TABLE "user-has-role"
|
1721
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1722
|
-
ALTER TABLE "user-has-permission"
|
1723
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1724
|
-
`,
|
1725
|
-
'11.0.1-modified-at': `
|
1726
|
-
ALTER TABLE "role-has-permission"
|
1727
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1728
|
-
`,
|
1729
|
-
'14.42.0-api-key-expiry-date': `
|
1730
|
-
ALTER TABLE "api key"
|
1731
|
-
ADD COLUMN IF NOT EXISTS "expiry date" TIMESTAMP NULL;
|
1732
|
-
`,
|
1733
|
-
},
|
1734
|
-
} as const satisfies sbvrUtils.ExecutableModel;
|
1735
1689
|
export const config = {
|
1736
|
-
models: [
|
1737
|
-
|
1690
|
+
models: [
|
1691
|
+
{
|
1692
|
+
apiRoot: 'Auth',
|
1693
|
+
modelText: userModel,
|
1694
|
+
customServerCode: exports,
|
1695
|
+
migrations: {
|
1696
|
+
'11.0.0-modified-at': `
|
1697
|
+
ALTER TABLE "actor"
|
1698
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1699
|
+
|
1700
|
+
ALTER TABLE "api key"
|
1701
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1702
|
+
ALTER TABLE "api key-has-permission"
|
1703
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1704
|
+
ALTER TABLE "api key-has-role"
|
1705
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1706
|
+
|
1707
|
+
ALTER TABLE "permission"
|
1708
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1709
|
+
|
1710
|
+
ALTER TABLE "role"
|
1711
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1712
|
+
|
1713
|
+
ALTER TABLE "user"
|
1714
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1715
|
+
ALTER TABLE "user-has-role"
|
1716
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1717
|
+
ALTER TABLE "user-has-permission"
|
1718
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1719
|
+
`,
|
1720
|
+
'11.0.1-modified-at': `
|
1721
|
+
ALTER TABLE "role-has-permission"
|
1722
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1723
|
+
`,
|
1724
|
+
'14.42.0-api-key-expiry-date': `
|
1725
|
+
ALTER TABLE "api key"
|
1726
|
+
ADD COLUMN IF NOT EXISTS "expiry date" TIMESTAMP NULL;
|
1727
|
+
`,
|
1728
|
+
},
|
1729
|
+
},
|
1730
|
+
] as sbvrUtils.ExecutableModel[],
|
1731
|
+
};
|
1738
1732
|
export const setup = () => {
|
1739
1733
|
addHook('all', 'all', 'all', {
|
1740
1734
|
sideEffects: false,
|
@@ -2,10 +2,6 @@ import type * as Express from 'express';
|
|
2
2
|
import type * as Db from '../database-layer/db';
|
3
3
|
import type { Model } from '../config-loader/config-loader';
|
4
4
|
import type { AnyObject, RequiredField } from './common-types';
|
5
|
-
import type {
|
6
|
-
PickDeferred,
|
7
|
-
Resource,
|
8
|
-
} from '@balena/abstract-sql-to-typescript';
|
9
5
|
|
10
6
|
// Augment the Express typings
|
11
7
|
declare global {
|
@@ -36,7 +32,6 @@ import {
|
|
36
32
|
} from '@balena/odata-to-abstract-sql';
|
37
33
|
import sbvrTypes from '@balena/sbvr-types';
|
38
34
|
import deepFreeze = require('deep-freeze');
|
39
|
-
import type { AnyResource, Params } from 'pinejs-client-core';
|
40
35
|
import { PinejsClientCore, type PromiseResultTypes } from 'pinejs-client-core';
|
41
36
|
|
42
37
|
import { ExtendedSBVRParser } from '../extended-sbvr-parser/extended-sbvr-parser';
|
@@ -45,9 +40,9 @@ import * as asyncMigrator from '../migrator/async';
|
|
45
40
|
import * as syncMigrator from '../migrator/sync';
|
46
41
|
import { generateODataMetadata } from '../odata-metadata/odata-metadata-generator';
|
47
42
|
|
48
|
-
import type DevModel from './dev';
|
49
43
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
50
44
|
const devModel = require('./dev.sbvr');
|
45
|
+
import * as tasks from '../tasks';
|
51
46
|
import * as permissions from './permissions';
|
52
47
|
import {
|
53
48
|
BadRequestError,
|
@@ -83,6 +78,7 @@ export {
|
|
83
78
|
addPureHook,
|
84
79
|
addSideEffectHook,
|
85
80
|
} from './hooks';
|
81
|
+
export { addTaskHandler } from '../tasks';
|
86
82
|
|
87
83
|
import memoizeWeak = require('memoizee/weak');
|
88
84
|
import * as controlFlow from './control-flow';
|
@@ -716,7 +712,7 @@ export const executeModels = async (
|
|
716
712
|
},
|
717
713
|
});
|
718
714
|
}
|
719
|
-
const result = await api.dev.get({
|
715
|
+
const result = (await api.dev.get({
|
720
716
|
resource: 'model',
|
721
717
|
passthrough: {
|
722
718
|
tx,
|
@@ -729,7 +725,7 @@ export const executeModels = async (
|
|
729
725
|
model_type: modelType,
|
730
726
|
},
|
731
727
|
},
|
732
|
-
})
|
728
|
+
})) as Array<{ id: number }>;
|
733
729
|
|
734
730
|
let method: SupportedMethod = 'POST';
|
735
731
|
let uri = '/dev/model';
|
@@ -779,7 +775,7 @@ export const postExecuteModels = async (tx: Db.Tx): Promise<void> => {
|
|
779
775
|
// Hence, skipped migrations from earlier models are not set as executed as the `migration` table is missing
|
780
776
|
// Here the skipped migrations that haven't been set properly are covered
|
781
777
|
// This is mostly an edge case when running on an empty database schema and migrations model hasn't been executed, yet.
|
782
|
-
// One
|
778
|
+
// One specific case are tests to run tests against migrated and unmigrated database states
|
783
779
|
|
784
780
|
for (const modelKey of Object.keys(models)) {
|
785
781
|
const pendingToSetExecutedMigrations =
|
@@ -789,6 +785,9 @@ export const postExecuteModels = async (tx: Db.Tx): Promise<void> => {
|
|
789
785
|
await setExecutedMigrations(tx, modelKey, pendingToSetExecutedMigrations);
|
790
786
|
}
|
791
787
|
}
|
788
|
+
|
789
|
+
// Initialize task worker and create required hooks
|
790
|
+
await tasks.setup(db);
|
792
791
|
};
|
793
792
|
|
794
793
|
const cleanupModel = (vocab: string) => {
|
@@ -1008,16 +1007,7 @@ export type Passthrough = AnyObject & {
|
|
1008
1007
|
tx?: Db.Tx;
|
1009
1008
|
};
|
1010
1009
|
|
1011
|
-
export class PinejsClient
|
1012
|
-
M extends {
|
1013
|
-
[key in keyof M]: Resource;
|
1014
|
-
} = {
|
1015
|
-
[key in string]: {
|
1016
|
-
Read: AnyObject;
|
1017
|
-
Write: AnyObject;
|
1018
|
-
};
|
1019
|
-
},
|
1020
|
-
> extends PinejsClientCore<unknown, M> {
|
1010
|
+
export class PinejsClient extends PinejsClientCore {
|
1021
1011
|
public async _request({
|
1022
1012
|
method,
|
1023
1013
|
url,
|
@@ -1035,37 +1025,11 @@ export class PinejsClient<
|
|
1035
1025
|
}) {
|
1036
1026
|
return (await runURI(method, url, body, tx, req, custom)) as object;
|
1037
1027
|
}
|
1038
|
-
|
1039
|
-
public post<TResource extends keyof M & string>(
|
1040
|
-
params: {
|
1041
|
-
resource: TResource;
|
1042
|
-
options?: Params<M[TResource]>['options'] & { returnResource?: true };
|
1043
|
-
} & Params<M[TResource]>,
|
1044
|
-
): Promise<PickDeferred<M[TResource]['Read']>>;
|
1045
|
-
public post<TResource extends keyof M & string>(
|
1046
|
-
params: {
|
1047
|
-
resource: TResource;
|
1048
|
-
options: Params<M[TResource]>['options'] & { returnResource: boolean };
|
1049
|
-
} & Params<M[TResource]>,
|
1050
|
-
): Promise<Pick<M[TResource]['Read'], 'id'>>; // TODO: This should use the primary key rather than hardcoding `id`
|
1051
|
-
/**
|
1052
|
-
* @deprecated POSTing via `url` is deprecated
|
1053
|
-
*/
|
1054
|
-
public post<T extends Resource = AnyResource>(
|
1055
|
-
params: {
|
1056
|
-
resource?: undefined;
|
1057
|
-
url: NonNullable<Params<T>['url']>;
|
1058
|
-
} & Params<T>,
|
1059
|
-
): Promise<AnyObject>;
|
1060
|
-
public post(params: Params<AnyResource>): Promise<AnyObject> {
|
1061
|
-
return super.post(params as Parameters<PinejsClient['post']>[0]);
|
1062
|
-
}
|
1063
1028
|
}
|
1064
1029
|
|
1065
|
-
export
|
1030
|
+
export const api: {
|
1066
1031
|
[vocab: string]: PinejsClient;
|
1067
|
-
}
|
1068
|
-
export const api = {} as API;
|
1032
|
+
} = {};
|
1069
1033
|
export const logger: {
|
1070
1034
|
[vocab: string]: Console;
|
1071
1035
|
} = {};
|
@@ -1172,8 +1136,8 @@ const getIdField = (
|
|
1172
1136
|
// TODO: Should resolveSynonym also be using the finalAbstractSqlModel?
|
1173
1137
|
getFinalAbstractSqlModel(request).tables[resolveSynonym(request)].idField;
|
1174
1138
|
|
1175
|
-
export const getAffectedIds = async
|
1176
|
-
args: HookArgs
|
1139
|
+
export const getAffectedIds = async (
|
1140
|
+
args: HookArgs & {
|
1177
1141
|
tx: Db.Tx;
|
1178
1142
|
},
|
1179
1143
|
): Promise<number[]> => {
|
@@ -1195,11 +1159,11 @@ export const getAffectedIds = async <Vocab extends string>(
|
|
1195
1159
|
return request.affectedIds;
|
1196
1160
|
};
|
1197
1161
|
|
1198
|
-
const $getAffectedIds = async
|
1162
|
+
const $getAffectedIds = async ({
|
1199
1163
|
req,
|
1200
1164
|
request,
|
1201
1165
|
tx,
|
1202
|
-
}: HookArgs
|
1166
|
+
}: HookArgs & {
|
1203
1167
|
tx: Db.Tx;
|
1204
1168
|
}): Promise<number[]> => {
|
1205
1169
|
if (!['PATCH', 'DELETE'].includes(request.method)) {
|
@@ -1582,7 +1546,8 @@ const runRequest = async (
|
|
1582
1546
|
if (env.DEBUG) {
|
1583
1547
|
log.log('Running', req.method, req.url);
|
1584
1548
|
}
|
1585
|
-
let
|
1549
|
+
let resultGet: Db.Result | undefined;
|
1550
|
+
let resultPost: number | undefined;
|
1586
1551
|
|
1587
1552
|
try {
|
1588
1553
|
try {
|
@@ -1591,18 +1556,18 @@ const runRequest = async (
|
|
1591
1556
|
|
1592
1557
|
switch (request.method) {
|
1593
1558
|
case 'GET':
|
1594
|
-
|
1559
|
+
resultGet = await runGet(req, request, tx);
|
1595
1560
|
break;
|
1596
1561
|
case 'POST':
|
1597
|
-
|
1562
|
+
resultPost = await runPost(req, request, tx);
|
1598
1563
|
break;
|
1599
1564
|
case 'PUT':
|
1600
1565
|
case 'PATCH':
|
1601
1566
|
case 'MERGE':
|
1602
|
-
|
1567
|
+
await runPut(req, request, tx);
|
1603
1568
|
break;
|
1604
1569
|
case 'DELETE':
|
1605
|
-
|
1570
|
+
await runDelete(req, request, tx);
|
1606
1571
|
break;
|
1607
1572
|
}
|
1608
1573
|
} catch (err: any) {
|
@@ -1628,7 +1593,12 @@ const runRequest = async (
|
|
1628
1593
|
throw err;
|
1629
1594
|
}
|
1630
1595
|
|
1631
|
-
await runHooks('POSTRUN', request.hooks, {
|
1596
|
+
await runHooks('POSTRUN', request.hooks, {
|
1597
|
+
req,
|
1598
|
+
request,
|
1599
|
+
result: resultGet ?? resultPost,
|
1600
|
+
tx,
|
1601
|
+
});
|
1632
1602
|
} catch (err: any) {
|
1633
1603
|
await runHooks('POSTRUN-ERROR', request.hooks, {
|
1634
1604
|
req,
|
@@ -1638,7 +1608,23 @@ const runRequest = async (
|
|
1638
1608
|
});
|
1639
1609
|
throw err;
|
1640
1610
|
}
|
1641
|
-
|
1611
|
+
|
1612
|
+
switch (request.method) {
|
1613
|
+
case 'GET':
|
1614
|
+
return await respondGet(req, request, resultGet, tx);
|
1615
|
+
case 'POST':
|
1616
|
+
return await respondPost(req, request, resultPost, tx);
|
1617
|
+
case 'PUT':
|
1618
|
+
case 'PATCH':
|
1619
|
+
case 'MERGE':
|
1620
|
+
return await respondPut(req, request, tx);
|
1621
|
+
case 'DELETE':
|
1622
|
+
return await respondDelete(req, request, tx);
|
1623
|
+
case 'OPTIONS':
|
1624
|
+
return await respondOptions(req, request, tx);
|
1625
|
+
default:
|
1626
|
+
throw new MethodNotAllowedError();
|
1627
|
+
}
|
1642
1628
|
};
|
1643
1629
|
|
1644
1630
|
const runChangeSet =
|
@@ -1686,30 +1672,6 @@ const updateBinds = (
|
|
1686
1672
|
return request;
|
1687
1673
|
};
|
1688
1674
|
|
1689
|
-
const prepareResponse = async (
|
1690
|
-
req: Express.Request,
|
1691
|
-
request: uriParser.ODataRequest,
|
1692
|
-
result: any,
|
1693
|
-
tx: Db.Tx,
|
1694
|
-
): Promise<Response> => {
|
1695
|
-
switch (request.method) {
|
1696
|
-
case 'GET':
|
1697
|
-
return await respondGet(req, request, result, tx);
|
1698
|
-
case 'POST':
|
1699
|
-
return await respondPost(req, request, result, tx);
|
1700
|
-
case 'PUT':
|
1701
|
-
case 'PATCH':
|
1702
|
-
case 'MERGE':
|
1703
|
-
return await respondPut(req, request, result, tx);
|
1704
|
-
case 'DELETE':
|
1705
|
-
return await respondDelete(req, request, result, tx);
|
1706
|
-
case 'OPTIONS':
|
1707
|
-
return await respondOptions(req, request, result, tx);
|
1708
|
-
default:
|
1709
|
-
throw new MethodNotAllowedError();
|
1710
|
-
}
|
1711
|
-
};
|
1712
|
-
|
1713
1675
|
const checkReadOnlyRequests = (request: uriParser.ODataRequest) => {
|
1714
1676
|
if (request.method !== 'GET') {
|
1715
1677
|
// Only GET requests can be read-only
|
@@ -1811,11 +1773,17 @@ const runGet = async (
|
|
1811
1773
|
const respondGet = async (
|
1812
1774
|
req: Express.Request,
|
1813
1775
|
request: uriParser.ODataRequest,
|
1814
|
-
result:
|
1776
|
+
result: Db.Result | undefined,
|
1815
1777
|
tx: Db.Tx,
|
1816
1778
|
): Promise<Response> => {
|
1817
1779
|
const vocab = request.vocabulary;
|
1818
1780
|
if (request.sqlQuery != null) {
|
1781
|
+
if (result == null) {
|
1782
|
+
// This shouldn't be able to happen because the result should only be null if there's no sqlQuery
|
1783
|
+
throw new Error(
|
1784
|
+
'Null result passed to respond GET that has a sqlQuery defined',
|
1785
|
+
);
|
1786
|
+
}
|
1819
1787
|
const format = request.odataQuery.options?.$format;
|
1820
1788
|
const metadata =
|
1821
1789
|
format != null && typeof format === 'object'
|
@@ -1880,7 +1848,7 @@ const runPost = async (
|
|
1880
1848
|
const respondPost = async (
|
1881
1849
|
req: Express.Request,
|
1882
1850
|
request: uriParser.ODataRequest,
|
1883
|
-
id: number,
|
1851
|
+
id: number | undefined,
|
1884
1852
|
tx: Db.Tx,
|
1885
1853
|
): Promise<Response> => {
|
1886
1854
|
const vocab = request.vocabulary;
|
@@ -1930,7 +1898,7 @@ const runPut = async (
|
|
1930
1898
|
_req: Express.Request,
|
1931
1899
|
request: uriParser.ODataRequest,
|
1932
1900
|
tx: Db.Tx,
|
1933
|
-
): Promise<
|
1901
|
+
): Promise<void> => {
|
1934
1902
|
let rowsAffected: number;
|
1935
1903
|
// If request.sqlQuery is an array it means it's an UPSERT, ie two queries: [InsertQuery, UpdateQuery]
|
1936
1904
|
if (Array.isArray(request.sqlQuery)) {
|
@@ -1946,13 +1914,11 @@ const runPut = async (
|
|
1946
1914
|
if (rowsAffected > 0) {
|
1947
1915
|
await validateModel(tx, _.last(request.translateVersions)!, request);
|
1948
1916
|
}
|
1949
|
-
return undefined;
|
1950
1917
|
};
|
1951
1918
|
|
1952
1919
|
const respondPut = async (
|
1953
1920
|
req: Express.Request,
|
1954
1921
|
request: uriParser.ODataRequest,
|
1955
|
-
result: any,
|
1956
1922
|
tx: Db.Tx,
|
1957
1923
|
): Promise<Response> => {
|
1958
1924
|
const response = {
|
@@ -1961,7 +1927,6 @@ const respondPut = async (
|
|
1961
1927
|
await runHooks('PRERESPOND', request.hooks, {
|
1962
1928
|
req,
|
1963
1929
|
request,
|
1964
|
-
result,
|
1965
1930
|
response,
|
1966
1931
|
tx,
|
1967
1932
|
});
|
@@ -1974,60 +1939,54 @@ const runDelete = async (
|
|
1974
1939
|
_req: Express.Request,
|
1975
1940
|
request: uriParser.ODataRequest,
|
1976
1941
|
tx: Db.Tx,
|
1977
|
-
): Promise<
|
1942
|
+
): Promise<void> => {
|
1978
1943
|
const { rowsAffected } = await runQuery(tx, request, undefined, true);
|
1979
1944
|
if (rowsAffected > 0) {
|
1980
1945
|
await validateModel(tx, _.last(request.translateVersions)!, request);
|
1981
1946
|
}
|
1982
|
-
|
1983
|
-
return undefined;
|
1984
1947
|
};
|
1985
1948
|
|
1986
|
-
export interface API {
|
1987
|
-
[devModelConfig.apiRoot]: PinejsClient<DevModel>;
|
1988
|
-
}
|
1989
|
-
const devModelConfig = {
|
1990
|
-
apiRoot: 'dev',
|
1991
|
-
modelText: devModel,
|
1992
|
-
logging: {
|
1993
|
-
log: false,
|
1994
|
-
},
|
1995
|
-
migrations: {
|
1996
|
-
'11.0.0-modified-at': `
|
1997
|
-
ALTER TABLE "model"
|
1998
|
-
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1999
|
-
`,
|
2000
|
-
'15.0.0-data-types': async ($tx, sbvrUtils) => {
|
2001
|
-
switch (sbvrUtils.db.engine) {
|
2002
|
-
case 'mysql':
|
2003
|
-
await $tx.executeSql(`\
|
2004
|
-
ALTER TABLE "model"
|
2005
|
-
MODIFY "model value" JSON NOT NULL;
|
2006
|
-
|
2007
|
-
UPDATE "model"
|
2008
|
-
SET "model value" = CAST('{"value":' || CAST("model value" AS CHAR) || '}' AS JSON)
|
2009
|
-
WHERE "model type" IN ('se', 'odataMetadata')
|
2010
|
-
AND CAST("model value" AS CHAR) LIKE '"%';`);
|
2011
|
-
break;
|
2012
|
-
case 'postgres':
|
2013
|
-
await $tx.executeSql(`\
|
2014
|
-
ALTER TABLE "model"
|
2015
|
-
ALTER COLUMN "model value" SET DATA TYPE JSONB USING "model value"::JSONB;
|
2016
|
-
|
2017
|
-
UPDATE "model"
|
2018
|
-
SET "model value" = CAST('{"value":' || CAST("model value" AS TEXT) || '}' AS JSON)
|
2019
|
-
WHERE "model type" IN ('se', 'odataMetadata')
|
2020
|
-
AND CAST("model value" AS TEXT) LIKE '"%';`);
|
2021
|
-
break;
|
2022
|
-
// No need to migrate for websql
|
2023
|
-
}
|
2024
|
-
},
|
2025
|
-
},
|
2026
|
-
} as const satisfies ExecutableModel;
|
2027
1949
|
export const executeStandardModels = async (tx: Db.Tx): Promise<void> => {
|
2028
1950
|
try {
|
2029
1951
|
// dev model must run first
|
2030
|
-
await executeModel(tx,
|
1952
|
+
await executeModel(tx, {
|
1953
|
+
apiRoot: 'dev',
|
1954
|
+
modelText: devModel,
|
1955
|
+
logging: {
|
1956
|
+
log: false,
|
1957
|
+
},
|
1958
|
+
migrations: {
|
1959
|
+
'11.0.0-modified-at': `
|
1960
|
+
ALTER TABLE "model"
|
1961
|
+
ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
|
1962
|
+
`,
|
1963
|
+
'15.0.0-data-types': async ($tx, sbvrUtils) => {
|
1964
|
+
switch (sbvrUtils.db.engine) {
|
1965
|
+
case 'mysql':
|
1966
|
+
await $tx.executeSql(`\
|
1967
|
+
ALTER TABLE "model"
|
1968
|
+
MODIFY "model value" JSON NOT NULL;
|
1969
|
+
|
1970
|
+
UPDATE "model"
|
1971
|
+
SET "model value" = CAST('{"value":' || CAST("model value" AS CHAR) || '}' AS JSON)
|
1972
|
+
WHERE "model type" IN ('se', 'odataMetadata')
|
1973
|
+
AND CAST("model value" AS CHAR) LIKE '"%';`);
|
1974
|
+
break;
|
1975
|
+
case 'postgres':
|
1976
|
+
await $tx.executeSql(`\
|
1977
|
+
ALTER TABLE "model"
|
1978
|
+
ALTER COLUMN "model value" SET DATA TYPE JSONB USING "model value"::JSONB;
|
1979
|
+
|
1980
|
+
UPDATE "model"
|
1981
|
+
SET "model value" = CAST('{"value":' || CAST("model value" AS TEXT) || '}' AS JSON)
|
1982
|
+
WHERE "model type" IN ('se', 'odataMetadata')
|
1983
|
+
AND CAST("model value" AS TEXT) LIKE '"%';`);
|
1984
|
+
break;
|
1985
|
+
// No need to migrate for websql
|
1986
|
+
}
|
1987
|
+
},
|
1988
|
+
},
|
1989
|
+
});
|
2031
1990
|
await executeModels(tx, permissions.config.models);
|
2032
1991
|
console.info('Successfully executed standard models.');
|
2033
1992
|
} catch (err: any) {
|
@@ -6,6 +6,7 @@ import * as dbModule from '../database-layer/db';
|
|
6
6
|
import * as configLoader from '../config-loader/config-loader';
|
7
7
|
import * as migrator from '../migrator/sync';
|
8
8
|
import type * as migratorUtils from '../migrator/utils';
|
9
|
+
import * as tasks from '../tasks';
|
9
10
|
|
10
11
|
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
|
11
12
|
import { PINEJS_ADVISORY_LOCK } from '../config-loader/env';
|
@@ -19,6 +20,7 @@ export * as errors from '../sbvr-api/errors';
|
|
19
20
|
export * as env from '../config-loader/env';
|
20
21
|
export * as types from '../sbvr-api/common-types';
|
21
22
|
export * as hooks from '../sbvr-api/hooks';
|
23
|
+
export * as tasks from '../tasks';
|
22
24
|
export * as webResourceHandler from '../webresource-handler';
|
23
25
|
export type { configLoader as ConfigLoader };
|
24
26
|
export type { migratorUtils as Migrator };
|
@@ -63,6 +65,7 @@ export const init = async <T extends string>(
|
|
63
65
|
await sbvrUtils.setup(app, db);
|
64
66
|
const cfgLoader = await configLoader.setup(app);
|
65
67
|
await cfgLoader.loadConfig(migrator.config);
|
68
|
+
await cfgLoader.loadConfig(tasks.config);
|
66
69
|
|
67
70
|
const promises: Array<Promise<void>> = [];
|
68
71
|
if (process.env.SBVR_SERVER_ENABLED) {
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import Ajv from 'ajv';
|
2
|
+
|
3
|
+
// Root path for the tasks API
|
4
|
+
export const apiRoot = 'tasks';
|
5
|
+
|
6
|
+
// Channel name for task insert notifications
|
7
|
+
export const channel = 'pinejs$task_insert';
|
8
|
+
|
9
|
+
// Setting inlineRefs=false as without it we run into a
|
10
|
+
// "Maximum call stack size exceeded" error apprarently caused
|
11
|
+
// by String.prototype._uncountable_words being set in sbvr-parser?
|
12
|
+
export const ajv = new Ajv({
|
13
|
+
inlineRefs: false,
|
14
|
+
});
|