@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 CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.27.11/config/assets/logo.svg) Appsemble Node Utilities
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.28.0/config/assets/logo.svg) Appsemble Node Utilities
2
2
 
3
3
  > NodeJS utilities used by Appsemble internally.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@appsemble/node-utils)](https://www.npmjs.com/package/@appsemble/node-utils)
6
- [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.27.11/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.27.11)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.28.0/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.28.0)
7
7
  [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](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.27.11/LICENSE.md) ©
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.27.11",
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.27.11",
42
- "@appsemble/utils": "0.27.11",
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({ $filter: ctx.queryParams.$filter, $orderby: ctx.queryParams.$orderby });
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 { order, where } = generateQuery(ctx, options);
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((_c = (_b = user.EmailAuthorizations) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.verified),
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 {};