@appsemble/utils 0.23.9 → 0.24.1
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/parameters/index.d.ts +2 -0
- package/api/components/parameters/index.js +2 -0
- package/api/components/parameters/memberEmail.d.ts +2 -0
- package/api/components/parameters/memberEmail.js +8 -0
- package/api/components/parameters/roles.d.ts +2 -0
- package/api/components/parameters/roles.js +13 -0
- package/api/components/schemas/ActionDefinition.js +2 -0
- package/api/components/schemas/AppDefinition.js +3 -0
- package/api/components/schemas/LinkActionDefinition.js +4 -1
- package/api/components/schemas/OrganizationMember.d.ts +2 -0
- package/api/components/schemas/{Member.js → OrganizationMember.js} +2 -2
- package/api/components/schemas/Resource.js +14 -2
- package/api/components/schemas/ResourceDefinition.js +8 -0
- package/api/components/schemas/Training.js +6 -3
- package/api/components/schemas/UserCreateActionDefinition.js +5 -5
- package/api/components/schemas/UserPropertyDefinition.d.ts +2 -0
- package/api/components/schemas/UserPropertyDefinition.js +47 -0
- package/api/components/schemas/UserQueryActionDefinition.d.ts +1 -0
- package/api/components/schemas/UserQueryActionDefinition.js +20 -0
- package/api/components/schemas/UserRegisterActionDefinition.js +1 -1
- package/api/components/schemas/UserRemoveActionDefinition.d.ts +1 -0
- package/api/components/schemas/UserRemoveActionDefinition.js +20 -0
- package/api/components/schemas/UserUpdateActionDefinition.js +12 -9
- package/api/components/schemas/UsersDefinition.d.ts +2 -0
- package/api/components/schemas/UsersDefinition.js +17 -0
- package/api/components/schemas/index.d.ts +5 -1
- package/api/components/schemas/index.js +5 -1
- package/api/paths/apps.js +21 -10
- package/api/paths/assets.js +4 -0
- package/api/paths/organizations.js +2 -2
- package/api/paths/templates.js +4 -0
- package/api/paths/trainings.js +31 -5
- package/api/paths/user.js +82 -1
- package/constants/Permission.d.ts +17 -1
- package/constants/Permission.js +16 -0
- package/constants/roles.d.ts +1 -0
- package/constants/roles.js +8 -0
- package/package.json +2 -2
- package/validation.d.ts +2 -2
- package/validation.js +58 -9
- package/validation.test.js +181 -1
- package/api/components/schemas/Member.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.24.1)
|
|
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.
|
|
29
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.24.1/LICENSE.md) ©
|
|
30
30
|
[Appsemble](https://appsemble.com)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const memberEmail = {
|
|
2
|
+
name: 'memberEmail',
|
|
3
|
+
in: 'path',
|
|
4
|
+
description: 'The email of the member on which to perform an operation',
|
|
5
|
+
required: true,
|
|
6
|
+
schema: { $ref: '#/components/schemas/OrganizationMember/properties/primaryEmail' },
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=memberEmail.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const roles = {
|
|
2
|
+
name: 'roles',
|
|
3
|
+
in: 'query',
|
|
4
|
+
description: 'The roles of users on which to perform an operation',
|
|
5
|
+
required: true,
|
|
6
|
+
schema: {
|
|
7
|
+
type: 'array',
|
|
8
|
+
items: {
|
|
9
|
+
$ref: '#/components/schemas/AppAccount/properties/role',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=roles.js.map
|
|
@@ -77,6 +77,8 @@ export const ActionDefinition = {
|
|
|
77
77
|
{ $ref: '#/components/schemas/UserRegisterActionDefinition' },
|
|
78
78
|
{ $ref: '#/components/schemas/UserCreateActionDefinition' },
|
|
79
79
|
{ $ref: '#/components/schemas/UserUpdateActionDefinition' },
|
|
80
|
+
{ $ref: '#/components/schemas/UserQueryActionDefinition' },
|
|
81
|
+
{ $ref: '#/components/schemas/UserRemoveActionDefinition' },
|
|
80
82
|
],
|
|
81
83
|
},
|
|
82
84
|
],
|
|
@@ -68,6 +68,9 @@ This **must** match the name of a page defined for the app.
|
|
|
68
68
|
controller: {
|
|
69
69
|
$ref: '#/components/schemas/ControllerDefinition',
|
|
70
70
|
},
|
|
71
|
+
users: {
|
|
72
|
+
$ref: '#/components/schemas/UsersDefinition',
|
|
73
|
+
},
|
|
71
74
|
resources: {
|
|
72
75
|
type: 'object',
|
|
73
76
|
description: `Resources define how Appsemble can store data for an app.
|
|
@@ -10,7 +10,6 @@ export const LinkActionDefinition = extendJSONSchema(BaseActionDefinition, {
|
|
|
10
10
|
description: 'The link action can be used to redirect the user to other pages or absolute URLs.',
|
|
11
11
|
},
|
|
12
12
|
to: {
|
|
13
|
-
description: 'The name of the page to link to. Subpages can be referred to using arrays. If this matches with an absolute URL, link will open this instead of matching it with a page or subpage.',
|
|
14
13
|
anyOf: [
|
|
15
14
|
{
|
|
16
15
|
type: 'string',
|
|
@@ -19,7 +18,11 @@ export const LinkActionDefinition = extendJSONSchema(BaseActionDefinition, {
|
|
|
19
18
|
type: 'array',
|
|
20
19
|
items: { type: 'string' },
|
|
21
20
|
},
|
|
21
|
+
{
|
|
22
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
23
|
+
},
|
|
22
24
|
],
|
|
25
|
+
description: 'The name of the page to link to. Subpages can be referred to using arrays. If this matches with an absolute URL, link will open this instead of matching it with a page or subpage.',
|
|
23
26
|
},
|
|
24
27
|
},
|
|
25
28
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { roles } from '../../../constants/index.js';
|
|
2
|
-
export const
|
|
2
|
+
export const OrganizationMember = {
|
|
3
3
|
type: 'object',
|
|
4
4
|
description: 'A member of an organization.',
|
|
5
5
|
required: ['id'],
|
|
@@ -26,4 +26,4 @@ export const Member = {
|
|
|
26
26
|
},
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
|
-
//# sourceMappingURL=
|
|
29
|
+
//# sourceMappingURL=OrganizationMember.js.map
|
|
@@ -10,9 +10,21 @@ export const Resource = {
|
|
|
10
10
|
$clonable: {
|
|
11
11
|
type: 'boolean',
|
|
12
12
|
},
|
|
13
|
+
$ephemeral: {
|
|
14
|
+
type: 'boolean',
|
|
15
|
+
},
|
|
13
16
|
$expires: {
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
anyOf: [
|
|
18
|
+
{
|
|
19
|
+
type: 'string',
|
|
20
|
+
format: 'date-time',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: 'string',
|
|
24
|
+
pattern: /^(\d+(y|yr|years))?\s*(\d+months)?\s*(\d+(w|wk|weeks))?\s*(\d+(d|days))?\s*(\d+(h|hr|hours))?\s*(\d+(m|min|minutes))?\s*(\d+(s|sec|seconds))?$/
|
|
25
|
+
.source,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
16
28
|
},
|
|
17
29
|
},
|
|
18
30
|
};
|
|
@@ -62,6 +62,14 @@ Example: 1d 8h 30m
|
|
|
62
62
|
pattern: /^(\d+(y|yr|years))?\s*(\d+months)?\s*(\d+(w|wk|weeks))?\s*(\d+(d|days))?\s*(\d+(h|hr|hours))?\s*(\d+(m|min|minutes))?\s*(\d+(s|sec|seconds))?$/
|
|
63
63
|
.source,
|
|
64
64
|
},
|
|
65
|
+
clonable: {
|
|
66
|
+
type: 'boolean',
|
|
67
|
+
description: 'Whether the resource should be able to be transferred when cloning the app it belongs to',
|
|
68
|
+
},
|
|
69
|
+
ephemeral: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
description: 'Whether the resource should be cleaned up regularly.',
|
|
72
|
+
},
|
|
65
73
|
schema: {
|
|
66
74
|
$ref: '#/components/schemas/JSONSchemaRoot',
|
|
67
75
|
description: 'JSON schema definitions that may be used by the app.',
|
|
@@ -17,9 +17,12 @@ export const Training = {
|
|
|
17
17
|
type: 'string',
|
|
18
18
|
description: 'A brief overview of the training.',
|
|
19
19
|
},
|
|
20
|
-
|
|
21
|
-
type: '
|
|
22
|
-
description: '
|
|
20
|
+
competences: {
|
|
21
|
+
type: 'array',
|
|
22
|
+
description: 'Competence tags for the training',
|
|
23
|
+
items: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
},
|
|
23
26
|
},
|
|
24
27
|
difficultyLevel: {
|
|
25
28
|
type: 'number',
|
|
@@ -11,19 +11,19 @@ export const UserCreateActionDefinition = extendJSONSchema(BaseActionDefinition,
|
|
|
11
11
|
|
|
12
12
|
Does nothing if the user is already logged in.`,
|
|
13
13
|
},
|
|
14
|
-
|
|
15
|
-
description: 'The
|
|
14
|
+
name: {
|
|
15
|
+
description: 'The display name of the user.',
|
|
16
16
|
},
|
|
17
17
|
email: {
|
|
18
18
|
description: 'The email to login with.',
|
|
19
19
|
},
|
|
20
|
-
|
|
21
|
-
description: 'The
|
|
20
|
+
password: {
|
|
21
|
+
description: 'The password to login with.',
|
|
22
22
|
},
|
|
23
23
|
properties: {
|
|
24
24
|
description: `The custom properties for the user.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Values will be validated based on \`user.properties\`, if defined in the app definition.`,
|
|
27
27
|
},
|
|
28
28
|
role: {
|
|
29
29
|
description: "The role for the created account. Defaults to the default role in the app's security definition.",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const UserPropertyDefinition = {
|
|
2
|
+
anyOf: [
|
|
3
|
+
{
|
|
4
|
+
type: 'object',
|
|
5
|
+
description: 'Definition for a user property.',
|
|
6
|
+
required: ['schema'],
|
|
7
|
+
additionalProperties: false,
|
|
8
|
+
properties: {
|
|
9
|
+
schema: {
|
|
10
|
+
anyOf: [
|
|
11
|
+
{ $ref: '#/components/schemas/JSONSchemaInteger' },
|
|
12
|
+
{ $ref: '#/components/schemas/JSONSchemaArray' },
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
reference: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
additionalProperties: false,
|
|
18
|
+
description: 'The object for the reference to another resource',
|
|
19
|
+
properties: {
|
|
20
|
+
resource: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'The resource referenced by this user property.',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'object',
|
|
30
|
+
description: 'Definition for a user property.',
|
|
31
|
+
required: ['schema'],
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
properties: {
|
|
34
|
+
schema: {
|
|
35
|
+
anyOf: [
|
|
36
|
+
{ $ref: '#/components/schemas/JSONSchemaString' },
|
|
37
|
+
{ $ref: '#/components/schemas/JSONSchemaNumber' },
|
|
38
|
+
{ $ref: '#/components/schemas/JSONSchemaEnum' },
|
|
39
|
+
{ $ref: '#/components/schemas/JSONSchemaBoolean' },
|
|
40
|
+
{ $ref: '#/components/schemas/JSONSchemaObject' },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=UserPropertyDefinition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const UserQueryActionDefinition: import("openapi-types").OpenAPIV3.SchemaObject;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseActionDefinition } from './BaseActionDefinition.js';
|
|
2
|
+
import { extendJSONSchema } from './utils.js';
|
|
3
|
+
export const UserQueryActionDefinition = extendJSONSchema(BaseActionDefinition, {
|
|
4
|
+
type: 'object',
|
|
5
|
+
additionalProperties: false,
|
|
6
|
+
required: [],
|
|
7
|
+
properties: {
|
|
8
|
+
type: {
|
|
9
|
+
enum: ['user.query'],
|
|
10
|
+
description: `Allows the user to fetch a list of accounts by their roles.
|
|
11
|
+
|
|
12
|
+
Does nothing if the user isn’t logged in.`,
|
|
13
|
+
},
|
|
14
|
+
roles: {
|
|
15
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
16
|
+
description: 'The roles of the accounts that would be fetched.',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=UserQueryActionDefinition.js.map
|
|
@@ -26,7 +26,7 @@ Does nothing if the user is already logged in.`,
|
|
|
26
26
|
properties: {
|
|
27
27
|
description: `The custom properties for the user.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Values will be validated based on \`user.properties\`, if defined in the app definition.`,
|
|
30
30
|
},
|
|
31
31
|
login: {
|
|
32
32
|
description: 'Whether to login after registering.',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const UserRemoveActionDefinition: import("openapi-types").OpenAPIV3.SchemaObject;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseActionDefinition } from './BaseActionDefinition.js';
|
|
2
|
+
import { extendJSONSchema } from './utils.js';
|
|
3
|
+
export const UserRemoveActionDefinition = extendJSONSchema(BaseActionDefinition, {
|
|
4
|
+
type: 'object',
|
|
5
|
+
additionalProperties: false,
|
|
6
|
+
required: ['email'],
|
|
7
|
+
properties: {
|
|
8
|
+
type: {
|
|
9
|
+
enum: ['user.remove'],
|
|
10
|
+
description: `Allows the user to delete an existing account.
|
|
11
|
+
|
|
12
|
+
Does nothing if the user isn’t logged in.`,
|
|
13
|
+
},
|
|
14
|
+
email: {
|
|
15
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
16
|
+
description: 'The email of the account to be deleted.',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=UserRemoveActionDefinition.js.map
|
|
@@ -11,22 +11,25 @@ export const UserUpdateActionDefinition = extendJSONSchema(BaseActionDefinition,
|
|
|
11
11
|
|
|
12
12
|
Does nothing if the user isn’t logged in.`,
|
|
13
13
|
},
|
|
14
|
-
|
|
15
|
-
description: 'The new
|
|
14
|
+
name: {
|
|
15
|
+
description: 'The new display name of the user.',
|
|
16
16
|
},
|
|
17
|
-
|
|
18
|
-
description: 'The
|
|
17
|
+
currentEmail: {
|
|
18
|
+
description: 'The current email address of the user.',
|
|
19
19
|
},
|
|
20
|
-
|
|
21
|
-
description: 'The new
|
|
20
|
+
newEmail: {
|
|
21
|
+
description: 'The new email address.',
|
|
22
22
|
},
|
|
23
|
-
|
|
24
|
-
description: 'The new
|
|
23
|
+
password: {
|
|
24
|
+
description: 'The new password.',
|
|
25
25
|
},
|
|
26
26
|
properties: {
|
|
27
27
|
description: `The custom properties for the user.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Values will be validated based on \`user.properties\`, if defined in the app definition.`,
|
|
30
|
+
},
|
|
31
|
+
role: {
|
|
32
|
+
description: "The role for the updated account. Defaults to the default role in the app's security definition.",
|
|
30
33
|
},
|
|
31
34
|
},
|
|
32
35
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const UsersDefinition = {
|
|
2
|
+
type: 'object',
|
|
3
|
+
description: 'Definition for user properties.',
|
|
4
|
+
required: [],
|
|
5
|
+
additionalProperties: false,
|
|
6
|
+
properties: {
|
|
7
|
+
properties: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
description: 'The properties object configuring users in the app',
|
|
10
|
+
additionalProperties: {
|
|
11
|
+
description: 'A single user property definition.',
|
|
12
|
+
$ref: '#/components/schemas/UserPropertyDefinition',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=UsersDefinition.js.map
|
|
@@ -57,7 +57,7 @@ export * from './LinkActionDefinition.js';
|
|
|
57
57
|
export * from './LinkBackActionDefinition.js';
|
|
58
58
|
export * from './LinkNextActionDefinition.js';
|
|
59
59
|
export * from './LogActionDefinition.js';
|
|
60
|
-
export * from './
|
|
60
|
+
export * from './OrganizationMember.js';
|
|
61
61
|
export * from './MatchActionDefinition.js';
|
|
62
62
|
export * from './MessageActionDefinition.js';
|
|
63
63
|
export * from './NoopActionDefinition.js';
|
|
@@ -123,5 +123,9 @@ export * from './UserLoginActionDefinition.js';
|
|
|
123
123
|
export * from './UserRegisterActionDefinition.js';
|
|
124
124
|
export * from './UserCreateActionDefinition.js';
|
|
125
125
|
export * from './UserUpdateActionDefinition.js';
|
|
126
|
+
export * from './UserQueryActionDefinition.js';
|
|
127
|
+
export * from './UserRemoveActionDefinition.js';
|
|
126
128
|
export * from './LoopPageDefinition.js';
|
|
127
129
|
export * from './LoopPageActionsDefinition.js';
|
|
130
|
+
export * from './UsersDefinition.js';
|
|
131
|
+
export * from './UserPropertyDefinition.js';
|
|
@@ -57,7 +57,7 @@ export * from './LinkActionDefinition.js';
|
|
|
57
57
|
export * from './LinkBackActionDefinition.js';
|
|
58
58
|
export * from './LinkNextActionDefinition.js';
|
|
59
59
|
export * from './LogActionDefinition.js';
|
|
60
|
-
export * from './
|
|
60
|
+
export * from './OrganizationMember.js';
|
|
61
61
|
export * from './MatchActionDefinition.js';
|
|
62
62
|
export * from './MessageActionDefinition.js';
|
|
63
63
|
export * from './NoopActionDefinition.js';
|
|
@@ -123,6 +123,10 @@ export * from './UserLoginActionDefinition.js';
|
|
|
123
123
|
export * from './UserRegisterActionDefinition.js';
|
|
124
124
|
export * from './UserCreateActionDefinition.js';
|
|
125
125
|
export * from './UserUpdateActionDefinition.js';
|
|
126
|
+
export * from './UserQueryActionDefinition.js';
|
|
127
|
+
export * from './UserRemoveActionDefinition.js';
|
|
126
128
|
export * from './LoopPageDefinition.js';
|
|
127
129
|
export * from './LoopPageActionsDefinition.js';
|
|
130
|
+
export * from './UsersDefinition.js';
|
|
131
|
+
export * from './UserPropertyDefinition.js';
|
|
128
132
|
//# sourceMappingURL=index.js.map
|
package/api/paths/apps.js
CHANGED
|
@@ -577,7 +577,7 @@ export const paths = {
|
|
|
577
577
|
schema: {
|
|
578
578
|
type: 'array',
|
|
579
579
|
items: {
|
|
580
|
-
$ref: '#/components/schemas/
|
|
580
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
581
581
|
},
|
|
582
582
|
},
|
|
583
583
|
},
|
|
@@ -608,7 +608,7 @@ export const paths = {
|
|
|
608
608
|
content: {
|
|
609
609
|
'application/json': {
|
|
610
610
|
schema: {
|
|
611
|
-
$ref: '#/components/schemas/
|
|
611
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
612
612
|
},
|
|
613
613
|
},
|
|
614
614
|
},
|
|
@@ -647,7 +647,7 @@ export const paths = {
|
|
|
647
647
|
content: {
|
|
648
648
|
'application/json': {
|
|
649
649
|
schema: {
|
|
650
|
-
$ref: '#/components/schemas/
|
|
650
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
651
651
|
},
|
|
652
652
|
},
|
|
653
653
|
},
|
|
@@ -1236,7 +1236,7 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1236
1236
|
schema: {
|
|
1237
1237
|
type: 'array',
|
|
1238
1238
|
items: {
|
|
1239
|
-
$ref: '#/components/schemas/
|
|
1239
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
1240
1240
|
},
|
|
1241
1241
|
},
|
|
1242
1242
|
},
|
|
@@ -1270,7 +1270,7 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1270
1270
|
content: {
|
|
1271
1271
|
'application/json': {
|
|
1272
1272
|
schema: {
|
|
1273
|
-
$ref: '#/components/schemas/
|
|
1273
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
1274
1274
|
},
|
|
1275
1275
|
},
|
|
1276
1276
|
},
|
|
@@ -1307,7 +1307,7 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1307
1307
|
content: {
|
|
1308
1308
|
'application/json': {
|
|
1309
1309
|
schema: {
|
|
1310
|
-
$ref: '#/components/schemas/
|
|
1310
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
1311
1311
|
},
|
|
1312
1312
|
},
|
|
1313
1313
|
},
|
|
@@ -1342,7 +1342,7 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1342
1342
|
content: {
|
|
1343
1343
|
'application/json': {
|
|
1344
1344
|
schema: {
|
|
1345
|
-
$ref: '#/components/schemas/
|
|
1345
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
1346
1346
|
},
|
|
1347
1347
|
},
|
|
1348
1348
|
},
|
|
@@ -1408,7 +1408,7 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1408
1408
|
content: {
|
|
1409
1409
|
'application/json': {
|
|
1410
1410
|
schema: {
|
|
1411
|
-
$ref: '#/components/schemas/
|
|
1411
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
1412
1412
|
},
|
|
1413
1413
|
},
|
|
1414
1414
|
},
|
|
@@ -1438,7 +1438,7 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1438
1438
|
content: {
|
|
1439
1439
|
'application/json': {
|
|
1440
1440
|
schema: {
|
|
1441
|
-
$ref: '#/components/schemas/
|
|
1441
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
1442
1442
|
},
|
|
1443
1443
|
},
|
|
1444
1444
|
},
|
|
@@ -1468,7 +1468,7 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1468
1468
|
content: {
|
|
1469
1469
|
'application/json': {
|
|
1470
1470
|
schema: {
|
|
1471
|
-
$ref: '#/components/schemas/
|
|
1471
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
1472
1472
|
},
|
|
1473
1473
|
},
|
|
1474
1474
|
},
|
|
@@ -1477,5 +1477,16 @@ This will return a 404 if the user has not uploaded one.`,
|
|
|
1477
1477
|
security: [{ app: ['teams:read'] }],
|
|
1478
1478
|
},
|
|
1479
1479
|
},
|
|
1480
|
+
'/api/apps/{appId}/reseed': {
|
|
1481
|
+
parameters: [{ $ref: '#/components/parameters/appId' }],
|
|
1482
|
+
post: {
|
|
1483
|
+
tags: ['app'],
|
|
1484
|
+
operationId: 'reseedDemoApp',
|
|
1485
|
+
responses: {
|
|
1486
|
+
200: { description: 'The app has successfully been reseeded.' },
|
|
1487
|
+
},
|
|
1488
|
+
security: [{ studio: ['apps:write'] }],
|
|
1489
|
+
},
|
|
1490
|
+
},
|
|
1480
1491
|
};
|
|
1481
1492
|
//# sourceMappingURL=apps.js.map
|
package/api/paths/assets.js
CHANGED
|
@@ -44,6 +44,10 @@ export const paths = {
|
|
|
44
44
|
pattern: normalized.source,
|
|
45
45
|
description: 'The given name of the asset. Assets may be referenced by their name or ID in the API.',
|
|
46
46
|
},
|
|
47
|
+
clonable: {
|
|
48
|
+
type: 'boolean',
|
|
49
|
+
description: 'Whether the asset should be transferable when cloning the app they are in.',
|
|
50
|
+
},
|
|
47
51
|
},
|
|
48
52
|
},
|
|
49
53
|
},
|
|
@@ -164,7 +164,7 @@ export const paths = {
|
|
|
164
164
|
schema: {
|
|
165
165
|
type: 'array',
|
|
166
166
|
items: {
|
|
167
|
-
$ref: '#/components/schemas/
|
|
167
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
168
168
|
},
|
|
169
169
|
},
|
|
170
170
|
},
|
|
@@ -409,7 +409,7 @@ export const paths = {
|
|
|
409
409
|
content: {
|
|
410
410
|
'application/json': {
|
|
411
411
|
schema: {
|
|
412
|
-
$ref: '#/components/schemas/
|
|
412
|
+
$ref: '#/components/schemas/OrganizationMember',
|
|
413
413
|
},
|
|
414
414
|
},
|
|
415
415
|
},
|
package/api/paths/templates.js
CHANGED
|
@@ -64,6 +64,10 @@ export const paths = {
|
|
|
64
64
|
type: 'boolean',
|
|
65
65
|
description: 'Include example resources.',
|
|
66
66
|
},
|
|
67
|
+
assets: {
|
|
68
|
+
type: 'boolean',
|
|
69
|
+
description: 'Include example assets.',
|
|
70
|
+
},
|
|
67
71
|
visibility: {
|
|
68
72
|
$ref: '#/components/schemas/App/properties/visibility',
|
|
69
73
|
},
|
package/api/paths/trainings.js
CHANGED
|
@@ -17,7 +17,7 @@ export const paths = {
|
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
|
-
security: [{ studio: [] }],
|
|
20
|
+
security: [{ studio: [] }, {}],
|
|
21
21
|
},
|
|
22
22
|
post: {
|
|
23
23
|
tags: ['training', 'learning', 'docs'],
|
|
@@ -60,7 +60,7 @@ export const paths = {
|
|
|
60
60
|
},
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
|
-
security: [{ studio: [] }],
|
|
63
|
+
security: [{ studio: [] }, {}],
|
|
64
64
|
},
|
|
65
65
|
patch: {
|
|
66
66
|
tags: ['training', 'learning', 'docs'],
|
|
@@ -69,9 +69,35 @@ export const paths = {
|
|
|
69
69
|
requestBody: {
|
|
70
70
|
required: true,
|
|
71
71
|
content: {
|
|
72
|
-
'
|
|
72
|
+
'multipart/form-data': {
|
|
73
73
|
schema: {
|
|
74
|
-
|
|
74
|
+
description: 'Schema to be used for editing trainings.',
|
|
75
|
+
properties: {
|
|
76
|
+
id: {
|
|
77
|
+
type: 'number',
|
|
78
|
+
readOnly: true,
|
|
79
|
+
minimum: 0,
|
|
80
|
+
description: 'The id of the training, will be generated automatically by the system.',
|
|
81
|
+
},
|
|
82
|
+
title: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'Title of the training.',
|
|
85
|
+
},
|
|
86
|
+
description: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'A brief overview of the training.',
|
|
89
|
+
},
|
|
90
|
+
competence: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'Competence tags for the training',
|
|
93
|
+
},
|
|
94
|
+
difficultyLevel: {
|
|
95
|
+
type: 'number',
|
|
96
|
+
description: 'Difficulty level between 1 and 5',
|
|
97
|
+
minimum: 1,
|
|
98
|
+
maximum: 5,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
75
101
|
},
|
|
76
102
|
},
|
|
77
103
|
},
|
|
@@ -119,7 +145,7 @@ export const paths = {
|
|
|
119
145
|
},
|
|
120
146
|
},
|
|
121
147
|
},
|
|
122
|
-
security: [{ studio: [] }],
|
|
148
|
+
security: [{ studio: [] }, {}],
|
|
123
149
|
},
|
|
124
150
|
post: {
|
|
125
151
|
tags: ['training', 'trainingBlocks', 'learning', 'docs'],
|
package/api/paths/user.js
CHANGED
|
@@ -123,7 +123,7 @@ export const paths = {
|
|
|
123
123
|
properties: {
|
|
124
124
|
id: { $ref: '#/components/schemas/Organization/properties/id' },
|
|
125
125
|
name: { $ref: '#/components/schemas/Organization/properties/name' },
|
|
126
|
-
role: { $ref: '#/components/schemas/
|
|
126
|
+
role: { $ref: '#/components/schemas/OrganizationMember/properties/role' },
|
|
127
127
|
},
|
|
128
128
|
},
|
|
129
129
|
},
|
|
@@ -354,6 +354,18 @@ export const paths = {
|
|
|
354
354
|
},
|
|
355
355
|
'/api/user/apps/{appId}/accounts': {
|
|
356
356
|
parameters: [{ $ref: '#/components/parameters/appId' }],
|
|
357
|
+
get: {
|
|
358
|
+
tags: ['appMember'],
|
|
359
|
+
description: 'Fetch app accounts by roles.',
|
|
360
|
+
operationId: 'getAppMembersByRoles',
|
|
361
|
+
parameters: [{ $ref: '#/components/parameters/roles' }],
|
|
362
|
+
security: [{ app: [] }],
|
|
363
|
+
responses: {
|
|
364
|
+
200: {
|
|
365
|
+
description: 'The accounts that were fetched.',
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
},
|
|
357
369
|
post: {
|
|
358
370
|
tags: ['appMember'],
|
|
359
371
|
description: 'Create a new app account using an email address and a password.',
|
|
@@ -401,6 +413,75 @@ export const paths = {
|
|
|
401
413
|
},
|
|
402
414
|
},
|
|
403
415
|
},
|
|
416
|
+
'/api/user/apps/{appId}/accounts/{memberEmail}': {
|
|
417
|
+
parameters: [
|
|
418
|
+
{ $ref: '#/components/parameters/appId' },
|
|
419
|
+
{ $ref: '#/components/parameters/memberEmail' },
|
|
420
|
+
],
|
|
421
|
+
patch: {
|
|
422
|
+
tags: ['appMember'],
|
|
423
|
+
description: 'Patch an app account by email.',
|
|
424
|
+
operationId: 'updateAppMemberByEmail',
|
|
425
|
+
security: [{ app: [] }],
|
|
426
|
+
requestBody: {
|
|
427
|
+
description: 'The user account to update.',
|
|
428
|
+
required: true,
|
|
429
|
+
content: {
|
|
430
|
+
'multipart/form-data': {
|
|
431
|
+
schema: {
|
|
432
|
+
type: 'object',
|
|
433
|
+
required: ['password'],
|
|
434
|
+
properties: {
|
|
435
|
+
name: {
|
|
436
|
+
type: 'string',
|
|
437
|
+
},
|
|
438
|
+
email: {
|
|
439
|
+
type: 'string',
|
|
440
|
+
format: 'email',
|
|
441
|
+
},
|
|
442
|
+
password: {
|
|
443
|
+
type: 'string',
|
|
444
|
+
minLength: 8,
|
|
445
|
+
},
|
|
446
|
+
properties: {
|
|
447
|
+
type: 'object',
|
|
448
|
+
additionalProperties: { type: 'string' },
|
|
449
|
+
description: 'The member’s custom properties.',
|
|
450
|
+
},
|
|
451
|
+
role: {
|
|
452
|
+
type: 'string',
|
|
453
|
+
description: "The role for the updated account. Defaults to the default role in the app's security definition.",
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
responses: {
|
|
461
|
+
200: {
|
|
462
|
+
description: 'A linked app account',
|
|
463
|
+
content: {
|
|
464
|
+
'application/json': {
|
|
465
|
+
schema: {
|
|
466
|
+
$ref: '#/components/schemas/AppAccount',
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
delete: {
|
|
474
|
+
tags: ['appMember'],
|
|
475
|
+
description: 'Delete a user account by email.',
|
|
476
|
+
operationId: 'deleteAppMemberByEmail',
|
|
477
|
+
security: [{ app: [] }],
|
|
478
|
+
responses: {
|
|
479
|
+
204: {
|
|
480
|
+
description: 'The account was deleted successfully.',
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
},
|
|
404
485
|
'/api/user/apps/{appId}/account/verify': {
|
|
405
486
|
parameters: [{ $ref: '#/components/parameters/appId' }],
|
|
406
487
|
post: {
|
|
@@ -94,5 +94,21 @@ export declare enum Permission {
|
|
|
94
94
|
/**
|
|
95
95
|
* The permission to edit collections.
|
|
96
96
|
*/
|
|
97
|
-
EditCollections = 22
|
|
97
|
+
EditCollections = 22,
|
|
98
|
+
/**
|
|
99
|
+
* The permission to create app accounts.
|
|
100
|
+
*/
|
|
101
|
+
CreateAppAccounts = 23,
|
|
102
|
+
/**
|
|
103
|
+
* The permission to read app accounts.
|
|
104
|
+
*/
|
|
105
|
+
ReadAppAccounts = 24,
|
|
106
|
+
/**
|
|
107
|
+
* The permission to delete app accounts.
|
|
108
|
+
*/
|
|
109
|
+
DeleteAppAccounts = 25,
|
|
110
|
+
/**
|
|
111
|
+
* The permission to edit app accounts.
|
|
112
|
+
*/
|
|
113
|
+
EditAppAccounts = 26
|
|
98
114
|
}
|
package/constants/Permission.js
CHANGED
|
@@ -96,5 +96,21 @@ export var Permission;
|
|
|
96
96
|
* The permission to edit collections.
|
|
97
97
|
*/
|
|
98
98
|
Permission[Permission["EditCollections"] = 22] = "EditCollections";
|
|
99
|
+
/**
|
|
100
|
+
* The permission to create app accounts.
|
|
101
|
+
*/
|
|
102
|
+
Permission[Permission["CreateAppAccounts"] = 23] = "CreateAppAccounts";
|
|
103
|
+
/**
|
|
104
|
+
* The permission to read app accounts.
|
|
105
|
+
*/
|
|
106
|
+
Permission[Permission["ReadAppAccounts"] = 24] = "ReadAppAccounts";
|
|
107
|
+
/**
|
|
108
|
+
* The permission to delete app accounts.
|
|
109
|
+
*/
|
|
110
|
+
Permission[Permission["DeleteAppAccounts"] = 25] = "DeleteAppAccounts";
|
|
111
|
+
/**
|
|
112
|
+
* The permission to edit app accounts.
|
|
113
|
+
*/
|
|
114
|
+
Permission[Permission["EditAppAccounts"] = 26] = "EditAppAccounts";
|
|
99
115
|
})(Permission || (Permission = {}));
|
|
100
116
|
//# sourceMappingURL=Permission.js.map
|
package/constants/roles.d.ts
CHANGED
package/constants/roles.js
CHANGED
|
@@ -3,6 +3,12 @@ const member = [Permission.ViewApps, Permission.ViewMembers];
|
|
|
3
3
|
const Translator = [...member, Permission.EditAppMessages];
|
|
4
4
|
const APIReader = [...member, Permission.ReadAssets, Permission.ReadResources];
|
|
5
5
|
const APIUser = [...APIReader, Permission.ManageAssets, Permission.ManageResources];
|
|
6
|
+
const AccountManager = [
|
|
7
|
+
Permission.CreateAppAccounts,
|
|
8
|
+
Permission.ReadAppAccounts,
|
|
9
|
+
Permission.DeleteAppAccounts,
|
|
10
|
+
Permission.EditAppAccounts,
|
|
11
|
+
];
|
|
6
12
|
const AppEditor = [
|
|
7
13
|
...member,
|
|
8
14
|
Permission.EditApps,
|
|
@@ -15,6 +21,7 @@ const AppEditor = [
|
|
|
15
21
|
];
|
|
16
22
|
const Maintainer = [
|
|
17
23
|
...AppEditor,
|
|
24
|
+
...AccountManager,
|
|
18
25
|
Permission.CreateApps,
|
|
19
26
|
Permission.DeleteApps,
|
|
20
27
|
Permission.EditAppSettings,
|
|
@@ -41,6 +48,7 @@ export const roles = {
|
|
|
41
48
|
AppEditor,
|
|
42
49
|
Maintainer,
|
|
43
50
|
Owner,
|
|
51
|
+
AccountManager,
|
|
44
52
|
};
|
|
45
53
|
export var TeamRole;
|
|
46
54
|
(function (TeamRole) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.1",
|
|
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.
|
|
40
|
+
"@appsemble/types": "0.24.1",
|
|
41
41
|
"axios": "^1.0.0",
|
|
42
42
|
"cron-parser": "^4.0.0",
|
|
43
43
|
"date-fns": "^2.0.0",
|
package/validation.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AppDefinition, type BlockManifest, type ProjectImplementations } from '@appsemble/types';
|
|
1
|
+
import { type AppDefinition, type BlockManifest, type ProjectImplementations, type Remapper } from '@appsemble/types';
|
|
2
2
|
import { type ValidatorResult } from 'jsonschema';
|
|
3
3
|
import { type Promisable } from 'type-fest';
|
|
4
4
|
import { type IdentifiableBlock } from './blockUtils.js';
|
|
@@ -8,7 +8,7 @@ import { type IdentifiableBlock } from './blockUtils.js';
|
|
|
8
8
|
* @param link The link to check
|
|
9
9
|
* @returns Whether or not the given link represents a link related to the Appsemble core.
|
|
10
10
|
*/
|
|
11
|
-
export declare function isAppLink(link: string[] | string): boolean;
|
|
11
|
+
export declare function isAppLink(link: Remapper | string[] | string): boolean;
|
|
12
12
|
export type BlockVersionsGetter = (blockMap: IdentifiableBlock[]) => Promisable<BlockManifest[]>;
|
|
13
13
|
/**
|
|
14
14
|
* Validate an app definition.
|
package/validation.js
CHANGED
|
@@ -34,6 +34,37 @@ function validateJSONSchema(schema, prefix, report) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
function validateUsersSchema(definition, report) {
|
|
38
|
+
var _a;
|
|
39
|
+
if (!definition.users) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const [propertyName, propertyDefinition] of Object.entries(definition.users.properties)) {
|
|
43
|
+
// Handled by schema validation
|
|
44
|
+
if (!(propertyDefinition === null || propertyDefinition === void 0 ? void 0 : propertyDefinition.schema)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const { schema } = propertyDefinition;
|
|
48
|
+
const prefix = ['users', 'properties', propertyName, 'schema'];
|
|
49
|
+
validateJSONSchema(schema, prefix, report);
|
|
50
|
+
if (!('type' in schema) && !('enum' in schema)) {
|
|
51
|
+
report(schema, 'must define type or enum', prefix);
|
|
52
|
+
}
|
|
53
|
+
if ('reference' in propertyDefinition) {
|
|
54
|
+
const { resource: resourceName } = propertyDefinition.reference;
|
|
55
|
+
const resourceDefinition = (_a = definition.resources) === null || _a === void 0 ? void 0 : _a[resourceName];
|
|
56
|
+
if (!resourceDefinition) {
|
|
57
|
+
report(resourceName, 'refers to a resource that doesn’t exist', [
|
|
58
|
+
'users',
|
|
59
|
+
'properties',
|
|
60
|
+
propertyName,
|
|
61
|
+
'reference',
|
|
62
|
+
resourceName,
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
37
68
|
function validateResourceSchemas(definition, report) {
|
|
38
69
|
if (!definition.resources) {
|
|
39
70
|
return;
|
|
@@ -447,7 +478,7 @@ function validateActions(definition, report) {
|
|
|
447
478
|
const urlRegex = new RegExp(`^${partialNormalized.source}:`);
|
|
448
479
|
iterApp(definition, {
|
|
449
480
|
onAction(action, path) {
|
|
450
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
481
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
451
482
|
if (path[0] === 'cron' && !serverActions.has(action.type)) {
|
|
452
483
|
report(action.type, 'action type is not supported for cron jobs', [...path, 'type']);
|
|
453
484
|
return;
|
|
@@ -456,17 +487,29 @@ function validateActions(definition, report) {
|
|
|
456
487
|
report(action.type, 'refers to a user action but the app doesn’t have a security definition', [...path, 'type']);
|
|
457
488
|
return;
|
|
458
489
|
}
|
|
490
|
+
if (['user.register', 'user.create', 'user.update'].includes(action.type) &&
|
|
491
|
+
Object.values(action.properties)[0] &&
|
|
492
|
+
((_a = definition.users) === null || _a === void 0 ? void 0 : _a.properties)) {
|
|
493
|
+
for (const propertyName of Object.keys(Object.values(action.properties)[0])) {
|
|
494
|
+
if (!((_b = definition.users) === null || _b === void 0 ? void 0 : _b.properties[propertyName])) {
|
|
495
|
+
report(action.type, 'contains a property that doesn’t exist in users.properties', [
|
|
496
|
+
...path,
|
|
497
|
+
'properties',
|
|
498
|
+
]);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
459
502
|
if (action.type.startsWith('resource.')) {
|
|
460
503
|
// All of the actions starting with `resource.` contain a property called `resource`.
|
|
461
504
|
const { resource: resourceName, view } = action;
|
|
462
|
-
const resource = (
|
|
505
|
+
const resource = (_c = definition.resources) === null || _c === void 0 ? void 0 : _c[resourceName];
|
|
463
506
|
if (!resource) {
|
|
464
507
|
report(action.type, 'refers to a resource that doesn’t exist', [...path, 'resource']);
|
|
465
508
|
return;
|
|
466
509
|
}
|
|
467
510
|
if (!action.type.startsWith('resource.subscription.')) {
|
|
468
511
|
const type = action.type.split('.')[1];
|
|
469
|
-
const roles = (
|
|
512
|
+
const roles = (_e = (_d = resource === null || resource === void 0 ? void 0 : resource[type]) === null || _d === void 0 ? void 0 : _d.roles) !== null && _e !== void 0 ? _e : resource === null || resource === void 0 ? void 0 : resource.roles;
|
|
470
513
|
if (!roles) {
|
|
471
514
|
report(action.type, 'refers to a resource action that is currently set to private', [
|
|
472
515
|
...path,
|
|
@@ -479,11 +522,11 @@ function validateActions(definition, report) {
|
|
|
479
522
|
return;
|
|
480
523
|
}
|
|
481
524
|
if ((type === 'get' || type === 'query') && view) {
|
|
482
|
-
if (!((
|
|
525
|
+
if (!((_f = resource.views) === null || _f === void 0 ? void 0 : _f[view])) {
|
|
483
526
|
report(action.type, 'refers to a view that doesn’t exist', [...path, 'view']);
|
|
484
527
|
return;
|
|
485
528
|
}
|
|
486
|
-
const viewRoles = (
|
|
529
|
+
const viewRoles = (_g = resource === null || resource === void 0 ? void 0 : resource.views) === null || _g === void 0 ? void 0 : _g[view].roles;
|
|
487
530
|
if (!(viewRoles === null || viewRoles === void 0 ? void 0 : viewRoles.length)) {
|
|
488
531
|
report(action.type, 'refers to a resource view that is currently set to private', [
|
|
489
532
|
...path,
|
|
@@ -499,19 +542,19 @@ function validateActions(definition, report) {
|
|
|
499
542
|
}
|
|
500
543
|
}
|
|
501
544
|
if (action.type.startsWith('flow.')) {
|
|
502
|
-
const page = (
|
|
545
|
+
const page = (_h = definition.pages) === null || _h === void 0 ? void 0 : _h[Number(path[1])];
|
|
503
546
|
if (page.type !== 'flow' && page.type !== 'loop') {
|
|
504
547
|
report(action.type, 'flow actions can only be used on pages with the type ‘flow’ or ‘loop’', [...path, 'type']);
|
|
505
548
|
return;
|
|
506
549
|
}
|
|
507
|
-
if (action.type === 'flow.cancel' && !((
|
|
550
|
+
if (action.type === 'flow.cancel' && !((_j = page.actions) === null || _j === void 0 ? void 0 : _j.onFlowCancel)) {
|
|
508
551
|
report(action.type, 'was defined but ‘onFlowCancel’ page action wasn’t defined', [
|
|
509
552
|
...path,
|
|
510
553
|
'type',
|
|
511
554
|
]);
|
|
512
555
|
return;
|
|
513
556
|
}
|
|
514
|
-
if (action.type === 'flow.finish' && !((
|
|
557
|
+
if (action.type === 'flow.finish' && !((_k = page.actions) === null || _k === void 0 ? void 0 : _k.onFlowFinish)) {
|
|
515
558
|
report(action.type, 'was defined but ‘onFlowFinish’ page action wasn’t defined', [
|
|
516
559
|
...path,
|
|
517
560
|
'type',
|
|
@@ -525,7 +568,7 @@ function validateActions(definition, report) {
|
|
|
525
568
|
if (page.type === 'flow' &&
|
|
526
569
|
action.type === 'flow.next' &&
|
|
527
570
|
Number(path[3]) === page.steps.length - 1 &&
|
|
528
|
-
!((
|
|
571
|
+
!((_l = page.actions) === null || _l === void 0 ? void 0 : _l.onFlowFinish)) {
|
|
529
572
|
report(action.type, 'was defined on the last step but ‘onFlowFinish’ page action wasn’t defined', [...path, 'type']);
|
|
530
573
|
return;
|
|
531
574
|
}
|
|
@@ -538,6 +581,11 @@ function validateActions(definition, report) {
|
|
|
538
581
|
}
|
|
539
582
|
if (action.type === 'link') {
|
|
540
583
|
const { to } = action;
|
|
584
|
+
if (typeof to === 'object' &&
|
|
585
|
+
(!Array.isArray(to) ||
|
|
586
|
+
(Array.isArray(to) && to.every((entry) => typeof entry === 'object')))) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
541
589
|
if (typeof to === 'string' && urlRegex.test(to)) {
|
|
542
590
|
return;
|
|
543
591
|
}
|
|
@@ -751,6 +799,7 @@ export async function validateAppDefinition(definition, getBlockVersions, contro
|
|
|
751
799
|
validateHooks(definition, report);
|
|
752
800
|
validateLanguage(definition, report);
|
|
753
801
|
validateResourceReferences(definition, report);
|
|
802
|
+
validateUsersSchema(definition, report);
|
|
754
803
|
validateResourceSchemas(definition, report);
|
|
755
804
|
validateSecurity(definition, report);
|
|
756
805
|
validateBlocks(definition, blockVersionMap, report);
|
package/validation.test.js
CHANGED
|
@@ -935,6 +935,45 @@ describe('validateAppDefinition', () => {
|
|
|
935
935
|
]),
|
|
936
936
|
]);
|
|
937
937
|
});
|
|
938
|
+
it('should validate user properties for type or enum', async () => {
|
|
939
|
+
const app = { ...createTestApp(), users: { properties: { foo: { schema: {} } } } };
|
|
940
|
+
const result = await validateAppDefinition(app, () => []);
|
|
941
|
+
expect(result.valid).toBe(false);
|
|
942
|
+
expect(result.errors).toStrictEqual([
|
|
943
|
+
new ValidationError('must define type or enum', {}, undefined, [
|
|
944
|
+
'users',
|
|
945
|
+
'properties',
|
|
946
|
+
'foo',
|
|
947
|
+
'schema',
|
|
948
|
+
]),
|
|
949
|
+
]);
|
|
950
|
+
});
|
|
951
|
+
it('should validate user properties for resource references', async () => {
|
|
952
|
+
const app = {
|
|
953
|
+
...createTestApp(),
|
|
954
|
+
users: {
|
|
955
|
+
properties: {
|
|
956
|
+
foo: {
|
|
957
|
+
schema: { type: 'integer' },
|
|
958
|
+
reference: {
|
|
959
|
+
resource: 'tasks',
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
};
|
|
965
|
+
const result = await validateAppDefinition(app, () => []);
|
|
966
|
+
expect(result.valid).toBe(false);
|
|
967
|
+
expect(result.errors).toStrictEqual([
|
|
968
|
+
new ValidationError('refers to a resource that doesn’t exist', 'tasks', undefined, [
|
|
969
|
+
'users',
|
|
970
|
+
'properties',
|
|
971
|
+
'foo',
|
|
972
|
+
'reference',
|
|
973
|
+
'tasks',
|
|
974
|
+
]),
|
|
975
|
+
]);
|
|
976
|
+
});
|
|
938
977
|
it('should validate resources use schemas define a type', async () => {
|
|
939
978
|
const app = createTestApp();
|
|
940
979
|
app.resources.person.schema = { properties: {} };
|
|
@@ -1483,7 +1522,7 @@ describe('validateAppDefinition', () => {
|
|
|
1483
1522
|
actions: {
|
|
1484
1523
|
onWhatever: {
|
|
1485
1524
|
type: 'user.update',
|
|
1486
|
-
|
|
1525
|
+
currentEmail: 'example@example.com',
|
|
1487
1526
|
password: 'password',
|
|
1488
1527
|
},
|
|
1489
1528
|
},
|
|
@@ -1689,6 +1728,147 @@ describe('validateAppDefinition', () => {
|
|
|
1689
1728
|
new ValidationError('was defined but ‘onFlowCancel’ page action wasn’t defined', 'flow.cancel', undefined, ['pages', 3, 'steps', 1, 'blocks', 0, 'actions', 'onWhatever', 'type']),
|
|
1690
1729
|
]);
|
|
1691
1730
|
});
|
|
1731
|
+
it('should report an error if a user register action on a block adds unsupported user properties', async () => {
|
|
1732
|
+
const app = {
|
|
1733
|
+
...createTestApp(),
|
|
1734
|
+
users: {
|
|
1735
|
+
properties: {
|
|
1736
|
+
foo: {
|
|
1737
|
+
schema: {
|
|
1738
|
+
type: 'string',
|
|
1739
|
+
},
|
|
1740
|
+
},
|
|
1741
|
+
},
|
|
1742
|
+
},
|
|
1743
|
+
};
|
|
1744
|
+
app.pages[0].blocks.push({
|
|
1745
|
+
type: 'test',
|
|
1746
|
+
version: '1.2.3',
|
|
1747
|
+
actions: {
|
|
1748
|
+
onWhatever: {
|
|
1749
|
+
type: 'user.register',
|
|
1750
|
+
displayName: 'name',
|
|
1751
|
+
email: 'email@example.com',
|
|
1752
|
+
password: 'password',
|
|
1753
|
+
properties: {
|
|
1754
|
+
'object.from': {
|
|
1755
|
+
bar: 'baz',
|
|
1756
|
+
},
|
|
1757
|
+
},
|
|
1758
|
+
},
|
|
1759
|
+
},
|
|
1760
|
+
});
|
|
1761
|
+
const result = await validateAppDefinition(app, () => [
|
|
1762
|
+
{
|
|
1763
|
+
name: '@appsemble/test',
|
|
1764
|
+
version: '1.2.3',
|
|
1765
|
+
files: [],
|
|
1766
|
+
languages: [],
|
|
1767
|
+
actions: {
|
|
1768
|
+
onWhatever: {},
|
|
1769
|
+
},
|
|
1770
|
+
},
|
|
1771
|
+
]);
|
|
1772
|
+
expect(result.valid).toBe(false);
|
|
1773
|
+
expect(result.errors).toStrictEqual([
|
|
1774
|
+
new ValidationError('contains a property that doesn’t exist in users.properties', 'user.register', undefined, ['pages', 0, 'blocks', 0, 'actions', 'onWhatever', 'properties']),
|
|
1775
|
+
]);
|
|
1776
|
+
});
|
|
1777
|
+
it('should report an error if a user create action on a block adds unsupported user properties', async () => {
|
|
1778
|
+
const app = {
|
|
1779
|
+
...createTestApp(),
|
|
1780
|
+
users: {
|
|
1781
|
+
properties: {
|
|
1782
|
+
foo: {
|
|
1783
|
+
schema: {
|
|
1784
|
+
type: 'string',
|
|
1785
|
+
},
|
|
1786
|
+
},
|
|
1787
|
+
},
|
|
1788
|
+
},
|
|
1789
|
+
};
|
|
1790
|
+
app.pages[0].blocks.push({
|
|
1791
|
+
type: 'test',
|
|
1792
|
+
version: '1.2.3',
|
|
1793
|
+
actions: {
|
|
1794
|
+
onWhatever: {
|
|
1795
|
+
type: 'user.create',
|
|
1796
|
+
name: 'name',
|
|
1797
|
+
email: 'email@example.com',
|
|
1798
|
+
password: 'password',
|
|
1799
|
+
role: 'role',
|
|
1800
|
+
properties: {
|
|
1801
|
+
'object.from': {
|
|
1802
|
+
bar: 'baz',
|
|
1803
|
+
},
|
|
1804
|
+
},
|
|
1805
|
+
},
|
|
1806
|
+
},
|
|
1807
|
+
});
|
|
1808
|
+
const result = await validateAppDefinition(app, () => [
|
|
1809
|
+
{
|
|
1810
|
+
name: '@appsemble/test',
|
|
1811
|
+
version: '1.2.3',
|
|
1812
|
+
files: [],
|
|
1813
|
+
languages: [],
|
|
1814
|
+
actions: {
|
|
1815
|
+
onWhatever: {},
|
|
1816
|
+
},
|
|
1817
|
+
},
|
|
1818
|
+
]);
|
|
1819
|
+
expect(result.valid).toBe(false);
|
|
1820
|
+
expect(result.errors).toStrictEqual([
|
|
1821
|
+
new ValidationError('contains a property that doesn’t exist in users.properties', 'user.create', undefined, ['pages', 0, 'blocks', 0, 'actions', 'onWhatever', 'properties']),
|
|
1822
|
+
]);
|
|
1823
|
+
});
|
|
1824
|
+
it('should report an error if a user update action on a block adds unsupported user properties', async () => {
|
|
1825
|
+
const app = {
|
|
1826
|
+
...createTestApp(),
|
|
1827
|
+
users: {
|
|
1828
|
+
properties: {
|
|
1829
|
+
foo: {
|
|
1830
|
+
schema: {
|
|
1831
|
+
type: 'string',
|
|
1832
|
+
},
|
|
1833
|
+
},
|
|
1834
|
+
},
|
|
1835
|
+
},
|
|
1836
|
+
};
|
|
1837
|
+
app.pages[0].blocks.push({
|
|
1838
|
+
type: 'test',
|
|
1839
|
+
version: '1.2.3',
|
|
1840
|
+
actions: {
|
|
1841
|
+
onWhatever: {
|
|
1842
|
+
type: 'user.update',
|
|
1843
|
+
name: 'name',
|
|
1844
|
+
currentEmail: 'email@example.com',
|
|
1845
|
+
newEmail: 'new-email@example.com',
|
|
1846
|
+
password: 'password',
|
|
1847
|
+
role: 'role',
|
|
1848
|
+
properties: {
|
|
1849
|
+
'object.from': {
|
|
1850
|
+
bar: 'baz',
|
|
1851
|
+
},
|
|
1852
|
+
},
|
|
1853
|
+
},
|
|
1854
|
+
},
|
|
1855
|
+
});
|
|
1856
|
+
const result = await validateAppDefinition(app, () => [
|
|
1857
|
+
{
|
|
1858
|
+
name: '@appsemble/test',
|
|
1859
|
+
version: '1.2.3',
|
|
1860
|
+
files: [],
|
|
1861
|
+
languages: [],
|
|
1862
|
+
actions: {
|
|
1863
|
+
onWhatever: {},
|
|
1864
|
+
},
|
|
1865
|
+
},
|
|
1866
|
+
]);
|
|
1867
|
+
expect(result.valid).toBe(false);
|
|
1868
|
+
expect(result.errors).toStrictEqual([
|
|
1869
|
+
new ValidationError('contains a property that doesn’t exist in users.properties', 'user.update', undefined, ['pages', 0, 'blocks', 0, 'actions', 'onWhatever', 'properties']),
|
|
1870
|
+
]);
|
|
1871
|
+
});
|
|
1692
1872
|
it('should report an error if a resource action on a block refers to a non-existent resource', async () => {
|
|
1693
1873
|
const app = createTestApp();
|
|
1694
1874
|
app.pages[0].blocks.push({
|