@appsemble/utils 0.30.1 → 0.30.3
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/BasePageDefinition.js +4 -0
- package/api/components/schemas/PageActionsDefinition.d.ts +2 -0
- package/api/components/schemas/{TabsPageActionsDefinition.js → PageActionsDefinition.js} +2 -2
- package/api/components/schemas/PageDefinition.js +3 -0
- package/api/components/schemas/TabsPageDefinition.js +1 -1
- package/api/components/schemas/index.d.ts +1 -1
- package/api/components/schemas/index.js +1 -1
- package/api/paths/apps.js +2 -2
- package/api/paths/users/current/apps.js +2 -2
- package/authorization.js +1 -1
- package/examples.js +29 -0
- package/package.json +2 -2
- package/reference-schemas/remappers/arrays.js +31 -0
- package/reference-schemas/remappers/unsorted.js +18 -0
- package/remap.js +27 -1
- package/remap.test.js +81 -0
- package/validation.js +1 -0
- package/validation.test.js +18 -12
- package/api/components/schemas/TabsPageActionsDefinition.d.ts +0 -2
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#  Appsemble Utilities
|
|
2
2
|
|
|
3
3
|
> Internal utility functions used across multiple Appsemble projects.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@appsemble/utils)
|
|
6
|
-
[](https://gitlab.com/appsemble/appsemble/-/releases/0.30.3)
|
|
7
7
|
[](https://prettier.io)
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
@@ -26,5 +26,5 @@ not guaranteed.
|
|
|
26
26
|
|
|
27
27
|
## License
|
|
28
28
|
|
|
29
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.30.
|
|
29
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.30.3/LICENSE.md) ©
|
|
30
30
|
[Appsemble](https://appsemble.com)
|
|
@@ -83,6 +83,10 @@ instructed to contact the app owner to get permissions.
|
|
|
83
83
|
theme: {
|
|
84
84
|
$ref: '#/components/schemas/Theme',
|
|
85
85
|
},
|
|
86
|
+
badgeCount: {
|
|
87
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
88
|
+
description: 'A Remapper that resolves to a number to be visibile in the side-menu.',
|
|
89
|
+
},
|
|
86
90
|
},
|
|
87
91
|
};
|
|
88
92
|
//# sourceMappingURL=BasePageDefinition.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const PageActionsDefinition = {
|
|
2
2
|
type: 'object',
|
|
3
3
|
description: 'Action fired on page events',
|
|
4
4
|
additionalProperties: false,
|
|
@@ -9,4 +9,4 @@ export const TabsPageActionsDefinition = {
|
|
|
9
9
|
},
|
|
10
10
|
},
|
|
11
11
|
};
|
|
12
|
-
//# sourceMappingURL=
|
|
12
|
+
//# sourceMappingURL=PageActionsDefinition.js.map
|
|
@@ -136,8 +136,8 @@ export * from './AppMemberPropertiesPatchActionDefinition.js';
|
|
|
136
136
|
export * from './AppMemberRoleUpdateActionDefinition.js';
|
|
137
137
|
export * from './LoopPageDefinition.js';
|
|
138
138
|
export * from './LoopPageActionsDefinition.js';
|
|
139
|
-
export * from './TabsPageActionsDefinition.js';
|
|
140
139
|
export * from './AppMembersDefinition.js';
|
|
141
140
|
export * from './AppMemberPropertyDefinition.js';
|
|
142
141
|
export * from './GroupMember.js';
|
|
143
142
|
export * from './AppAccount.js';
|
|
143
|
+
export * from './PageActionsDefinition.js';
|
|
@@ -136,9 +136,9 @@ export * from './AppMemberPropertiesPatchActionDefinition.js';
|
|
|
136
136
|
export * from './AppMemberRoleUpdateActionDefinition.js';
|
|
137
137
|
export * from './LoopPageDefinition.js';
|
|
138
138
|
export * from './LoopPageActionsDefinition.js';
|
|
139
|
-
export * from './TabsPageActionsDefinition.js';
|
|
140
139
|
export * from './AppMembersDefinition.js';
|
|
141
140
|
export * from './AppMemberPropertyDefinition.js';
|
|
142
141
|
export * from './GroupMember.js';
|
|
143
142
|
export * from './AppAccount.js';
|
|
143
|
+
export * from './PageActionsDefinition.js';
|
|
144
144
|
//# sourceMappingURL=index.js.map
|
package/api/paths/apps.js
CHANGED
|
@@ -121,11 +121,11 @@ export const pathItems = {
|
|
|
121
121
|
in: 'query',
|
|
122
122
|
},
|
|
123
123
|
],
|
|
124
|
-
description: 'Get all
|
|
124
|
+
description: 'Get all publically available apps.',
|
|
125
125
|
operationId: 'queryApps',
|
|
126
126
|
responses: {
|
|
127
127
|
200: {
|
|
128
|
-
description: 'The list of all apps.',
|
|
128
|
+
description: 'The list of all public apps.',
|
|
129
129
|
content: {
|
|
130
130
|
'application/json': {
|
|
131
131
|
schema: {
|
|
@@ -9,11 +9,11 @@ export const pathItems = {
|
|
|
9
9
|
in: 'query',
|
|
10
10
|
},
|
|
11
11
|
],
|
|
12
|
-
description: 'Get all apps
|
|
12
|
+
description: 'Get all apps from organizations that the user is in.',
|
|
13
13
|
operationId: 'queryCurrentUserApps',
|
|
14
14
|
responses: {
|
|
15
15
|
200: {
|
|
16
|
-
description: 'The list of all
|
|
16
|
+
description: 'The list of all apps the user is in.',
|
|
17
17
|
content: {
|
|
18
18
|
'application/json': {
|
|
19
19
|
schema: {
|
package/authorization.js
CHANGED
|
@@ -27,7 +27,7 @@ function checkAppPermissions(acquiredPermissions, requiredPermissions) {
|
|
|
27
27
|
}
|
|
28
28
|
export function getAppInheritedRoles(appSecurityDefinition, roles, accumulatedRoles = []) {
|
|
29
29
|
var _a;
|
|
30
|
-
if (!appSecurityDefinition) {
|
|
30
|
+
if (!appSecurityDefinition || !roles) {
|
|
31
31
|
return [];
|
|
32
32
|
}
|
|
33
33
|
for (const role of roles) {
|
package/examples.js
CHANGED
|
@@ -33,6 +33,11 @@ export const examples = {
|
|
|
33
33
|
result: {},
|
|
34
34
|
skip: true,
|
|
35
35
|
},
|
|
36
|
+
type: {
|
|
37
|
+
input: [1, 2, 3],
|
|
38
|
+
remapper: { type: null },
|
|
39
|
+
result: 'array',
|
|
40
|
+
},
|
|
36
41
|
array: {
|
|
37
42
|
input: ['a', 'b', 'c'],
|
|
38
43
|
remapper: {
|
|
@@ -112,6 +117,30 @@ export const examples = {
|
|
|
112
117
|
},
|
|
113
118
|
],
|
|
114
119
|
},
|
|
120
|
+
'array.filter': {
|
|
121
|
+
input: [
|
|
122
|
+
{
|
|
123
|
+
name: 'Peter',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'Louis',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'Brian',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
remapper: {
|
|
133
|
+
'array.filter': {
|
|
134
|
+
equals: [
|
|
135
|
+
{
|
|
136
|
+
prop: 'name',
|
|
137
|
+
},
|
|
138
|
+
'Louis',
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
result: [{ name: 'Louis' }],
|
|
143
|
+
},
|
|
115
144
|
'array.find': {
|
|
116
145
|
input: [
|
|
117
146
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/utils",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.3",
|
|
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.30.
|
|
40
|
+
"@appsemble/types": "0.30.3",
|
|
41
41
|
"axios": "^1.0.0",
|
|
42
42
|
"cron-parser": "^4.0.0",
|
|
43
43
|
"date-fns": "^2.0.0",
|
|
@@ -100,6 +100,37 @@ remapper is a filtered list:
|
|
|
100
100
|
"age": 20
|
|
101
101
|
}
|
|
102
102
|
]
|
|
103
|
+
\`\`\`
|
|
104
|
+
`,
|
|
105
|
+
},
|
|
106
|
+
'array.filter': {
|
|
107
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
108
|
+
description: `
|
|
109
|
+
Filters out values based on the input conditions provided.
|
|
110
|
+
|
|
111
|
+
For example:
|
|
112
|
+
|
|
113
|
+
${schemaExample('array.filter')}
|
|
114
|
+
|
|
115
|
+
Example of how it can be used to filter a number array.
|
|
116
|
+
|
|
117
|
+
Input:
|
|
118
|
+
|
|
119
|
+
\`\`\`json
|
|
120
|
+
[1,2,1,3,4,1,5]
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
\`\`\`yaml
|
|
124
|
+
array.filter:
|
|
125
|
+
equals:
|
|
126
|
+
- array: item
|
|
127
|
+
- 1
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
Result:
|
|
131
|
+
|
|
132
|
+
\`\`\`json
|
|
133
|
+
[1,1,1]
|
|
103
134
|
\`\`\`
|
|
104
135
|
`,
|
|
105
136
|
},
|
|
@@ -145,6 +145,24 @@ object will be checked. The result looks like this:
|
|
|
145
145
|
},
|
|
146
146
|
],
|
|
147
147
|
},
|
|
148
|
+
type: {
|
|
149
|
+
enum: [null],
|
|
150
|
+
description: `Returns the type of the input object
|
|
151
|
+
|
|
152
|
+
For example, with the following input value:
|
|
153
|
+
\`\`\`json
|
|
154
|
+
[0, 5, 7, 8]
|
|
155
|
+
\`\`\`
|
|
156
|
+
\`\`\`yaml
|
|
157
|
+
type: null
|
|
158
|
+
\`\`\`
|
|
159
|
+
|
|
160
|
+
The result will be:
|
|
161
|
+
\`\`\`json
|
|
162
|
+
"array"
|
|
163
|
+
\`\`\`
|
|
164
|
+
`,
|
|
165
|
+
},
|
|
148
166
|
log: {
|
|
149
167
|
enum: ['info', 'warn', 'error'],
|
|
150
168
|
description: `Logs in the browser's console and returns the incoming data and the remapper function's context.
|
package/remap.js
CHANGED
|
@@ -194,6 +194,16 @@ const mapperImplementations = {
|
|
|
194
194
|
}
|
|
195
195
|
return result;
|
|
196
196
|
},
|
|
197
|
+
type(args, input) {
|
|
198
|
+
// eslint-disable-next-line eqeqeq
|
|
199
|
+
if (input === null) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
if (Array.isArray(input)) {
|
|
203
|
+
return 'array';
|
|
204
|
+
}
|
|
205
|
+
return typeof input;
|
|
206
|
+
},
|
|
197
207
|
'array.map': (mapper, input, context) => {
|
|
198
208
|
var _a;
|
|
199
209
|
return (_a = input === null || input === void 0 ? void 0 : input.map((item, index) => remap(mapper, item, {
|
|
@@ -221,6 +231,19 @@ const mapperImplementations = {
|
|
|
221
231
|
});
|
|
222
232
|
},
|
|
223
233
|
array: (prop, input, context) => { var _a; return (_a = context.array) === null || _a === void 0 ? void 0 : _a[prop]; },
|
|
234
|
+
'array.filter'(mapper, input, context) {
|
|
235
|
+
if (!Array.isArray(input)) {
|
|
236
|
+
console.error(`${input} is not an array!`);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return input === null || input === void 0 ? void 0 : input.filter((item, index) => {
|
|
240
|
+
const remapped = remap(mapper, item, {
|
|
241
|
+
...context,
|
|
242
|
+
array: { index, length: input.length, item },
|
|
243
|
+
});
|
|
244
|
+
return remapped;
|
|
245
|
+
});
|
|
246
|
+
},
|
|
224
247
|
'array.find'(mapper, input, context) {
|
|
225
248
|
var _a;
|
|
226
249
|
if (!Array.isArray(input)) {
|
|
@@ -296,7 +319,10 @@ const mapperImplementations = {
|
|
|
296
319
|
}
|
|
297
320
|
}
|
|
298
321
|
else if (typeof prop === 'number' || typeof prop === 'string') {
|
|
299
|
-
result =
|
|
322
|
+
result =
|
|
323
|
+
Array.isArray(result) && typeof prop === 'number' && prop < 0
|
|
324
|
+
? result[result.length + prop]
|
|
325
|
+
: result[prop];
|
|
300
326
|
}
|
|
301
327
|
return result;
|
|
302
328
|
},
|
package/remap.test.js
CHANGED
|
@@ -723,6 +723,43 @@ describe('object.assign', () => {
|
|
|
723
723
|
},
|
|
724
724
|
});
|
|
725
725
|
});
|
|
726
|
+
describe('type', () => {
|
|
727
|
+
runTests({
|
|
728
|
+
'input array, type remapper test': {
|
|
729
|
+
input: [
|
|
730
|
+
{ firstName: 'John', lastName: 'Doe' },
|
|
731
|
+
{ firstName: 'Jane', lastName: 'Smith' },
|
|
732
|
+
],
|
|
733
|
+
mappers: { type: null },
|
|
734
|
+
expected: 'array',
|
|
735
|
+
},
|
|
736
|
+
'input object, type remapper test': {
|
|
737
|
+
input: { firstName: 'John', lastName: 'Doe' },
|
|
738
|
+
mappers: { type: null },
|
|
739
|
+
expected: 'object',
|
|
740
|
+
},
|
|
741
|
+
'input number, type remapper test': {
|
|
742
|
+
input: 1,
|
|
743
|
+
mappers: { type: null },
|
|
744
|
+
expected: 'number',
|
|
745
|
+
},
|
|
746
|
+
'input string, type remapper test': {
|
|
747
|
+
input: 'I am a string',
|
|
748
|
+
mappers: { type: null },
|
|
749
|
+
expected: 'string',
|
|
750
|
+
},
|
|
751
|
+
'null input type remapper test': {
|
|
752
|
+
input: null,
|
|
753
|
+
mappers: { type: null },
|
|
754
|
+
expected: null,
|
|
755
|
+
},
|
|
756
|
+
'undefined input type remapper test': {
|
|
757
|
+
input: undefined,
|
|
758
|
+
mappers: { type: null },
|
|
759
|
+
expected: 'undefined',
|
|
760
|
+
},
|
|
761
|
+
});
|
|
762
|
+
});
|
|
726
763
|
describe('array.map', () => {
|
|
727
764
|
runTests({
|
|
728
765
|
'apply remappers to each array item': {
|
|
@@ -877,6 +914,40 @@ describe('array', () => {
|
|
|
877
914
|
},
|
|
878
915
|
});
|
|
879
916
|
});
|
|
917
|
+
describe('array.filter', () => {
|
|
918
|
+
runTests({
|
|
919
|
+
'return a new array containing the object with specified value from array': {
|
|
920
|
+
input: [{ name: 'Craig' }, { name: 'Joey' }, { name: 'Stuart' }],
|
|
921
|
+
mappers: [{ 'array.filter': { equals: [{ prop: 'name' }, 'Craig'] } }],
|
|
922
|
+
expected: [{ name: 'Craig' }],
|
|
923
|
+
},
|
|
924
|
+
'return a new array containing the 2 objects with specified value from array': {
|
|
925
|
+
input: [{ name: 'Craig' }, { name: 'Joey' }, { name: 'Stuart' }, { name: 'Craig' }],
|
|
926
|
+
mappers: [{ 'array.filter': { equals: [{ prop: 'name' }, 'Craig'] } }],
|
|
927
|
+
expected: [{ name: 'Craig' }, { name: 'Craig' }],
|
|
928
|
+
},
|
|
929
|
+
'return a new array containing a single value when the array does not include objects': {
|
|
930
|
+
input: ['Craig', 'Joey', 'Stuart'],
|
|
931
|
+
mappers: [{ 'array.filter': { equals: [{ array: 'item' }, 'Craig'] } }],
|
|
932
|
+
expected: ['Craig'],
|
|
933
|
+
},
|
|
934
|
+
'return an empty array when condition doesn’t match anything': {
|
|
935
|
+
input: ['Craig', 'Joey', 'Stuart'],
|
|
936
|
+
mappers: [{ 'array.filter': { equals: [{ array: 'item' }, 'Peter'] } }],
|
|
937
|
+
expected: [],
|
|
938
|
+
},
|
|
939
|
+
'it should filter arrays with mixed content type(string)': {
|
|
940
|
+
input: ['Craig', 5, 'Joey', 7, 'Stuart'],
|
|
941
|
+
mappers: [{ 'array.filter': { equals: [{ array: 'item' }, 'Stuart'] } }],
|
|
942
|
+
expected: ['Stuart'],
|
|
943
|
+
},
|
|
944
|
+
'it should filter arrays with mixed content type based on type of the item': {
|
|
945
|
+
input: ['Craig', 5, 'Joey', 7, 'Stuart'],
|
|
946
|
+
mappers: [{ 'array.filter': { equals: [{ type: { array: 'item' } }, 'number'] } }],
|
|
947
|
+
expected: [5, 7],
|
|
948
|
+
},
|
|
949
|
+
});
|
|
950
|
+
});
|
|
880
951
|
describe('array.find', () => {
|
|
881
952
|
runTests({
|
|
882
953
|
'return object with specified value from array': {
|
|
@@ -1018,6 +1089,16 @@ describe('prop', () => {
|
|
|
1018
1089
|
mappers: { prop: 'foo' },
|
|
1019
1090
|
expected: null,
|
|
1020
1091
|
},
|
|
1092
|
+
'handle array input': {
|
|
1093
|
+
input: ['one', 'two', 'three', 'four'],
|
|
1094
|
+
mappers: { prop: 1 },
|
|
1095
|
+
expected: 'two',
|
|
1096
|
+
},
|
|
1097
|
+
'handle array input and negative index': {
|
|
1098
|
+
input: ['one', 'two', 'three', 'four'],
|
|
1099
|
+
mappers: { prop: -3 },
|
|
1100
|
+
expected: 'two',
|
|
1101
|
+
},
|
|
1021
1102
|
});
|
|
1022
1103
|
});
|
|
1023
1104
|
describe('random.choice', () => {
|
package/validation.js
CHANGED
|
@@ -1150,6 +1150,7 @@ export async function validateAppDefinition(definition, getBlockVersions, contro
|
|
|
1150
1150
|
result = validator.validate(definition, {});
|
|
1151
1151
|
}
|
|
1152
1152
|
if (!definition) {
|
|
1153
|
+
result.addError('App definition can not be null');
|
|
1153
1154
|
return result;
|
|
1154
1155
|
}
|
|
1155
1156
|
const blocks = getAppBlocks(definition);
|
package/validation.test.js
CHANGED
|
@@ -2001,20 +2001,26 @@ describe('validateAppDefinition', () => {
|
|
|
2001
2001
|
new ValidationError('there is no-one in the app, who has permissions to use this action', 'resource.get', undefined, ['controller', 'actions', 'onWhatever', 'resource']),
|
|
2002
2002
|
]);
|
|
2003
2003
|
});
|
|
2004
|
-
it('should
|
|
2004
|
+
it('should throw if an app is null', async () => {
|
|
2005
2005
|
const result = await validateAppDefinition(null, () => []);
|
|
2006
|
-
expect(result.valid).toBe(
|
|
2007
|
-
expect(result.errors).toStrictEqual([
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2006
|
+
expect(result.valid).toBe(false);
|
|
2007
|
+
expect(result.errors).toStrictEqual([
|
|
2008
|
+
expect.objectContaining({
|
|
2009
|
+
message: 'App definition can not be null',
|
|
2010
|
+
instance: null,
|
|
2011
|
+
schema: {},
|
|
2012
|
+
}),
|
|
2013
|
+
]);
|
|
2013
2014
|
});
|
|
2014
|
-
it('should report an error if
|
|
2015
|
-
const result = await validateAppDefinition(
|
|
2016
|
-
expect(result.valid).toBe(
|
|
2017
|
-
expect(result.errors).toStrictEqual([
|
|
2015
|
+
it('should report an error if the defaultPage does not exist', async () => {
|
|
2016
|
+
const result = await validateAppDefinition({ name: 'Test App', pages: [], defaultPage: 'Test Page' }, () => []);
|
|
2017
|
+
expect(result.valid).toBe(false);
|
|
2018
|
+
expect(result.errors).toStrictEqual([
|
|
2019
|
+
expect.objectContaining({
|
|
2020
|
+
instance: 'Test Page',
|
|
2021
|
+
message: 'does not refer to an existing page',
|
|
2022
|
+
}),
|
|
2023
|
+
]);
|
|
2018
2024
|
});
|
|
2019
2025
|
it('should handle if an unexpected error occurs', async () => {
|
|
2020
2026
|
const result = await validateAppDefinition({
|