@appsemble/utils 0.22.10 → 0.23.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.
Files changed (47) hide show
  1. package/README.md +3 -3
  2. package/api/components/parameters/index.d.ts +2 -0
  3. package/api/components/parameters/index.js +2 -0
  4. package/api/components/parameters/trainingBlockId.d.ts +2 -0
  5. package/api/components/parameters/trainingBlockId.js +8 -0
  6. package/api/components/parameters/trainingId.d.ts +2 -0
  7. package/api/components/parameters/trainingId.js +8 -0
  8. package/api/components/schemas/App.js +4 -0
  9. package/api/components/schemas/AppServiceSecret.js +1 -1
  10. package/api/components/schemas/ResourceDefinition.js +24 -4
  11. package/api/components/schemas/Training.d.ts +2 -0
  12. package/api/components/schemas/Training.js +32 -0
  13. package/api/components/schemas/TrainingBlock.d.ts +2 -0
  14. package/api/components/schemas/TrainingBlock.js +38 -0
  15. package/api/components/schemas/index.d.ts +2 -0
  16. package/api/components/schemas/index.js +2 -0
  17. package/api/components/securitySchemes/cli.js +1 -0
  18. package/api/components/securitySchemes/cli.test.js +1 -0
  19. package/api/index.test.js +1 -0
  20. package/api/paths/apps.js +58 -2
  21. package/api/paths/index.js +2 -0
  22. package/api/paths/trainings.d.ts +2 -0
  23. package/api/paths/trainings.js +279 -0
  24. package/appMessages.test.js +1 -0
  25. package/appSecurity.test.js +1 -0
  26. package/blockUtils.test.js +1 -0
  27. package/constants/patterns.test.js +1 -0
  28. package/constants/roles.js +3 -3
  29. package/constants/scopes.d.ts +1 -1
  30. package/constants/scopes.js +1 -0
  31. package/convertToCsv.test.js +1 -0
  32. package/has.test.js +1 -0
  33. package/i18n.test.js +1 -0
  34. package/iterApp.test.js +1 -0
  35. package/jsonschema.test.js +1 -0
  36. package/mapValues.test.js +1 -0
  37. package/miscellaneous.test.js +1 -0
  38. package/normalize.test.js +1 -0
  39. package/objectCache.test.js +1 -0
  40. package/package.json +3 -2
  41. package/prefix.test.js +1 -0
  42. package/reference-schemas/remappers/arrays.js +29 -0
  43. package/remap.test.js +1 -0
  44. package/string.test.js +1 -0
  45. package/theme.test.js +1 -0
  46. package/validateStyle.test.js +1 -0
  47. package/validation.test.js +1 -0
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.22.10/config/assets/logo.svg) Appsemble Utilities
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.23.1/config/assets/logo.svg) Appsemble Utilities
2
2
 
3
3
  > Internal utility functions used across multiple Appsemble projects.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@appsemble/utils)](https://www.npmjs.com/package/@appsemble/utils)
6
- [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.22.10/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.22.10)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.23.1/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.23.1)
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 @@ not guaranteed.
26
26
 
27
27
  ## License
28
28
 
