@balena/pinejs 17.1.0-build-model-based-typings-437bb06f44567532aec78e550f3d545732466411-1 → 17.1.0-build-joshbwlng-tasks-61ce10e444abec6afea3fec43e9a5c37c7cedea6-1
Sign up to get free protection for your applications and to get access to all the features.
- package/.pinejs-cache.json +1 -1
- package/.versionbot/CHANGELOG.yml +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
|
+
});
|