@appsemble/node-utils 0.27.11 → 0.28.0
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/README.md +3 -3
- package/app.js +7 -1
- package/package.json +3 -3
- package/server/controllers/resources.js +13 -8
- package/server/controllers/resources.test.js +9 -0
- package/server/types.d.ts +8 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#  Appsemble Node Utilities
|
|
2
2
|
|
|
3
3
|
> NodeJS utilities used by Appsemble internally.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@appsemble/node-utils)
|
|
6
|
-
[](https://gitlab.com/appsemble/appsemble/-/releases/0.28.0)
|
|
7
7
|
[](https://prettier.io)
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
@@ -26,5 +26,5 @@ compatibility is not guaranteed.
|
|
|
26
26
|
|
|
27
27
|
## License
|
|
28
28
|
|
|
29
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.
|
|
29
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.28.0/LICENSE.md) ©
|
|
30
30
|
[Appsemble](https://appsemble.com)
|
package/app.js
CHANGED
|
@@ -27,13 +27,14 @@ const getPluralRules = memoize((locale, opts) => new Intl.PluralRules(locale, op
|
|
|
27
27
|
*/
|
|
28
28
|
export async function getRemapperContext(app, language, userInfo, options, context) {
|
|
29
29
|
var _a, _b;
|
|
30
|
-
const { getAppMessages, getAppUrl } = options;
|
|
30
|
+
const { getAppMessages, getAppUrl, getAppVariables } = options;
|
|
31
31
|
const appUrl = String(await getAppUrl({ context, app }));
|
|
32
32
|
const appMessages = await getAppMessages({
|
|
33
33
|
app,
|
|
34
34
|
context,
|
|
35
35
|
language,
|
|
36
36
|
});
|
|
37
|
+
const appVariables = await getAppVariables({ context, app });
|
|
37
38
|
const cache = objectCache((message) => new IntlMessageFormat(message, language, undefined, {
|
|
38
39
|
formatters: {
|
|
39
40
|
// @ts-expect-error intl-messageformat types are wrong
|
|
@@ -51,6 +52,10 @@ export async function getRemapperContext(app, language, userInfo, options, conte
|
|
|
51
52
|
const message = msg ? msg.messages.messageIds[id] : defaultMessage;
|
|
52
53
|
return cache(message || `'{${id}}'`);
|
|
53
54
|
},
|
|
55
|
+
getVariable(variableName) {
|
|
56
|
+
var _a;
|
|
57
|
+
return (_a = appVariables.find((appVariable) => appVariable.name === variableName)) === null || _a === void 0 ? void 0 : _a.value;
|
|
58
|
+
},
|
|
54
59
|
userInfo,
|
|
55
60
|
appMember: userInfo === null || userInfo === void 0 ? void 0 : userInfo.appMember,
|
|
56
61
|
context: {},
|
|
@@ -229,6 +234,7 @@ export async function applyAppVariant(appPath, appVariant) {
|
|
|
229
234
|
await copy(appPath, appVariantDestDir);
|
|
230
235
|
logger.verbose(`Successfully copied app files to ${appVariantDestDir}`);
|
|
231
236
|
const appVariantDefDir = join(appPath, 'variants', appVariant);
|
|
237
|
+
await transferAppVariantFiles(appPath, appVariantDefDir, appVariantDestDir, 'config');
|
|
232
238
|
await transferAppVariantFiles(appPath, appVariantDefDir, appVariantDestDir, 'assets');
|
|
233
239
|
await transferAppVariantFiles(appPath, appVariantDefDir, appVariantDestDir, 'resources');
|
|
234
240
|
await transferAppVariantFiles(appPath, appVariantDefDir, appVariantDestDir, 'screenshots');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/node-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "NodeJS utilities used by Appsemble internally.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"test": "vitest"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@appsemble/types": "0.
|
|
42
|
-
"@appsemble/utils": "0.
|
|
41
|
+
"@appsemble/types": "0.28.0",
|
|
42
|
+
"@appsemble/utils": "0.28.0",
|
|
43
43
|
"@formatjs/fast-memoize": "^2.0.0",
|
|
44
44
|
"@fortawesome/fontawesome-free": "^6.0.0",
|
|
45
45
|
"@koa/cors": "^4.0.0",
|
|
@@ -2,9 +2,13 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { isDeepStrictEqual } from 'node:util';
|
|
3
3
|
import { assertKoaError, getRemapperContext, getResourceDefinition, logger, processResourceBody, throwKoaError, } from '@appsemble/node-utils';
|
|
4
4
|
import { defaultLocale, remap } from '@appsemble/utils';
|
|
5
|
-
function generateQuery(ctx, { parseQuery }) {
|
|
5
|
+
function generateQuery(ctx, { parseQuery }, resourceDefinition) {
|
|
6
6
|
try {
|
|
7
|
-
return parseQuery({
|
|
7
|
+
return parseQuery({
|
|
8
|
+
$filter: ctx.queryParams.$filter,
|
|
9
|
+
$orderby: ctx.queryParams.$orderby,
|
|
10
|
+
resourceDefinition,
|
|
11
|
+
});
|
|
8
12
|
}
|
|
9
13
|
catch (error) {
|
|
10
14
|
if (error instanceof Error) {
|
|
@@ -16,11 +20,13 @@ function generateQuery(ctx, { parseQuery }) {
|
|
|
16
20
|
}
|
|
17
21
|
export function createQueryResources(options) {
|
|
18
22
|
return async (ctx) => {
|
|
19
|
-
var _a, _b, _c;
|
|
23
|
+
var _a, _b, _c, _d, _e;
|
|
20
24
|
const { pathParams: { appId, resourceType }, queryParams: { $select, $skip, $top }, user, } = ctx;
|
|
21
25
|
const { getApp, getAppResources, verifyResourceActionPermission } = options;
|
|
22
26
|
const app = await getApp({ context: ctx, query: { where: { id: appId } } });
|
|
23
|
-
const
|
|
27
|
+
const view = (_a = ctx.queryParams) === null || _a === void 0 ? void 0 : _a.view;
|
|
28
|
+
const resourceDefinition = getResourceDefinition(app, resourceType, ctx, view);
|
|
29
|
+
const { order, where } = generateQuery(ctx, options, resourceDefinition);
|
|
24
30
|
const memberQuery = await verifyResourceActionPermission({
|
|
25
31
|
context: ctx,
|
|
26
32
|
app,
|
|
@@ -29,6 +35,7 @@ export function createQueryResources(options) {
|
|
|
29
35
|
options,
|
|
30
36
|
ctx,
|
|
31
37
|
});
|
|
38
|
+
const isSameOrigin = ((_b = ctx === null || ctx === void 0 ? void 0 : ctx.headers) === null || _b === void 0 ? void 0 : _b.origin) === ((_c = ctx === null || ctx === void 0 ? void 0 : ctx.headers) === null || _c === void 0 ? void 0 : _c.host);
|
|
32
39
|
const findOptions = {
|
|
33
40
|
limit: $top,
|
|
34
41
|
offset: $skip,
|
|
@@ -41,7 +48,7 @@ export function createQueryResources(options) {
|
|
|
41
48
|
type: resourceType,
|
|
42
49
|
AppId: appId,
|
|
43
50
|
expires: { or: [{ gt: new Date() }, null] },
|
|
44
|
-
...(app.demoMode ? { seed: false, ephemeral: true } : {}),
|
|
51
|
+
...(app.demoMode && !isSameOrigin ? { seed: false, ephemeral: true } : {}),
|
|
45
52
|
},
|
|
46
53
|
],
|
|
47
54
|
},
|
|
@@ -53,14 +60,12 @@ export function createQueryResources(options) {
|
|
|
53
60
|
type: resourceType,
|
|
54
61
|
context: ctx,
|
|
55
62
|
});
|
|
56
|
-
const view = (_a = ctx.queryParams) === null || _a === void 0 ? void 0 : _a.view;
|
|
57
|
-
const resourceDefinition = getResourceDefinition(app, resourceType, ctx, view);
|
|
58
63
|
if (view) {
|
|
59
64
|
const context = await getRemapperContext(app, app.definition.defaultLanguage || defaultLocale, user && {
|
|
60
65
|
sub: user.id,
|
|
61
66
|
name: user.name,
|
|
62
67
|
email: user.primaryEmail,
|
|
63
|
-
email_verified: Boolean((
|
|
68
|
+
email_verified: Boolean((_e = (_d = user.EmailAuthorizations) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.verified),
|
|
64
69
|
zoneinfo: user.timezone,
|
|
65
70
|
}, options, ctx);
|
|
66
71
|
ctx.body = resources.map((resource) => remap(resourceDefinition.views[view].remap, resource, context));
|
|
@@ -10,6 +10,7 @@ let mockVerifyResourceActionPermission;
|
|
|
10
10
|
let mockParseQuery;
|
|
11
11
|
let mockGetAppUrl;
|
|
12
12
|
let mockGetAppMessages;
|
|
13
|
+
let mockGetAppVariables;
|
|
13
14
|
let mockCreateAppResourcesWithAssets;
|
|
14
15
|
let mockGetAppAssets;
|
|
15
16
|
let mockCtx;
|
|
@@ -22,6 +23,7 @@ describe('createQueryResources', () => {
|
|
|
22
23
|
mockParseQuery = vi.fn();
|
|
23
24
|
mockGetAppUrl = vi.fn();
|
|
24
25
|
mockGetAppMessages = vi.fn();
|
|
26
|
+
mockGetAppVariables = vi.fn();
|
|
25
27
|
mockCtx = {
|
|
26
28
|
pathParams: { appId: 1, resourceType: 'mockResourceType' },
|
|
27
29
|
queryParams: { $select: 'field1, field2', $skip: 0, $top: 10 },
|
|
@@ -161,11 +163,13 @@ describe('createQueryResources', () => {
|
|
|
161
163
|
};
|
|
162
164
|
const mockAppUrl = new URL('https://localhost');
|
|
163
165
|
const mockAppMessages = [{ language: 'eng', messages: {} }];
|
|
166
|
+
const mockAppVariables = [{ name: 'variable', value: 'variable' }];
|
|
164
167
|
mockGetApp.mockResolvedValue(mockApp);
|
|
165
168
|
mockGetAppResources.mockResolvedValue(mockResources);
|
|
166
169
|
mockParseQuery.mockReturnValue(mockParsedQuery);
|
|
167
170
|
mockGetAppUrl.mockResolvedValue(mockAppUrl);
|
|
168
171
|
mockGetAppMessages.mockResolvedValue(mockAppMessages);
|
|
172
|
+
mockGetAppVariables.mockResolvedValue(mockAppVariables);
|
|
169
173
|
mockCtx.queryParams.view = 'view';
|
|
170
174
|
const options = {
|
|
171
175
|
getApp: mockGetApp,
|
|
@@ -174,6 +178,7 @@ describe('createQueryResources', () => {
|
|
|
174
178
|
parseQuery: mockParseQuery,
|
|
175
179
|
getAppUrl: mockGetAppUrl,
|
|
176
180
|
getAppMessages: mockGetAppMessages,
|
|
181
|
+
getAppVariables: mockGetAppVariables,
|
|
177
182
|
};
|
|
178
183
|
const resourceDefinition = getResourceDefinition(mockApp, 'mockResourceType', mockCtx, 'view');
|
|
179
184
|
const remapperContext = await getRemapperContext(mockApp, mockApp.definition.defaultLanguage || defaultLocale, null, options, mockCtx);
|
|
@@ -305,6 +310,7 @@ describe('createGetResourceById', () => {
|
|
|
305
310
|
mockVerifyResourceActionPermission = vi.fn();
|
|
306
311
|
mockGetAppUrl = vi.fn();
|
|
307
312
|
mockGetAppMessages = vi.fn();
|
|
313
|
+
mockGetAppVariables = vi.fn();
|
|
308
314
|
mockCtx = {
|
|
309
315
|
pathParams: { appId: 1, resourceId: 1, resourceType: 'mockResourceType' },
|
|
310
316
|
user: { id: 'mockUserId', name: 'John Doe', primaryEmail: 'john@example.com' },
|
|
@@ -403,10 +409,12 @@ describe('createGetResourceById', () => {
|
|
|
403
409
|
const mockResource = { id: 1 };
|
|
404
410
|
const mockAppUrl = new URL('https://localhost');
|
|
405
411
|
const mockAppMessages = [{ language: 'eng', messages: {} }];
|
|
412
|
+
const mockAppVariables = [{ name: 'variable', value: 'variable' }];
|
|
406
413
|
mockGetApp.mockResolvedValue(mockApp);
|
|
407
414
|
mockGetAppResource.mockResolvedValue(mockResource);
|
|
408
415
|
mockGetAppUrl.mockResolvedValue(mockAppUrl);
|
|
409
416
|
mockGetAppMessages.mockResolvedValue(mockAppMessages);
|
|
417
|
+
mockGetAppVariables.mockResolvedValue(mockAppVariables);
|
|
410
418
|
mockCtx.queryParams = { view: 'view' };
|
|
411
419
|
const options = {
|
|
412
420
|
getApp: mockGetApp,
|
|
@@ -414,6 +422,7 @@ describe('createGetResourceById', () => {
|
|
|
414
422
|
verifyResourceActionPermission: mockVerifyResourceActionPermission,
|
|
415
423
|
getAppUrl: mockGetAppUrl,
|
|
416
424
|
getAppMessages: mockGetAppMessages,
|
|
425
|
+
getAppVariables: mockGetAppVariables,
|
|
417
426
|
};
|
|
418
427
|
const resourceDefinition = getResourceDefinition(mockApp, 'mockResourceType', mockCtx, 'view');
|
|
419
428
|
const remapperContext = await getRemapperContext(mockApp, mockApp.definition.defaultLanguage || defaultLocale, null, options, mockCtx);
|
package/server/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import { type App, type AppMember, type AppMessages, type Asset, type BlockDefinition, type BlockManifest, type ControllerDefinition, type EmailActionDefinition, type Resource, type ResourceDefinition, type TeamMember, type Theme as ThemeType, type UserInfo } from '@appsemble/types';
|
|
2
|
+
import { type App, type AppConfigEntry, type AppMember, type AppMessages, type Asset, type BlockDefinition, type BlockManifest, type ControllerDefinition, type EmailActionDefinition, type Resource, type ResourceDefinition, type TeamMember, type Theme as ThemeType, type UserInfo } from '@appsemble/types';
|
|
3
3
|
import { type IdentifiableBlock, type Permission } from '@appsemble/utils';
|
|
4
4
|
import { type RawAxiosRequestConfig } from 'axios';
|
|
5
5
|
import { type Context, type DefaultContext as DefaultContextInterface, type DefaultState, type ParameterizedContext } from 'koa';
|
|
@@ -13,6 +13,7 @@ export interface UtilsUser {
|
|
|
13
13
|
verified: boolean;
|
|
14
14
|
}[];
|
|
15
15
|
subscribed?: boolean;
|
|
16
|
+
demoLoginUser?: boolean;
|
|
16
17
|
}
|
|
17
18
|
declare module 'koa' {
|
|
18
19
|
interface Request {
|
|
@@ -24,6 +25,7 @@ declare module 'koa' {
|
|
|
24
25
|
appsembleApp: App;
|
|
25
26
|
appBlocks: BlockManifest[];
|
|
26
27
|
appMessages: AppMessages[];
|
|
28
|
+
appVariables: AppConfigEntry[];
|
|
27
29
|
appMembers: AppMember[];
|
|
28
30
|
appUserInfo: UserInfo;
|
|
29
31
|
appTeams: ExtendedTeam[];
|
|
@@ -56,6 +58,7 @@ declare module 'koas-security' {
|
|
|
56
58
|
declare module 'koas-parameters' {
|
|
57
59
|
interface PathParams {
|
|
58
60
|
appId: number;
|
|
61
|
+
appPath: string;
|
|
59
62
|
appCollectionId: number;
|
|
60
63
|
appOAuth2SecretId: number;
|
|
61
64
|
appSamlSecretId: number;
|
|
@@ -79,6 +82,8 @@ declare module 'koas-parameters' {
|
|
|
79
82
|
teamId: string;
|
|
80
83
|
token: string;
|
|
81
84
|
appServiceId: number;
|
|
85
|
+
appSecretId: number;
|
|
86
|
+
appVariableId: number;
|
|
82
87
|
userId: string;
|
|
83
88
|
memberEmail: string;
|
|
84
89
|
trainingBlockId: number;
|
|
@@ -239,6 +244,7 @@ export interface ReloadUserParams {
|
|
|
239
244
|
export interface ParseQueryParams {
|
|
240
245
|
$filter: string;
|
|
241
246
|
$orderby: string;
|
|
247
|
+
resourceDefinition: ResourceDefinition;
|
|
242
248
|
}
|
|
243
249
|
export interface GetAppResourceParams extends GetAppSubEntityParams {
|
|
244
250
|
findOptions: FindOptions;
|
|
@@ -380,5 +386,6 @@ export interface Options {
|
|
|
380
386
|
deleteAppAsset: (params: DeleteAppAssetParams) => Promise<number>;
|
|
381
387
|
email: (params: EmailParams) => Promise<void>;
|
|
382
388
|
sendNotifications: (params: SendNotificationsParams) => Promise<void>;
|
|
389
|
+
getAppVariables: (params: GetAppSubEntityParams) => Promise<AppConfigEntry[]>;
|
|
383
390
|
}
|
|
384
391
|
export {};
|