29
- [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.22.10/LICENSE.md) ©
29
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.23.1/LICENSE.md) ©
30
30
  [Appsemble](https://appsemble.com)
@@ -17,3 +17,5 @@ export * from './resourceType.js';
17
17
  export * from './screenshotId.js';
18
18
  export * from './appServiceId.js';
19
19
  export * from './view.js';
20
+ export * from './trainingId.js';
21
+ export * from './trainingBlockId.js';
@@ -17,4 +17,6 @@ export * from './resourceType.js';
17
17
  export * from './screenshotId.js';
18
18
  export * from './appServiceId.js';
19
19
  export * from './view.js';
20
+ export * from './trainingId.js';
21
+ export * from './trainingBlockId.js';
20
22
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const trainingBlockId: OpenAPIV3.ParameterObject;
@@ -0,0 +1,8 @@
1
+ export const trainingBlockId = {
2
+ name: 'trainingBlockId',
3
+ in: 'path',
4
+ description: 'Id of the trainingBlock on which the operation will be performed.',
5
+ required: true,
6
+ schema: { $ref: '#/components/schemas/TrainingBlock/properties/id' },
7
+ };
8
+ //# sourceMappingURL=trainingBlockId.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const trainingId: OpenAPIV3.ParameterObject;
@@ -0,0 +1,8 @@
1
+ export const trainingId = {
2
+ name: 'trainingId',
3
+ in: 'path',
4
+ description: 'Id of the training on which the operation will be performed.',
5
+ required: true,
6
+ schema: { $ref: '#/components/schemas/Training/properties/id' },
7
+ };
8
+ //# sourceMappingURL=trainingId.js.map
@@ -66,6 +66,10 @@ This must be set to \`false\` before any other changes can be made to an app.
66
66
  type: 'boolean',
67
67
  description: 'Determines whether this app should be included when fetching for templates.',
68
68
  },
69
+ demoMode: {
70
+ type: 'boolean',
71
+ description: 'Determines whether this app should be used in demo mode.',
72
+ },
69
73
  longDescription: {
70
74
  type: 'string',
71
75
  description: `
@@ -9,7 +9,7 @@ export const AppServiceSecret = {
9
9
  description: 'An autogenerated ID.',
10
10
  readOnly: true,
11
11
  },
12
- serviceName: {
12
+ name: {
13
13
  type: 'string',
14
14
  description: 'An optional name to give extra clarity what the secret is used for.',
15
15
  },
@@ -13,15 +13,35 @@ const query = {
13
13
  description: 'The query parameters to use in the request.',
14
14
  additionalProperties: { type: 'string' },
15
15
  };
16
+ const referenceActionTrigger = {
17
+ type: 'object',
18
+ additionalProperties: false,
19
+ description: 'Defines the type of trigger and the cascading strategy for it',
20
+ properties: {
21
+ type: {
22
+ enum: ['create', 'update', 'delete'],
23
+ },
24
+ cascade: {
25
+ description: `Defines the cascading strategy.
26
+
27
+ If 'update' is specified, the referencing property of the referencing resource is set to null.
28
+
29
+ If 'delete' is specified, the referencing resource is deleted.
30
+
31
+ If not specified, the referenced resource cannot be deleted
32
+ without deleting the referencing resource first.`,
33
+ enum: ['update', 'delete'],
34
+ },
35
+ },
36
+ };
16
37
  const referenceAction = {
17
38
  type: 'object',
18
39
  additionalProperties: false,
19
- // XXX
20
- description: 'To be documented.',
40
+ description: 'Defines what happens when the specified action is executed on the referenced resource.',
21
41
  properties: {
22
- trigger: {
42
+ triggers: {
23
43
  type: 'array',
24
- items: { enum: ['create', 'update', 'delete'] },
44
+ items: referenceActionTrigger,
25
45
  minItems: 1,
26
46
  uniqueItems: true,
27
47
  },
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const Training: OpenAPIV3.NonArraySchemaObject;
@@ -0,0 +1,32 @@
1
+ export const Training = {
2
+ type: 'object',
3
+ description: 'Object representation of a training',
4
+ additionalProperties: false,
5
+ properties: {
6
+ id: {
7
+ type: 'number',
8
+ readOnly: true,
9
+ minimum: 0,
10
+ description: 'The id of the training, will be generated automatically by the system.',
11
+ },
12
+ title: {
13
+ type: 'string',
14
+ description: 'Title of the training.',
15
+ },
16
+ description: {
17
+ type: 'string',
18
+ description: 'A brief overview of the training.',
19
+ },
20
+ competence: {
21
+ type: 'string',
22
+ description: 'Tag for the training',
23
+ },
24
+ difficultyLevel: {
25
+ type: 'number',
26
+ description: 'Difficulty level between 1 and 5',
27
+ minimum: 1,
28
+ maximum: 5,
29
+ },
30
+ },
31
+ };
32
+ //# sourceMappingURL=Training.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const TrainingBlock: OpenAPIV3.NonArraySchemaObject;
@@ -0,0 +1,38 @@
1
+ export const TrainingBlock = {
2
+ type: 'object',
3
+ description: '',
4
+ additionalProperties: false,
5
+ properties: {
6
+ id: {
7
+ type: 'string',
8
+ description: 'The id of the Training Block, will be generated automatically by the system.',
9
+ readOnly: true,
10
+ },
11
+ trainingId: {
12
+ type: 'number',
13
+ minimum: 0,
14
+ description: 'Id of the parent training.',
15
+ },
16
+ title: {
17
+ type: 'string',
18
+ description: 'Title of the training block.',
19
+ },
20
+ documentationLink: {
21
+ type: 'string',
22
+ description: 'A URL pointing to a page in the documentation.',
23
+ },
24
+ videoLink: {
25
+ type: 'string',
26
+ description: 'Link of the video associated with the training block.',
27
+ },
28
+ exampleCode: {
29
+ type: 'string',
30
+ description: 'Example code for the user to be copied.',
31
+ },
32
+ externalResource: {
33
+ type: 'string',
34
+ description: 'Link to any external resource.',
35
+ },
36
+ },
37
+ };
38
+ //# sourceMappingURL=TrainingBlock.js.map
@@ -113,6 +113,8 @@ export * from './TeamsDefinition.js';
113
113
  export * from './Theme.js';
114
114
  export * from './ThrowActionDefinition.js';
115
115
  export * from './User.js';
116
+ export * from './Training.js';
117
+ export * from './TrainingBlock.js';
116
118
  export * from './UserEmail.js';
117
119
  export * from './UserLogoutActionDefinition.js';
118
120
  export * from './UserLoginActionDefinition.js';
@@ -113,6 +113,8 @@ export * from './TeamsDefinition.js';
113
113
  export * from './Theme.js';
114
114
  export * from './ThrowActionDefinition.js';
115
115
  export * from './User.js';
116
+ export * from './Training.js';
117
+ export * from './TrainingBlock.js';
116
118
  export * from './UserEmail.js';
117
119
  export * from './UserLogoutActionDefinition.js';
118
120
  export * from './UserLoginActionDefinition.js';
@@ -10,6 +10,7 @@ export const cli = {
10
10
  tokenUrl: '/oauth2/token',
11
11
  scopes: {
12
12
  'apps:write': 'Create and update apps',
13
+ 'apps:delete': 'Delete apps',
13
14
  'blocks:write': 'Register and update blocks, and publish new block versions.',
14
15
  'blocks:delete': 'Delete specific block versions.',
15
16
  'organizations:write': 'Create and manage organizations.',
@@ -1,3 +1,4 @@
1
+ import { expect, it } from 'vitest';
1
2
  import { cli } from './cli.js';
2
3
  import { scopes } from '../../../constants/index.js';
3
4
  it('should match the known scopes defined in utils', () => {
package/api/index.test.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readdirSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { createValidator } from 'koas-core/lib/validation.js';
4
+ import { describe, expect, it } from 'vitest';
4
5
  import { parse } from 'yaml';
5
6
  import { api, schemas } from './index.js';
6
7
  describe('schemas', () => {
package/api/paths/apps.js CHANGED
@@ -32,6 +32,9 @@ export const paths = {
32
32
  template: {
33
33
  $ref: '#/components/schemas/App/properties/template',
34
34
  },
35
+ demoMode: {
36
+ $ref: '#/components/schemas/App/properties/demoMode',
37
+ },
35
38
  longDescription: {
36
39
  $ref: '#/components/schemas/App/properties/longDescription',
37
40
  },
@@ -170,6 +173,9 @@ export const paths = {
170
173
  template: {
171
174
  $ref: '#/components/schemas/App/properties/template',
172
175
  },
176
+ demoMode: {
177
+ $ref: '#/components/schemas/App/properties/demoMode',
178
+ },
173
179
  longDescription: {
174
180
  $ref: '#/components/schemas/App/properties/longDescription',
175
181
  },
@@ -272,6 +278,53 @@ export const paths = {
272
278
  description: 'The app was successfully deleted.',
273
279
  },
274
280
  },
281
+ security: [{ studio: [] }, { cli: ['apps:delete'] }],
282
+ },
283
+ },
284
+ '/api/apps/import/organization/{organizationId}': {
285
+ parameters: [{ $ref: '#/components/parameters/organizationId' }],
286
+ post: {
287
+ tags: ['app', 'import', 'zip'],
288
+ description: 'Import an app from a zip file',
289
+ operationId: 'importApp',
290
+ requestBody: {
291
+ content: {
292
+ 'application/zip': {
293
+ schema: {
294
+ type: 'string',
295
+ format: 'binary',
296
+ },
297
+ },
298
+ },
299
+ },
300
+ responses: {
301
+ 200: {
302
+ description: 'App imported successfully',
303
+ $ref: '#/components/responses/app',
304
+ },
305
+ },
306
+ security: [{ studio: [] }],
307
+ },
308
+ },
309
+ '/api/apps/{appId}/export': {
310
+ parameters: [{ $ref: '#/components/parameters/appId' }],
311
+ get: {
312
+ tags: ['app', 'export', 'zip'],
313
+ description: 'Export the app',
314
+ operationId: 'exportApp',
315
+ parameters: [
316
+ {
317
+ name: 'resources',
318
+ schema: { type: 'boolean' },
319
+ description: 'Whether to include resources for an app.',
320
+ in: 'query',
321
+ },
322
+ ],
323
+ responses: {
324
+ 200: {
325
+ description: 'App exported successfully.',
326
+ },
327
+ },
275
328
  security: [{ studio: [] }],
276
329
  },
277
330
  },
@@ -301,6 +354,9 @@ export const paths = {
301
354
  responses: {
302
355
  204: {
303
356
  description: 'Lock status successfully changed',
357
+ content: {
358
+ 'application/zip': {},
359
+ },
304
360
  },
305
361
  },
306
362
  security: [{ studio: [] }, { cli: ['apps:write'] }],
@@ -1272,7 +1328,7 @@ This will return a 404 if the user has not uploaded one.`,
1272
1328
  },
1273
1329
  },
1274
1330
  },
1275
- security: [{ studio: [] }, { cli: ['teams:write'] }],
1331
+ security: [{ studio: [] }, { app: [] }, { cli: ['teams:write'] }],
1276
1332
  },
1277
1333
  delete: {
1278
1334
  tags: ['app'],
@@ -1283,7 +1339,7 @@ This will return a 404 if the user has not uploaded one.`,
1283
1339
  description: 'The team member has been removed successfully.',
1284
1340
  },
1285
1341
  },
1286
- security: [{ studio: [] }, { cli: ['teams:write'] }],
1342
+ security: [{ studio: [] }, { app: [] }, { cli: ['teams:write'] }],
1287
1343
  },
1288
1344
  },
1289
1345
  '/api/apps/{appId}/teams/{teamId}/invite': {
@@ -23,6 +23,7 @@ import { paths as resourceHistory } from './resourceHistory.js';
23
23
  import { paths as resources } from './resources.js';
24
24
  import { paths as saml } from './saml.js';
25
25
  import { paths as templates } from './templates.js';
26
+ import { paths as trainings } from './trainings.js';
26
27
  import { paths as user } from './user.js';
27
28
  export const paths = {
28
29
  ...appOAuth2Secrets,
@@ -51,5 +52,6 @@ export const paths = {
51
52
  ...translations,
52
53
  ...appServiceSecrets,
53
54
  ...user,
55
+ ...trainings,
54
56
  };
55
57
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const paths: OpenAPIV3.PathsObject;
@@ -0,0 +1,279 @@
1
+ export const paths = {
2
+ '/api/trainings': {
3
+ get: {
4
+ tags: ['training', 'learning', 'docs'],
5
+ description: 'Fetch all trainings available',
6
+ operationId: 'getTrainings',
7
+ responses: {
8
+ 200: {
9
+ description: 'An array of all the available trainings.',
10
+ content: {
11
+ 'application/json': {
12
+ schema: {
13
+ type: 'array',
14
+ items: { $ref: '#/components/schemas/Training' },
15
+ },
16
+ },
17
+ },
18
+ },
19
+ },
20
+ security: [{ studio: [] }],
21
+ },
22
+ post: {
23
+ tags: ['training', 'learning', 'docs'],
24
+ description: '',
25
+ operationId: 'createTraining',
26
+ requestBody: {
27
+ required: true,
28
+ content: {
29
+ 'application/json': {
30
+ schema: {
31
+ $ref: '#/components/schemas/Training',
32
+ },
33
+ },
34
+ },
35
+ },
36
+ responses: {
37
+ 201: {
38
+ description: 'Created new training successfully',
39
+ $ref: '#/components/schemas/Training',
40
+ },
41
+ },
42
+ security: [{ studio: [] }],
43
+ },
44
+ },
45
+ '/api/trainings/{trainingId}': {
46
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
47
+ get: {
48
+ tags: ['training', 'learning', 'docs'],
49
+ description: 'Fetch a single training by id.',
50
+ operationId: 'getTrainingById',
51
+ responses: {
52
+ 200: {
53
+ description: 'Object representation of a training',
54
+ content: {
55
+ 'application/json': {
56
+ schema: {
57
+ $ref: '#/components/schemas/Training',
58
+ },
59
+ },
60
+ },
61
+ },
62
+ },
63
+ security: [{ studio: [] }],
64
+ },
65
+ patch: {
66
+ tags: ['training', 'learning', 'docs'],
67
+ description: 'Fetch a single training by id.',
68
+ operationId: 'patchTraining',
69
+ requestBody: {
70
+ required: true,
71
+ content: {
72
+ 'application/json': {
73
+ schema: {
74
+ $ref: '#/components/schemas/Training',
75
+ },
76
+ },
77
+ },
78
+ },
79
+ responses: {
80
+ 200: {
81
+ description: 'Object representation of a training',
82
+ content: {
83
+ 'application/json': {
84
+ schema: {
85
+ $ref: '#/components/schemas/Training',
86
+ },
87
+ },
88
+ },
89
+ },
90
+ },
91
+ security: [{ studio: [] }],
92
+ },
93
+ delete: {
94
+ tags: ['delete', 'training'],
95
+ description: 'Delete a training by Id.',
96
+ operationId: 'deleteTraining',
97
+ responses: {
98
+ 204: { description: 'Deleted the specified training.' },
99
+ },
100
+ security: [{ studio: [] }],
101
+ },
102
+ },
103
+ '/api/trainings/{trainingId}/blocks': {
104
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
105
+ get: {
106
+ tags: ['training', 'trainingBlocks', 'learning', 'docs'],
107
+ description: 'Fetch all training blocks by training id',
108
+ operationId: 'getTrainingBlocksByTrainingId',
109
+ responses: {
110
+ 200: {
111
+ description: 'An array of all the training Blocks associated with a training.',
112
+ content: {
113
+ 'application/json': {
114
+ schema: {
115
+ type: 'array',
116
+ items: { $ref: '#/components/schemas/TrainingBlock' },
117
+ },
118
+ },
119
+ },
120
+ },
121
+ },
122
+ security: [{ studio: [] }],
123
+ },
124
+ post: {
125
+ tags: ['training', 'trainingBlocks', 'learning', 'docs'],
126
+ description: 'Create a new training block as a child of a training.',
127
+ operationId: 'createTrainingBlock',
128
+ requestBody: {
129
+ required: true,
130
+ content: {
131
+ 'multipart/form-data': {
132
+ schema: {
133
+ type: 'object',
134
+ properties: {
135
+ documentationLink: {
136
+ $ref: '#/components/schemas/TrainingBlock/properties/documentationLink',
137
+ },
138
+ videoLink: { $ref: '#/components/schemas/TrainingBlock/properties/videoLink' },
139
+ exampleCode: { $ref: '#/components/schemas/TrainingBlock/properties/exampleCode' },
140
+ title: { $ref: '#/components/schemas/TrainingBlock/properties/title' },
141
+ externalResource: {
142
+ $ref: '#/components/schemas/TrainingBlock/properties/externalResource',
143
+ },
144
+ },
145
+ },
146
+ },
147
+ },
148
+ },
149
+ responses: { 201: { description: 'Created new training block successfully' } },
150
+ security: [{ studio: [] }],
151
+ },
152
+ },
153
+ '/api/trainings/{trainingId}/enroll/users': {
154
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
155
+ get: {
156
+ tags: ['training', 'users', 'enrolled'],
157
+ description: 'Get a list of all users who have completed a training.',
158
+ operationId: 'getTrainedUsers',
159
+ responses: {
160
+ 200: {
161
+ description: 'A list of all the users who have completed a training.',
162
+ content: {
163
+ 'application/json': {
164
+ schema: {
165
+ type: 'array',
166
+ items: {
167
+ $ref: '#/components/schemas/User',
168
+ },
169
+ },
170
+ },
171
+ },
172
+ },
173
+ },
174
+ security: [{ studio: [] }],
175
+ },
176
+ },
177
+ '/api/trainings/{trainingId}/enroll': {
178
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
179
+ get: {
180
+ tags: ['training', 'learning', 'user'],
181
+ description: 'Check if a user is enrolled in a training.',
182
+ operationId: 'isUserEnrolled',
183
+ responses: {
184
+ 200: {
185
+ description: 'A boolean returning whether a user is enrolled in a training',
186
+ content: {
187
+ 'application/json': {
188
+ schema: {
189
+ type: 'object',
190
+ properties: {
191
+ enrolled: {
192
+ type: 'boolean',
193
+ description: 'If a user is enrolled in a training',
194
+ },
195
+ completed: {
196
+ type: 'boolean',
197
+ description: 'If the training has been completed by the user.',
198
+ },
199
+ },
200
+ },
201
+ },
202
+ },
203
+ },
204
+ },
205
+ security: [{ studio: [] }],
206
+ },
207
+ post: {
208
+ tags: ['training', 'learning', 'user'],
209
+ description: 'Enroll a user in a training.',
210
+ operationId: 'enrollUserInTraining',
211
+ responses: { 201: { description: 'Enrolled in the training successfully' } },
212
+ security: [{ studio: [] }],
213
+ },
214
+ patch: {
215
+ tags: ['training', 'learning', 'user'],
216
+ description: 'Edit an enrollment of a user in a training',
217
+ operationId: 'updateTrainingCompletionStatus',
218
+ requestBody: {
219
+ required: true,
220
+ content: {
221
+ 'multipart/form-data': {
222
+ schema: {
223
+ type: 'object',
224
+ properties: {
225
+ completed: {
226
+ type: 'boolean',
227
+ description: 'If the training has been completed by the user.',
228
+ },
229
+ },
230
+ },
231
+ },
232
+ },
233
+ },
234
+ responses: { 200: { description: 'Updated user training successfully.' } },
235
+ security: [{ studio: [] }],
236
+ },
237
+ },
238
+ '/api/training/blocks/{trainingBlockId}': {
239
+ parameters: [{ $ref: '#/components/parameters/trainingBlockId' }],
240
+ patch: {
241
+ tags: ['training', 'trainingBlocks', 'learning', 'docs'],
242
+ description: 'Update a training block with new content',
243
+ operationId: 'patchTrainingBlock',
244
+ requestBody: {
245
+ required: true,
246
+ content: {
247
+ 'multipart/form-data': {
248
+ schema: {
249
+ type: 'object',
250
+ properties: {
251
+ documentationLink: {
252
+ $ref: '#/components/schemas/TrainingBlock/properties/documentationLink',
253
+ },
254
+ videoLink: { $ref: '#/components/schemas/TrainingBlock/properties/videoLink' },
255
+ exampleCode: { $ref: '#/components/schemas/TrainingBlock/properties/exampleCode' },
256
+ title: { $ref: '#/components/schemas/TrainingBlock/properties/title' },
257
+ externalResource: {
258
+ $ref: '#/components/schemas/TrainingBlock/properties/externalResource',
259
+ },
260
+ },
261
+ },
262
+ },
263
+ },
264
+ },
265
+ responses: { 200: { $ref: '#/components/schemas/TrainingBlock' } },
266
+ security: [{ studio: [] }],
267
+ },
268
+ delete: {
269
+ tags: ['delete', 'training'],
270
+ description: 'Delete a training block by Id.',
271
+ operationId: 'deleteTrainingBlock',
272
+ responses: {
273
+ 204: { description: 'Deleted the specified training block.' },
274
+ },
275
+ security: [{ studio: [] }],
276
+ },
277
+ },
278
+ };
279
+ //# sourceMappingURL=trainings.js.map
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it, vi } from 'vitest';
1
2
  import { extractAppMessages, findMessageIds } from './appMessages.js';
2
3
  describe('findMessageIds', () => {
3
4
  it('should ignore null', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { resolveRoleInheritance } from './appSecurity.js';
2
3
  describe('resolveRoleInheritance', () => {
3
4
  it('should return an empty array if no security definition is defined', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { getAppBlocks, normalizeBlockName, parseBlockName, prefixBlockURL, stripBlockName, } from './blockUtils.js';
2
3
  describe('normalizeBlockName', () => {
3
4
  it('should prepend @appsemble if no organization is prepended', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { domainPattern, googleAnalyticsIDPattern, normalized, partialNormalized, } from './patterns.js';
2
3
  describe('partialNormalized', () => {
3
4
  it.each([
@@ -22,6 +22,9 @@ const Maintainer = [
22
22
  Permission.ManageTeams,
23
23
  Permission.PublishBlocks,
24
24
  Permission.DeleteBlocks,
25
+ Permission.CreateCollections,
26
+ Permission.DeleteCollections,
27
+ Permission.EditCollections,
25
28
  ];
26
29
  const Owner = [
27
30
  ...Maintainer,
@@ -29,9 +32,6 @@ const Owner = [
29
32
  Permission.DeleteOrganization,
30
33
  Permission.ManageMembers,
31
34
  Permission.ManageRoles,
32
- Permission.CreateCollections,
33
- Permission.DeleteCollections,
34
- Permission.EditCollections,
35
35
  ];
36
36
  export const roles = {
37
37
  Member: member,
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * All known OAuth2 scopes for client credentials.
3
3
  */
4
- export declare const scopes: readonly ["apps:write", "blocks:write", "blocks:delete", "organizations:write", "resources:read", "resources:write", "assets:write", "teams:read", "teams:write"];
4
+ export declare const scopes: readonly ["apps:write", "apps:delete", "blocks:write", "blocks:delete", "organizations:write", "resources:read", "resources:write", "assets:write", "teams:read", "teams:write"];
@@ -3,6 +3,7 @@
3
3
  */
4
4
  export const scopes = [
5
5
  'apps:write',
6
+ 'apps:delete',
6
7
  'blocks:write',
7
8
  'blocks:delete',
8
9
  'organizations:write',
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { convertToCsv } from './convertToCsv.js';
2
3
  describe('convertToCsv', () => {
3
4
  it('should throw an error if input is null', () => {
package/has.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { has } from './has.js';
2
3
  describe('has', () => {
3
4
  it('should return false for null targets', () => {
package/i18n.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { detectLocale, sortLocales } from './i18n.js';
2
3
  describe('sortLocales', () => {
3
4
  const tests = [
package/iterApp.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it, vi } from 'vitest';
1
2
  import { iterAction, iterApp, iterBlock, iterBlockList, iterPage } from './iterApp.js';
2
3
  describe('iterAction', () => {
3
4
  it('should call the appropriate callbacks', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it, vi } from 'vitest';
1
2
  import { combineSchemas, generateDataFromSchema, iterJSONSchema } from './jsonschema.js';
2
3
  describe('generateDataFromSchema', () => {
3
4
  it('should not crash if no schema is defined', () => {
package/mapValues.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { mapValues } from './mapValues.js';
2
3
  describe('mapValues', () => {
3
4
  it('should call the iteratee over each own property', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { identity, rethrow, stripNullValues } from './miscellaneous.js';
2
3
  /**
3
4
  * Return the input data.
package/normalize.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { expect, it } from 'vitest';
1
2
  import { normalize } from './normalize.js';
2
3
  const fixtures = [
3
4
  ['Foo', 'foo'],
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { objectCache } from './objectCache.js';
2
3
  describe('objectCache', () => {
3
4
  it('should cache results', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsemble/utils",
3
- "version": "0.22.10",
3
+ "version": "0.23.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.22.10",
40
+ "@appsemble/types": "0.23.1",
41
41
  "axios": "^1.0.0",
42
42
  "cron-parser": "^4.0.0",
43
43
  "date-fns": "^2.0.0",
@@ -59,6 +59,7 @@
59
59
  "@types/lcm": "^0.0.0",
60
60
  "bulma": "0.9.3",
61
61
  "koas-core": "^0.7.0",
62
+ "vitest": "^0.34.0",
62
63
  "yaml": "^2.0.0"
63
64
  },
64
65
  "engines": {
package/prefix.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { expect, it } from 'vitest';
1
2
  import { prefix } from './prefix.js';
2
3
  it('should prefix a string if the prefix is truthy', () => {
3
4
  const result = prefix('bar', 'foo');
@@ -257,6 +257,35 @@ Result:
257
257
  }
258
258
  ]
259
259
  \`\`\`
260
+
261
+ Extra example:
262
+
263
+ array remapper can also be used in combination with different remappers as in the current example comes object remapper:
264
+
265
+ Example object:
266
+ \`\`\`json
267
+ {
268
+ "names": ["Bart", "Bas", "Sam"]
269
+ }
270
+ \`\`\`
271
+
272
+ Combination of object and array remapper:
273
+
274
+ \`\`\`yaml
275
+ object.assign:
276
+ names:
277
+ - { prop: Applicants }
278
+ - array.append:
279
+ - Kevin
280
+ \`\`\`
281
+
282
+ Result:
283
+ \`\`\`json
284
+ {
285
+ "names": ["Bart", "Bas", "Sam", "Kevin"]
286
+ }
287
+ \`\`\`
288
+ \
260
289
  `,
261
290
  },
262
291
  'array.omit': {
package/remap.test.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { IntlMessageFormat } from 'intl-messageformat';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
3
  import { remap } from './remap.js';
3
4
  function runTests(tests) {
4
5
  it.each(Object.entries(tests))('should %s', (name, { context, expected, history, input, mappers, messages, userInfo }) => {
package/string.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { camelToHyphen, decodeJSONRef, toUpperCase } from './string.js';
2
3
  describe('camelToHyphen', () => {
3
4
  it('should convert camel case to hyphenated', () => {
package/theme.test.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import bulmaPkg from 'bulma/package.json';
2
+ import { describe, expect, it } from 'vitest';
2
3
  import { createThemeURL, mergeThemes } from './theme.js';
3
4
  describe('mergeThemes', () => {
4
5
  it('should use the base theme as default', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { StyleValidationError, validateStyle } from './validateStyle.js';
2
3
  describe('validateStyle', () => {
3
4
  it('should validate correct CSS', () => {
@@ -1,4 +1,5 @@
1
1
  import { ValidationError } from 'jsonschema';
2
+ import { describe, expect, it } from 'vitest';
2
3
  import { validateAppDefinition } from './validation.js';
3
4
  function createTestApp() {
4
5
  return {