@appsemble/utils 0.24.9 → 0.24.12
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/api/components/schemas/ResourceDefinition.js +1 -0
- package/api/components/schemas/UserPropertyDefinition.js +2 -0
- package/api/paths/apps.js +3 -3
- package/api/paths/user.js +1 -1
- package/constants/fonts.js +333 -18
- package/miscellaneous.js +3 -0
- package/miscellaneous.test.js +12 -0
- package/package.json +2 -2
- package/reference-schemas/remappers/data.js +26 -0
- package/remap.d.ts +5 -1
- package/remap.js +1 -0
- package/remap.test.js +2 -0
- package/validation.js +13 -0
- package/validation.test.js +90 -0
package/miscellaneous.js
CHANGED
package/miscellaneous.test.js
CHANGED
|
@@ -36,6 +36,18 @@ describe('stripNullValues', () => {
|
|
|
36
36
|
const input = [, undefined, null];
|
|
37
37
|
expect(stripNullValues(input)).toStrictEqual([]);
|
|
38
38
|
});
|
|
39
|
+
it('should not remove Blob values', () => {
|
|
40
|
+
const blob = new Blob([], { type: 'image/jpeg' });
|
|
41
|
+
const input = {
|
|
42
|
+
foo: null,
|
|
43
|
+
bar: blob,
|
|
44
|
+
baz: 'hello',
|
|
45
|
+
};
|
|
46
|
+
expect(stripNullValues(input)).toStrictEqual({
|
|
47
|
+
bar: blob,
|
|
48
|
+
baz: 'hello',
|
|
49
|
+
});
|
|
50
|
+
});
|
|
39
51
|
it('should remove undefined, and null values from objects', () => {
|
|
40
52
|
const input = {
|
|
41
53
|
foo: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/utils",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.12",
|
|
4
4
|
"description": "Utility functions used in Appsemble internally",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"test": "vitest"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@appsemble/types": "0.24.
|
|
40
|
+
"@appsemble/types": "0.24.12",
|
|
41
41
|
"axios": "^1.0.0",
|
|
42
42
|
"cron-parser": "^4.0.0",
|
|
43
43
|
"date-fns": "^2.0.0",
|
|
@@ -361,6 +361,32 @@ Example:
|
|
|
361
361
|
"sub": "5c6270e2-ad31-414f-bcab-6752a2c4dcfd",
|
|
362
362
|
"properties": {}
|
|
363
363
|
}
|
|
364
|
+
\`\`\`
|
|
365
|
+
`,
|
|
366
|
+
},
|
|
367
|
+
appMember: {
|
|
368
|
+
enum: ['userId', 'memberId', 'name', 'primary_email', 'role'],
|
|
369
|
+
description: `
|
|
370
|
+
> **Note:** For this remapper to work, the user that activated the remapper has to be logged in to
|
|
371
|
+
> the app
|
|
372
|
+
|
|
373
|
+
Provides some fields of the appMember object.
|
|
374
|
+
|
|
375
|
+
- \`userId\`: The id of the user to which the appMember object belongs.
|
|
376
|
+
- \`memberId\`: The id of the appMember object itself. This value should be used when fetching resources created by the current user.
|
|
377
|
+
- \`primary_email\`: User’s **primary** email address.
|
|
378
|
+
- \`name\`: The user’s name.
|
|
379
|
+
- \`role\`: User's role in the context of the app.
|
|
380
|
+
Example:
|
|
381
|
+
|
|
382
|
+
\`\`\`json
|
|
383
|
+
{
|
|
384
|
+
"memberId": "1f433c7a-54ea-466e-89f7-5dbb8f324b12",
|
|
385
|
+
"name": "Test User"
|
|
386
|
+
"primaryEmail": "example@hotmail.nl",
|
|
387
|
+
"role": "Medewerker",
|
|
388
|
+
"userId": "5c6270e2-ad31-414f-bcab-6752a2c4dcfd"
|
|
389
|
+
}
|
|
364
390
|
\`\`\`
|
|
365
391
|
`,
|
|
366
392
|
},
|
package/remap.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Remapper, type UserInfo } from '@appsemble/types';
|
|
1
|
+
import { type AppMember, type Remapper, type UserInfo } from '@appsemble/types';
|
|
2
2
|
import { type IntlMessageFormat } from 'intl-messageformat';
|
|
3
3
|
export interface IntlMessage {
|
|
4
4
|
id?: string;
|
|
@@ -48,6 +48,10 @@ export interface RemapperContext {
|
|
|
48
48
|
* A custom context passed to the remap function.
|
|
49
49
|
*/
|
|
50
50
|
context: Record<string, any>;
|
|
51
|
+
/**
|
|
52
|
+
* The appMember object for the current user in the app.
|
|
53
|
+
*/
|
|
54
|
+
appMember: AppMember;
|
|
51
55
|
}
|
|
52
56
|
interface InternalContext extends RemapperContext {
|
|
53
57
|
root?: unknown;
|
package/remap.js
CHANGED
|
@@ -347,5 +347,6 @@ const mapperImplementations = {
|
|
|
347
347
|
return message.format() || `{${messageId}}`;
|
|
348
348
|
},
|
|
349
349
|
user: (property, input, context) => { var _a; return (_a = context.userInfo) === null || _a === void 0 ? void 0 : _a[property]; },
|
|
350
|
+
appMember: (property, input, context) => { var _a, _b; return (_b = (_a = context.userInfo) === null || _a === void 0 ? void 0 : _a.appMember) === null || _b === void 0 ? void 0 : _b[property]; },
|
|
350
351
|
};
|
|
351
352
|
//# sourceMappingURL=remap.js.map
|
package/remap.test.js
CHANGED
|
@@ -13,6 +13,7 @@ function runTests(tests) {
|
|
|
13
13
|
appId: 6789,
|
|
14
14
|
locale: 'en',
|
|
15
15
|
pageData: { hello: 'Page data' },
|
|
16
|
+
appMember: userInfo === null || userInfo === void 0 ? void 0 : userInfo.appMember,
|
|
16
17
|
});
|
|
17
18
|
expect(result).toStrictEqual(expected);
|
|
18
19
|
});
|
|
@@ -234,6 +235,7 @@ describe('log', () => {
|
|
|
234
235
|
appId: 6789,
|
|
235
236
|
locale: 'en',
|
|
236
237
|
pageData: { hello: 'Page data' },
|
|
238
|
+
appMember: userInfo === null || userInfo === void 0 ? void 0 : userInfo.appMember,
|
|
237
239
|
});
|
|
238
240
|
expect(console[mappers.log]).toHaveBeenCalledWith(expected);
|
|
239
241
|
});
|
package/validation.js
CHANGED
|
@@ -76,6 +76,19 @@ function validateResourceSchemas(definition, report) {
|
|
|
76
76
|
}
|
|
77
77
|
const { schema } = resource;
|
|
78
78
|
const prefix = ['resources', resourceName, 'schema'];
|
|
79
|
+
const reservedKeywords = new Set([
|
|
80
|
+
'created',
|
|
81
|
+
'updated',
|
|
82
|
+
'author',
|
|
83
|
+
'editor',
|
|
84
|
+
'seed',
|
|
85
|
+
'ephemeral',
|
|
86
|
+
'clonable',
|
|
87
|
+
'expires',
|
|
88
|
+
]);
|
|
89
|
+
if (reservedKeywords.has(resourceName)) {
|
|
90
|
+
report(schema, 'is a reserved keyword', ['resources', resourceName]);
|
|
91
|
+
}
|
|
79
92
|
validateJSONSchema(schema, prefix, report);
|
|
80
93
|
if (!('type' in schema)) {
|
|
81
94
|
report(schema, 'must define type object', prefix);
|
package/validation.test.js
CHANGED
|
@@ -866,6 +866,96 @@ describe('validateAppDefinition', () => {
|
|
|
866
866
|
new ValidationError('does not exist in this app’s roles', 'Unknown', undefined, ['roles', 0]),
|
|
867
867
|
]);
|
|
868
868
|
});
|
|
869
|
+
it('should validate resource types against reserved keywords', async () => {
|
|
870
|
+
const app = {
|
|
871
|
+
name: 'Test app',
|
|
872
|
+
defaultPage: 'Test Page',
|
|
873
|
+
pages: [
|
|
874
|
+
{
|
|
875
|
+
name: 'Test Page',
|
|
876
|
+
blocks: [],
|
|
877
|
+
},
|
|
878
|
+
],
|
|
879
|
+
resources: {
|
|
880
|
+
created: {
|
|
881
|
+
schema: {
|
|
882
|
+
type: 'object',
|
|
883
|
+
properties: {
|
|
884
|
+
name: { type: 'string' },
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
updated: {
|
|
889
|
+
schema: {
|
|
890
|
+
type: 'object',
|
|
891
|
+
properties: {
|
|
892
|
+
name: { type: 'string' },
|
|
893
|
+
},
|
|
894
|
+
},
|
|
895
|
+
},
|
|
896
|
+
author: {
|
|
897
|
+
schema: {
|
|
898
|
+
type: 'object',
|
|
899
|
+
properties: {
|
|
900
|
+
name: { type: 'string' },
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
editor: {
|
|
905
|
+
schema: {
|
|
906
|
+
type: 'object',
|
|
907
|
+
properties: {
|
|
908
|
+
name: { type: 'string' },
|
|
909
|
+
},
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
seed: {
|
|
913
|
+
schema: {
|
|
914
|
+
type: 'object',
|
|
915
|
+
properties: {
|
|
916
|
+
name: { type: 'string' },
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
},
|
|
920
|
+
ephemeral: {
|
|
921
|
+
schema: {
|
|
922
|
+
type: 'object',
|
|
923
|
+
properties: {
|
|
924
|
+
name: { type: 'string' },
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
},
|
|
928
|
+
clonable: {
|
|
929
|
+
schema: {
|
|
930
|
+
type: 'object',
|
|
931
|
+
properties: {
|
|
932
|
+
name: { type: 'string' },
|
|
933
|
+
},
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
expires: {
|
|
937
|
+
schema: {
|
|
938
|
+
type: 'object',
|
|
939
|
+
properties: {
|
|
940
|
+
name: { type: 'string' },
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
},
|
|
945
|
+
};
|
|
946
|
+
const result = await validateAppDefinition(app, () => []);
|
|
947
|
+
expect(result.valid).toBe(false);
|
|
948
|
+
expect(result.errors).toStrictEqual([
|
|
949
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'created']),
|
|
950
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'updated']),
|
|
951
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'author']),
|
|
952
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'editor']),
|
|
953
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'seed']),
|
|
954
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'ephemeral']),
|
|
955
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'clonable']),
|
|
956
|
+
new ValidationError('is a reserved keyword', { type: 'object', properties: { name: { type: 'string' } } }, undefined, ['resources', 'expires']),
|
|
957
|
+
]);
|
|
958
|
+
});
|
|
869
959
|
it('should validate the resource roles exist', async () => {
|
|
870
960
|
const app = createTestApp();
|
|
871
961
|
app.resources.person.roles = ['Unknown'];
|