@appsemble/cli 0.27.12 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/commands/app/delete.js +2 -10
- package/commands/app/export.js +2 -3
- package/commands/block/delete.js +2 -3
- package/commands/team/create.js +1 -2
- package/commands/team/delete.js +1 -2
- package/lib/app.d.ts +8 -1
- package/lib/app.js +21 -4
- package/lib/app.test.js +495 -23
- package/lib/asset.test.d.ts +1 -0
- package/lib/asset.test.js +101 -0
- package/lib/block.d.ts +2 -1
- package/lib/block.js +3 -2
- package/lib/block.test.js +52 -1
- package/lib/organization.js +5 -3
- package/lib/organization.test.d.ts +1 -0
- package/lib/organization.test.js +222 -0
- package/lib/resource.test.d.ts +1 -0
- package/lib/resource.test.js +391 -0
- package/lib/team.d.ts +6 -2
- package/lib/team.js +6 -4
- package/lib/team.test.d.ts +1 -0
- package/lib/team.test.js +606 -0
- package/lib/testUtils.d.ts +2 -0
- package/lib/testUtils.js +11 -0
- package/package.json +6 -6
- package/server/options/parseQuery.test.js +2 -0
- package/templates/apps/empty/app-definition.yaml +2 -2
- package/templates/blocks/mini-jsx/package.json +1 -1
- package/templates/blocks/preact/package.json +2 -2
- package/templates/blocks/vanilla/package.json +1 -1
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { resolveFixture } from '@appsemble/node-utils';
|
|
2
|
+
import { createServer, createTestUser, models, setArgv, useTestDatabase } from '@appsemble/server';
|
|
3
|
+
import { setTestApp } from 'axios-test-instance';
|
|
4
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { initAxios } from './initAxios.js';
|
|
6
|
+
import { publishResource, updateResource } from './resource.js';
|
|
7
|
+
import { authorizeCLI } from './testUtils.js';
|
|
8
|
+
const { App, Organization, OrganizationMember, Resource } = models;
|
|
9
|
+
const argv = { host: 'http://localhost', secret: 'test', aesSecret: 'testSecret' };
|
|
10
|
+
let user;
|
|
11
|
+
let organization;
|
|
12
|
+
let testApp;
|
|
13
|
+
useTestDatabase(import.meta);
|
|
14
|
+
beforeAll(() => {
|
|
15
|
+
vi.useFakeTimers();
|
|
16
|
+
setArgv(argv);
|
|
17
|
+
});
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
vi.clearAllTimers();
|
|
20
|
+
vi.setSystemTime(0);
|
|
21
|
+
const server = await createServer();
|
|
22
|
+
testApp = await setTestApp(server);
|
|
23
|
+
initAxios({ remote: testApp.defaults.baseURL });
|
|
24
|
+
user = await createTestUser();
|
|
25
|
+
organization = await Organization.create({
|
|
26
|
+
id: 'testorganization',
|
|
27
|
+
name: 'Test Organization',
|
|
28
|
+
});
|
|
29
|
+
await OrganizationMember.create({
|
|
30
|
+
OrganizationId: organization.id,
|
|
31
|
+
UserId: user.id,
|
|
32
|
+
role: 'Owner',
|
|
33
|
+
});
|
|
34
|
+
await Organization.create({ id: 'appsemble', name: 'Appsemble' });
|
|
35
|
+
});
|
|
36
|
+
afterAll(() => {
|
|
37
|
+
vi.useRealTimers();
|
|
38
|
+
});
|
|
39
|
+
describe('publishResource', () => {
|
|
40
|
+
it('should publish resources from a file', async () => {
|
|
41
|
+
const resourceDefinition = {
|
|
42
|
+
roles: ['$public'],
|
|
43
|
+
schema: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
required: ['name'],
|
|
47
|
+
properties: {
|
|
48
|
+
name: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
},
|
|
51
|
+
email: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
format: 'email',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const app = await App.create({
|
|
59
|
+
path: 'test-app',
|
|
60
|
+
definition: {
|
|
61
|
+
resources: { test: resourceDefinition },
|
|
62
|
+
name: 'Test App',
|
|
63
|
+
defaultPage: 'Test Page',
|
|
64
|
+
},
|
|
65
|
+
vapidPublicKey: 'a',
|
|
66
|
+
vapidPrivateKey: 'b',
|
|
67
|
+
visibility: 'public',
|
|
68
|
+
OrganizationId: organization.id,
|
|
69
|
+
});
|
|
70
|
+
await authorizeCLI('resources:write', testApp);
|
|
71
|
+
await publishResource({
|
|
72
|
+
appId: app.id,
|
|
73
|
+
definition: resourceDefinition,
|
|
74
|
+
path: resolveFixture('resources/test.json'),
|
|
75
|
+
remote: testApp.defaults.baseURL,
|
|
76
|
+
type: 'test',
|
|
77
|
+
seed: false,
|
|
78
|
+
});
|
|
79
|
+
const resources = await Resource.findAll({
|
|
80
|
+
where: { AppId: app.id },
|
|
81
|
+
});
|
|
82
|
+
expect(resources.map((r) => r.toJSON())).toMatchInlineSnapshot(`
|
|
83
|
+
[
|
|
84
|
+
{
|
|
85
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
86
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
87
|
+
"email": "test@example.com",
|
|
88
|
+
"id": 1,
|
|
89
|
+
"name": "John Doe",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
93
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
94
|
+
"email": "test2@example.com",
|
|
95
|
+
"id": 2,
|
|
96
|
+
"name": "Jane Doe",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
100
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
101
|
+
"id": 3,
|
|
102
|
+
"name": "test name",
|
|
103
|
+
},
|
|
104
|
+
]
|
|
105
|
+
`);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
it('should throw if there is an error', async () => {
|
|
109
|
+
const resourceDefinition = {
|
|
110
|
+
roles: ['$public'],
|
|
111
|
+
schema: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
additionalProperties: false,
|
|
114
|
+
required: ['name'],
|
|
115
|
+
properties: {
|
|
116
|
+
name: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
},
|
|
119
|
+
email: {
|
|
120
|
+
type: 'string',
|
|
121
|
+
format: 'email',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
const app = await App.create({
|
|
127
|
+
path: 'test-app',
|
|
128
|
+
definition: {
|
|
129
|
+
resources: { test: resourceDefinition },
|
|
130
|
+
name: 'Test App',
|
|
131
|
+
defaultPage: 'Test Page',
|
|
132
|
+
},
|
|
133
|
+
vapidPublicKey: 'a',
|
|
134
|
+
vapidPrivateKey: 'b',
|
|
135
|
+
visibility: 'public',
|
|
136
|
+
OrganizationId: organization.id,
|
|
137
|
+
});
|
|
138
|
+
await authorizeCLI('resources:write', testApp);
|
|
139
|
+
await app.destroy();
|
|
140
|
+
await expect(() => publishResource({
|
|
141
|
+
appId: app.id,
|
|
142
|
+
definition: resourceDefinition,
|
|
143
|
+
path: resolveFixture('resources/test.json'),
|
|
144
|
+
remote: testApp.defaults.baseURL,
|
|
145
|
+
type: 'test',
|
|
146
|
+
seed: false,
|
|
147
|
+
})).rejects.toThrow('Request failed with status code 404');
|
|
148
|
+
});
|
|
149
|
+
describe('updateResource', () => {
|
|
150
|
+
it('should update existing resources', async () => {
|
|
151
|
+
const resourceDefinition = {
|
|
152
|
+
roles: ['$public'],
|
|
153
|
+
schema: {
|
|
154
|
+
type: 'object',
|
|
155
|
+
additionalProperties: false,
|
|
156
|
+
required: ['name'],
|
|
157
|
+
properties: {
|
|
158
|
+
name: {
|
|
159
|
+
type: 'string',
|
|
160
|
+
},
|
|
161
|
+
email: {
|
|
162
|
+
type: 'string',
|
|
163
|
+
format: 'email',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
const app = await App.create({
|
|
169
|
+
path: 'test-app',
|
|
170
|
+
definition: {
|
|
171
|
+
resources: { test: resourceDefinition },
|
|
172
|
+
name: 'Test App',
|
|
173
|
+
defaultPage: 'Test Page',
|
|
174
|
+
},
|
|
175
|
+
vapidPublicKey: 'a',
|
|
176
|
+
vapidPrivateKey: 'b',
|
|
177
|
+
visibility: 'public',
|
|
178
|
+
OrganizationId: organization.id,
|
|
179
|
+
});
|
|
180
|
+
await Resource.bulkCreate([
|
|
181
|
+
{
|
|
182
|
+
id: 1,
|
|
183
|
+
type: 'test',
|
|
184
|
+
AppId: app.id,
|
|
185
|
+
data: {
|
|
186
|
+
name: 'Hello World',
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: 2,
|
|
191
|
+
type: 'test',
|
|
192
|
+
AppId: app.id,
|
|
193
|
+
data: {
|
|
194
|
+
name: 'World Hello',
|
|
195
|
+
email: 'hello@example.com',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 3,
|
|
200
|
+
type: 'test',
|
|
201
|
+
AppId: app.id,
|
|
202
|
+
data: {
|
|
203
|
+
name: 'Random name',
|
|
204
|
+
email: 'removed@example.com',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
]);
|
|
208
|
+
await authorizeCLI('resources:write', testApp);
|
|
209
|
+
await updateResource({
|
|
210
|
+
appId: app.id,
|
|
211
|
+
resourceName: 'test',
|
|
212
|
+
remote: testApp.defaults.baseURL,
|
|
213
|
+
path: resolveFixture('resources/test-update.json'),
|
|
214
|
+
});
|
|
215
|
+
const resources = await Resource.findAll();
|
|
216
|
+
expect(resources.map((r) => r.toJSON())).toMatchInlineSnapshot(`
|
|
217
|
+
[
|
|
218
|
+
{
|
|
219
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
220
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
221
|
+
"email": "test@example.com",
|
|
222
|
+
"id": 1,
|
|
223
|
+
"name": "John Doe",
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
227
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
228
|
+
"email": "test2@example.com",
|
|
229
|
+
"id": 2,
|
|
230
|
+
"name": "Jane Doe",
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
234
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
235
|
+
"id": 3,
|
|
236
|
+
"name": "test name",
|
|
237
|
+
},
|
|
238
|
+
]
|
|
239
|
+
`);
|
|
240
|
+
});
|
|
241
|
+
it('should throw if the resource to be updated does not exist', async () => {
|
|
242
|
+
const resourceDefinition = {
|
|
243
|
+
roles: ['$public'],
|
|
244
|
+
schema: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
additionalProperties: false,
|
|
247
|
+
required: ['name'],
|
|
248
|
+
properties: {
|
|
249
|
+
name: {
|
|
250
|
+
type: 'string',
|
|
251
|
+
},
|
|
252
|
+
email: {
|
|
253
|
+
type: 'string',
|
|
254
|
+
format: 'email',
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
const app = await App.create({
|
|
260
|
+
path: 'test-app',
|
|
261
|
+
definition: {
|
|
262
|
+
resources: { test: resourceDefinition },
|
|
263
|
+
name: 'Test App',
|
|
264
|
+
defaultPage: 'Test Page',
|
|
265
|
+
},
|
|
266
|
+
vapidPublicKey: 'a',
|
|
267
|
+
vapidPrivateKey: 'b',
|
|
268
|
+
visibility: 'public',
|
|
269
|
+
OrganizationId: organization.id,
|
|
270
|
+
});
|
|
271
|
+
await Resource.bulkCreate([
|
|
272
|
+
{
|
|
273
|
+
id: 1,
|
|
274
|
+
type: 'test',
|
|
275
|
+
AppId: app.id,
|
|
276
|
+
data: {
|
|
277
|
+
name: 'Hello World',
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 2,
|
|
282
|
+
type: 'test',
|
|
283
|
+
AppId: app.id,
|
|
284
|
+
data: {
|
|
285
|
+
name: 'World Hello',
|
|
286
|
+
email: 'hello@example.com',
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
]);
|
|
290
|
+
await authorizeCLI('resources:write', testApp);
|
|
291
|
+
await expect(() => updateResource({
|
|
292
|
+
appId: app.id,
|
|
293
|
+
resourceName: 'test',
|
|
294
|
+
remote: testApp.defaults.baseURL,
|
|
295
|
+
path: resolveFixture('resources/test-update.json'),
|
|
296
|
+
})).rejects.toThrow('Request failed with status code 404');
|
|
297
|
+
});
|
|
298
|
+
it('should not update if there are no IDs in resource file', async () => {
|
|
299
|
+
const resourceDefinition = {
|
|
300
|
+
roles: ['$public'],
|
|
301
|
+
schema: {
|
|
302
|
+
type: 'object',
|
|
303
|
+
additionalProperties: false,
|
|
304
|
+
required: ['name'],
|
|
305
|
+
properties: {
|
|
306
|
+
name: {
|
|
307
|
+
type: 'string',
|
|
308
|
+
},
|
|
309
|
+
email: {
|
|
310
|
+
type: 'string',
|
|
311
|
+
format: 'email',
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
const app = await App.create({
|
|
317
|
+
path: 'test-app',
|
|
318
|
+
definition: {
|
|
319
|
+
resources: { test: resourceDefinition },
|
|
320
|
+
name: 'Test App',
|
|
321
|
+
defaultPage: 'Test Page',
|
|
322
|
+
},
|
|
323
|
+
vapidPublicKey: 'a',
|
|
324
|
+
vapidPrivateKey: 'b',
|
|
325
|
+
visibility: 'public',
|
|
326
|
+
OrganizationId: organization.id,
|
|
327
|
+
});
|
|
328
|
+
await Resource.bulkCreate([
|
|
329
|
+
{
|
|
330
|
+
id: 1,
|
|
331
|
+
type: 'test',
|
|
332
|
+
AppId: app.id,
|
|
333
|
+
data: {
|
|
334
|
+
name: 'Hello World',
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: 2,
|
|
339
|
+
type: 'test',
|
|
340
|
+
AppId: app.id,
|
|
341
|
+
data: {
|
|
342
|
+
name: 'World Hello',
|
|
343
|
+
email: 'hello@example.com',
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
]);
|
|
347
|
+
await authorizeCLI('resources:write', testApp);
|
|
348
|
+
const resources = await Resource.findAll();
|
|
349
|
+
expect(resources.map((r) => r.toJSON())).toMatchInlineSnapshot(`
|
|
350
|
+
[
|
|
351
|
+
{
|
|
352
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
353
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
354
|
+
"id": 1,
|
|
355
|
+
"name": "Hello World",
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
359
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
360
|
+
"email": "hello@example.com",
|
|
361
|
+
"id": 2,
|
|
362
|
+
"name": "World Hello",
|
|
363
|
+
},
|
|
364
|
+
]
|
|
365
|
+
`);
|
|
366
|
+
await updateResource({
|
|
367
|
+
appId: app.id,
|
|
368
|
+
resourceName: 'test',
|
|
369
|
+
remote: testApp.defaults.baseURL,
|
|
370
|
+
path: resolveFixture('resources/test.json'),
|
|
371
|
+
});
|
|
372
|
+
expect(resources.map((r) => r.toJSON())).toMatchInlineSnapshot(`
|
|
373
|
+
[
|
|
374
|
+
{
|
|
375
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
376
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
377
|
+
"id": 1,
|
|
378
|
+
"name": "Hello World",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"$created": "1970-01-01T00:00:00.000Z",
|
|
382
|
+
"$updated": "1970-01-01T00:00:00.000Z",
|
|
383
|
+
"email": "hello@example.com",
|
|
384
|
+
"id": 2,
|
|
385
|
+
"name": "World Hello",
|
|
386
|
+
},
|
|
387
|
+
]
|
|
388
|
+
`);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
//# sourceMappingURL=resource.test.js.map
|
package/lib/team.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ interface SharedTeamParams {
|
|
|
8
8
|
* The remote server to create the team on.
|
|
9
9
|
*/
|
|
10
10
|
remote: string;
|
|
11
|
+
/**
|
|
12
|
+
* The client credentials used to authenticate
|
|
13
|
+
*/
|
|
14
|
+
clientCredentials?: string;
|
|
11
15
|
}
|
|
12
16
|
interface SharedExistingTeamParams extends SharedTeamParams {
|
|
13
17
|
/**
|
|
@@ -48,8 +52,8 @@ interface CreateTeamParams extends SharedTeamParams {
|
|
|
48
52
|
annotations?: string[];
|
|
49
53
|
}
|
|
50
54
|
export declare function resolveAnnotations(annotations: string[]): Record<string, string>;
|
|
51
|
-
export declare function createTeam({ annotations, appId, name, remote, }: CreateTeamParams): Promise<void>;
|
|
52
|
-
export declare function deleteTeam({ appId, id, remote }: SharedExistingTeamParams): Promise<void>;
|
|
55
|
+
export declare function createTeam({ annotations, appId, clientCredentials, name, remote, }: CreateTeamParams): Promise<void>;
|
|
56
|
+
export declare function deleteTeam({ appId, clientCredentials, id, remote, }: SharedExistingTeamParams): Promise<void>;
|
|
53
57
|
export declare function updateTeam({ annotations, appId, id, name, remote, }: UpdateTeamParams): Promise<void>;
|
|
54
58
|
export declare function inviteMember({ appId, id, remote, user, }: SharedTeamMemberParams): Promise<void>;
|
|
55
59
|
export declare function updateMember({ appId, id, remote, role, user, }: UpdateTeamMemberParams): Promise<void>;
|
package/lib/team.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppsembleError, logger } from '@appsemble/node-utils';
|
|
1
|
+
import { AppsembleError, authenticate, logger } from '@appsemble/node-utils';
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
export function resolveAnnotations(annotations) {
|
|
4
4
|
const annotationRegex = /^\w+=.+$/;
|
|
@@ -12,22 +12,24 @@ export function resolveAnnotations(annotations) {
|
|
|
12
12
|
}))
|
|
13
13
|
: undefined;
|
|
14
14
|
}
|
|
15
|
-
export async function createTeam({ annotations = [], appId, name, remote, }) {
|
|
15
|
+
export async function createTeam({ annotations = [], appId, clientCredentials, name, remote, }) {
|
|
16
16
|
logger.info(`Creating team ${name}`);
|
|
17
|
+
await authenticate(remote, 'teams:write', clientCredentials);
|
|
17
18
|
const { data: { id }, } = await axios.post(`/api/apps/${appId}/teams`, {
|
|
18
19
|
name,
|
|
19
20
|
annotations: resolveAnnotations(annotations),
|
|
20
21
|
}, { baseURL: remote });
|
|
21
22
|
logger.info(`Successfully created team ${name} with ID ${id}`);
|
|
22
23
|
}
|
|
23
|
-
export async function deleteTeam({ appId, id, remote }) {
|
|
24
|
+
export async function deleteTeam({ appId, clientCredentials, id, remote, }) {
|
|
25
|
+
await authenticate(remote, 'teams:write', clientCredentials);
|
|
24
26
|
logger.info(`Deleting team ${id}`);
|
|
25
27
|
await axios.delete(`/api/apps/${appId}/teams/${id}`, { baseURL: remote });
|
|
26
28
|
logger.info(`Successfully deleted team ${id}`);
|
|
27
29
|
}
|
|
28
30
|
export async function updateTeam({ annotations = [], appId, id, name, remote, }) {
|
|
29
31
|
logger.info(`Updating team ${id}`);
|
|
30
|
-
await axios.
|
|
32
|
+
await axios.patch(`/api/apps/${appId}/teams/${id}`, {
|
|
31
33
|
name,
|
|
32
34
|
annotations: resolveAnnotations(annotations),
|
|
33
35
|
}, { baseURL: remote });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|