@prisme.ai/sdk 0.0.2

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/lib/api.ts ADDED
@@ -0,0 +1,439 @@
1
+ import QueryString from 'qs';
2
+ import Fetcher from './fetcher';
3
+ import { Event, Workspace } from './types';
4
+ import { Events } from './events';
5
+ import { removedUndefinedProperties } from './utils';
6
+
7
+ type UserPermissions = Prismeai.UserPermissions;
8
+
9
+ interface PageWithMetadata extends Prismeai.Page {
10
+ createdAt: string;
11
+ createdBy: string;
12
+ updatedAt: string;
13
+ updatedBy: string;
14
+ }
15
+
16
+ export class Api extends Fetcher {
17
+ async me() {
18
+ return await this.get('/me');
19
+ }
20
+
21
+ async signin(
22
+ email: string,
23
+ password: string
24
+ ): Promise<
25
+ Prismeai.User & {
26
+ token: string;
27
+ }
28
+ > {
29
+ return await this.post('/login', {
30
+ email,
31
+ password,
32
+ });
33
+ }
34
+
35
+ async signup(
36
+ email: string,
37
+ password: string,
38
+ firstName: string,
39
+ lastName: string
40
+ ): Promise<
41
+ Prismeai.User & {
42
+ token: string;
43
+ }
44
+ > {
45
+ return await this.post('/signup', {
46
+ email: email,
47
+ password,
48
+ firstName,
49
+ lastName,
50
+ });
51
+ }
52
+
53
+ async signout() {
54
+ await this.post('/logout');
55
+ this.token = null;
56
+ }
57
+
58
+ // Workspaces
59
+ async getWorkspaces(): Promise<Workspace[]> {
60
+ return await this.get('/workspaces?limit=300');
61
+ }
62
+
63
+ async getWorkspace(id: string): Promise<Workspace | null> {
64
+ return await this.get(`/workspaces/${id}`);
65
+ }
66
+
67
+ async createWorkspace(name: string): Promise<Workspace> {
68
+ return await this.post('/workspaces', { name });
69
+ }
70
+
71
+ async updateWorkspace(workspace: Workspace): Promise<Workspace> {
72
+ return await this.patch(
73
+ `/workspaces/${workspace.id}`,
74
+ await this.replaceAllImagesData(workspace, workspace.id)
75
+ );
76
+ }
77
+
78
+ async deleteWorkspace(workspaceId: Workspace['id']): Promise<Workspace> {
79
+ return await this.delete(`/workspaces/${workspaceId}`);
80
+ }
81
+
82
+ // Automations
83
+ async createAutomation(
84
+ workspace: Workspace,
85
+ automation: Prismeai.Automation
86
+ ): Promise<Prismeai.Automation & { slug: string }> {
87
+ return await this.post(`/workspaces/${workspace.id}/automations`, {
88
+ ...automation,
89
+ });
90
+ }
91
+
92
+ async updateAutomation(
93
+ workspace: Workspace,
94
+ slug: string,
95
+ automation: Prismeai.Automation
96
+ ): Promise<Prismeai.Automation & { slug: string }> {
97
+ return await this.patch(
98
+ `/workspaces/${workspace.id}/automations/${slug}`,
99
+ await this.replaceAllImagesData(automation, workspace.id)
100
+ );
101
+ }
102
+
103
+ async deleteAutomation(workspace: Workspace, slug: string): Promise<string> {
104
+ return await this.delete(`/workspaces/${workspace.id}/automations/${slug}`);
105
+ }
106
+
107
+ // Pages
108
+ async getPages(
109
+ workspaceId: NonNullable<Workspace['id']>
110
+ ): Promise<Prismeai.Page[]> {
111
+ try {
112
+ const pages = await this.get<PageWithMetadata[]>(
113
+ `/workspaces/${workspaceId}/pages`
114
+ );
115
+ return pages.map(
116
+ ({ createdAt, createdBy, updatedAt, updatedBy, ...page }: any) => page
117
+ );
118
+ } catch (e) {
119
+ return [];
120
+ }
121
+ }
122
+
123
+ async getPage(
124
+ workspaceId: PrismeaiAPI.GetPage.Parameters.WorkspaceId,
125
+ pageId: PrismeaiAPI.GetPage.Parameters.Id
126
+ ): Promise<Prismeai.DetailedPage> {
127
+ return await this.get(`/workspaces/${workspaceId}/pages/${pageId}`);
128
+ }
129
+
130
+ async getPageBySlug(
131
+ pageSlug: PrismeaiAPI.GetPageBySlug.Parameters.Slug
132
+ ): Promise<Prismeai.DetailedPage> {
133
+ return await this.get(`/pages/${pageSlug}`);
134
+ }
135
+
136
+ async createPage(
137
+ workspaceId: NonNullable<Workspace['id']>,
138
+ page: Prismeai.Page
139
+ ): Promise<Prismeai.Page> {
140
+ const {
141
+ createdAt,
142
+ createdBy,
143
+ updatedAt,
144
+ updatedBy,
145
+ ...newPage
146
+ } = await this.post<PageWithMetadata>(
147
+ `/workspaces/${workspaceId}/pages`,
148
+ page
149
+ );
150
+ return newPage;
151
+ }
152
+
153
+ // Replace images as dataurl to uploaded url in any type of data
154
+
155
+ async updatePage(
156
+ workspaceId: NonNullable<Workspace['id']>,
157
+ page: Prismeai.Page
158
+ ): Promise<Prismeai.Page> {
159
+ const {
160
+ createdAt,
161
+ createdBy,
162
+ updatedAt,
163
+ updatedBy,
164
+ ...updatedPage
165
+ } = await this.patch<PageWithMetadata>(
166
+ `/workspaces/${workspaceId}/pages/${page.id}`,
167
+ await this.replaceAllImagesData(page, workspaceId)
168
+ );
169
+ return updatedPage;
170
+ }
171
+
172
+ async deletePage(
173
+ workspaceId: NonNullable<Workspace['id']>,
174
+ pageId: string
175
+ ): Promise<Pick<Prismeai.Page, 'id'>> {
176
+ return await this.delete(`/workspaces/${workspaceId}/pages/${pageId}`);
177
+ }
178
+
179
+ // Events
180
+ streamEvents(workspaceId: string): Promise<Events> {
181
+ const events = new Events(workspaceId, this.token || '', this.host);
182
+ return new Promise((resolve, reject) => {
183
+ events.once('connect', () => {
184
+ resolve(events);
185
+ });
186
+ events.once('connect_error', () => {
187
+ reject();
188
+ events.close();
189
+ });
190
+ });
191
+ }
192
+
193
+ async getEvents(
194
+ workspaceId: string,
195
+ options: { beforeDate?: Date | string } = {}
196
+ ): Promise<Event<Date>[]> {
197
+ try {
198
+ const query = QueryString.stringify(options);
199
+ const {
200
+ result: { events },
201
+ } = await this.get<{
202
+ result: {
203
+ events: Event<string>[];
204
+ };
205
+ }>(`/workspaces/${workspaceId}/events${query && `?${query}`}`);
206
+ return events.map(({ createdAt, ...event }) => ({
207
+ ...event,
208
+ createdAt: new Date(createdAt),
209
+ }));
210
+ } catch (e) {
211
+ return [];
212
+ }
213
+ }
214
+
215
+ async postEvents(
216
+ workspaceId: PrismeaiAPI.SendWorkspaceEvent.Parameters.WorkspaceId,
217
+ events: PrismeaiAPI.SendWorkspaceEvent.RequestBody['events']
218
+ ): Promise<boolean> {
219
+ try {
220
+ await this.post<PrismeaiAPI.SendWorkspaceEvent.Responses.$200>(
221
+ `/workspaces/${workspaceId}/events`,
222
+ {
223
+ events,
224
+ }
225
+ );
226
+ return true;
227
+ } catch (e) {
228
+ return false;
229
+ }
230
+ }
231
+
232
+ async getPermissions(
233
+ subjectType: PrismeaiAPI.GetPermissions.Parameters.SubjectType,
234
+ subjectId: string
235
+ ): Promise<PrismeaiAPI.GetPermissions.Responses.$200> {
236
+ return await this.get(`/${subjectType}/${subjectId}/permissions`);
237
+ }
238
+
239
+ async addPermissions(
240
+ subjectType: PrismeaiAPI.GetPermissions.Parameters.SubjectType,
241
+ subjectId: string,
242
+ permissions: UserPermissions
243
+ ): Promise<PrismeaiAPI.Share.Responses.$200> {
244
+ return await this.post(
245
+ `/${subjectType}/${subjectId}/permissions`,
246
+ permissions
247
+ );
248
+ }
249
+
250
+ async deletePermissions(
251
+ subjectType: PrismeaiAPI.GetPermissions.Parameters.SubjectType,
252
+ subjectId: string,
253
+ userEmail: string
254
+ ): Promise<PrismeaiAPI.RevokePermissions.Responses.$200> {
255
+ return await this.delete(
256
+ `/${subjectType}/${subjectId}/permissions/${userEmail}`
257
+ );
258
+ }
259
+
260
+ async getApps({
261
+ query,
262
+ page,
263
+ limit,
264
+ workspaceId,
265
+ }: {
266
+ query?: PrismeaiAPI.SearchApps.QueryParameters['text'];
267
+ page?: PrismeaiAPI.SearchApps.QueryParameters['page'];
268
+ limit?: PrismeaiAPI.SearchApps.QueryParameters['limit'];
269
+ workspaceId?: PrismeaiAPI.SearchApps.QueryParameters['workspaceId'];
270
+ }): Promise<PrismeaiAPI.SearchApps.Responses.$200> {
271
+ const params = new URLSearchParams(
272
+ removedUndefinedProperties(
273
+ {
274
+ text: `${query || ''}`,
275
+ page: `${page || ''}`,
276
+ limit: `${limit || ''}`,
277
+ workspaceId: `${workspaceId || ''}`,
278
+ },
279
+ true
280
+ )
281
+ );
282
+ return await this.get(`/apps?${params.toString()}`);
283
+ }
284
+
285
+ async installApp(
286
+ workspaceId: PrismeaiAPI.InstallAppInstance.PathParameters['workspaceId'],
287
+ body: PrismeaiAPI.InstallAppInstance.RequestBody
288
+ ): Promise<PrismeaiAPI.InstallAppInstance.Responses.$200> {
289
+ return await this.post(`/workspaces/${workspaceId}/apps`, body);
290
+ }
291
+
292
+ async updateApp(
293
+ workspaceId: PrismeaiAPI.ConfigureAppInstance.PathParameters['workspaceId'],
294
+ slug: PrismeaiAPI.ConfigureAppInstance.PathParameters['slug'],
295
+ body: PrismeaiAPI.ConfigureAppInstance.RequestBody
296
+ ): Promise<PrismeaiAPI.ConfigureAppInstance.Responses.$200> {
297
+ return await this.patch(`/workspaces/${workspaceId}/apps/${slug}`, body);
298
+ }
299
+
300
+ async uninstallApp(
301
+ workspaceId: PrismeaiAPI.UninstallAppInstance.PathParameters['workspaceId'],
302
+ slug: PrismeaiAPI.ConfigureAppInstance.PathParameters['slug']
303
+ ): Promise<PrismeaiAPI.UninstallAppInstance.Responses.$200> {
304
+ return await this.delete(`/workspaces/${workspaceId}/apps/${slug}`);
305
+ }
306
+
307
+ async publishApp(
308
+ body: PrismeaiAPI.PublishApp.RequestBody
309
+ ): Promise<PrismeaiAPI.PublishApp.Responses.$200> {
310
+ return await this.post(`/apps`, body);
311
+ }
312
+
313
+ async listAppInstances(
314
+ workspaceId: PrismeaiAPI.ListAppInstances.PathParameters['workspaceId']
315
+ ): Promise<PrismeaiAPI.ListAppInstances.Responses.$200> {
316
+ return await this.get(`/workspaces/${workspaceId}/apps`);
317
+ }
318
+
319
+ async fetchImports(
320
+ workspaceId: PrismeaiAPI.ListAppInstances.Parameters.WorkspaceId
321
+ ): Promise<PrismeaiAPI.ListAppInstances.Responses.$200> {
322
+ return await this.get(`/workspaces/${workspaceId}/apps`);
323
+ }
324
+
325
+ async getAppConfig<T>(
326
+ workspaceId: PrismeaiAPI.GetAppInstanceConfig.Parameters.WorkspaceId,
327
+ slug: PrismeaiAPI.GetAppInstanceConfig.Parameters.Slug
328
+ ): Promise<T> {
329
+ const config = await this.get<T>(
330
+ `/workspaces/${workspaceId}/apps/${slug}/config`
331
+ );
332
+ return config as T;
333
+ }
334
+
335
+ async updateAppConfig(
336
+ workspaceId: PrismeaiAPI.UpdateAppInstanceConfig.Parameters.WorkspaceId,
337
+ slug: PrismeaiAPI.UpdateAppInstanceConfig.Parameters.Slug,
338
+ config: any
339
+ ): Promise<PrismeaiAPI.UpdateAppInstanceConfig.Responses.$200['config']> {
340
+ await this.patch<Prismeai.AppInstance>(
341
+ `/workspaces/${workspaceId}/apps/${slug}/config`,
342
+ { ...config }
343
+ );
344
+ return config;
345
+ }
346
+
347
+ async uploadFiles(files: string | string[], workspaceId: string) {
348
+ function dataURItoBlob(dataURI: string): [Blob, string] {
349
+ // convert base64/URLEncoded data component to raw binary data held in a string
350
+ let byteString;
351
+ if (dataURI.split(',')[0].indexOf('base64') >= 0)
352
+ byteString = atob(dataURI.split(',')[1]);
353
+ else byteString = unescape(dataURI.split(',')[1]);
354
+
355
+ // separate out the mime component
356
+ const metadata = dataURI
357
+ .split(';')
358
+ .filter((v, k, all) => k < all.length - 1)
359
+ .map((v) => v.split(/:/));
360
+ const [, mimeString = ''] = metadata.find(([k, v]) => k === 'data') || [];
361
+ const [, ext] = mimeString.split(/\//);
362
+ const [, fileName = `file.${ext}`] =
363
+ metadata.find(([k, v]) => k === 'filename') || [];
364
+
365
+ // write the bytes of the string to a typed array
366
+ let ia = new Uint8Array(byteString.length);
367
+ for (var i = 0; i < byteString.length; i++) {
368
+ ia[i] = byteString.charCodeAt(i);
369
+ }
370
+
371
+ return [new Blob([ia], { type: mimeString }), fileName];
372
+ }
373
+ const formData = new FormData();
374
+ (Array.isArray(files) ? files : [files]).forEach((file) => {
375
+ formData.append('file', ...dataURItoBlob(file));
376
+ });
377
+
378
+ try {
379
+ return await this._fetch<PrismeaiAPI.UploadFile.Responses.$200>(
380
+ `/workspaces/${workspaceId}/files`,
381
+ {
382
+ method: 'POST',
383
+ body: formData,
384
+ }
385
+ );
386
+ } catch (e) {}
387
+ return [];
388
+ }
389
+
390
+ async replaceAllImagesData(original: any, workspaceId: string) {
391
+ const key = '…uploading-';
392
+ const toUpload: string[] = [];
393
+ const searchImages = (mayHaveImage: any, uploaded?: string[]) => {
394
+ switch (typeof mayHaveImage) {
395
+ case 'string':
396
+ if (uploaded && mayHaveImage.match(key)) {
397
+ // Replace with url
398
+ const [, index] = mayHaveImage.split(key);
399
+ return uploaded[+index];
400
+ }
401
+ if (mayHaveImage.match(/^data:/)) {
402
+ toUpload.push(mayHaveImage);
403
+ return `${key}${toUpload.length - 1}`;
404
+ }
405
+ return mayHaveImage;
406
+ case 'object':
407
+ const isArray = Array.isArray(mayHaveImage);
408
+ const withImagesUrl = isArray
409
+ ? [...mayHaveImage]
410
+ : { ...mayHaveImage };
411
+ for (const key of Object.keys(withImagesUrl)) {
412
+ withImagesUrl[key] = searchImages(withImagesUrl[key], uploaded);
413
+ }
414
+ return withImagesUrl;
415
+ default:
416
+ return mayHaveImage;
417
+ }
418
+ };
419
+
420
+ const searching = searchImages(original);
421
+
422
+ if (toUpload.length === 0) return original;
423
+
424
+ const uploaded = await this.uploadFiles(toUpload, workspaceId);
425
+
426
+ const replaced = searchImages(
427
+ searching,
428
+ uploaded.map(({ url }) => url)
429
+ );
430
+
431
+ return replaced;
432
+ }
433
+
434
+ async callAutomation(workspaceId: string, automation: string): Promise<any> {
435
+ return this._fetch(`/workspaces/${workspaceId}/webhooks/${automation}`);
436
+ }
437
+ }
438
+
439
+ export default new Api('https://api.eda.prisme.ai');
@@ -0,0 +1,57 @@
1
+ import getConfig from 'next/config';
2
+ import Events from './events';
3
+ import io from 'socket.io-client';
4
+
5
+ const { publicRuntimeConfig } = getConfig();
6
+
7
+ jest.mock('socket.io-client', () => {
8
+ const mock = {
9
+ disconnect: jest.fn(),
10
+ onAny: jest.fn(),
11
+ offAny: jest.fn(),
12
+ once: jest.fn(),
13
+ };
14
+ const io = jest.fn(() => mock);
15
+ return io;
16
+ });
17
+
18
+ it('should connect to Websocket', () => {
19
+ const client = new Events('1', 'abcde');
20
+ expect(io).toHaveBeenCalledWith(
21
+ `https://api.eda.prisme.ai/workspaces/1/events`,
22
+ {
23
+ extraHeaders: {
24
+ 'x-prismeai-session-token': 'abcde',
25
+ },
26
+ }
27
+ );
28
+ });
29
+
30
+ it('should disconnect to Websocket', () => {
31
+ const client = new Events('1', 'abcde');
32
+ (client as any).client.connected = true;
33
+ client.destroy();
34
+ expect(io().disconnect).toHaveBeenCalled();
35
+ });
36
+
37
+ it('should wait before disconnecting Websocket', () => {
38
+ const client = new Events('1', 'abcde');
39
+ const ioInstance = io();
40
+ (client as any).client.connected = false;
41
+ ((client as any).client.once as jest.Mock).mockClear();
42
+ ioInstance.disconnect = jest.fn();
43
+ client.destroy();
44
+ expect(ioInstance.disconnect).not.toHaveBeenCalled();
45
+ expect((client as any).client.once).toHaveBeenCalled();
46
+ ((client as any).client.once as jest.Mock).mock.calls[0][1]();
47
+ expect(ioInstance.disconnect).toHaveBeenCalled();
48
+ });
49
+
50
+ it('should listen to all events', () => {
51
+ const client = new Events('1', 'abcde');
52
+ const listener = () => null;
53
+ const off = client.all(listener);
54
+ expect(io().onAny).toHaveBeenCalledWith(listener);
55
+ off();
56
+ expect(io().offAny).toHaveBeenCalledWith(listener);
57
+ });
package/lib/events.ts ADDED
@@ -0,0 +1,69 @@
1
+ import io, { Socket } from 'socket.io-client';
2
+
3
+ export class Events {
4
+ protected client: Socket;
5
+ public workspaceId: string;
6
+
7
+ constructor(
8
+ workspaceId: string,
9
+ token: string,
10
+ apiHost: string = 'https://api.eda.prisme.ai'
11
+ ) {
12
+ this.workspaceId = workspaceId;
13
+ this.client = io(`${apiHost}/workspaces/${workspaceId}/events`, {
14
+ extraHeaders: {
15
+ 'x-prismeai-session-token': token,
16
+ },
17
+ });
18
+ }
19
+
20
+ get socket() {
21
+ return this.client;
22
+ }
23
+
24
+ destroy() {
25
+ this.workspaceId = '';
26
+
27
+ if (this.client.connected) {
28
+ this.client.disconnect();
29
+ return;
30
+ }
31
+ this.client.once('connect', () => {
32
+ this.client.disconnect();
33
+ });
34
+ }
35
+
36
+ all(listener: (eventName: string, eventData: Prismeai.PrismeEvent) => void) {
37
+ this.client.onAny(listener);
38
+
39
+ return () => this.client.offAny(listener);
40
+ }
41
+
42
+ on(
43
+ ev: string,
44
+ listener: (eventName: string, eventData: Prismeai.PrismeEvent) => void
45
+ ) {
46
+ this.client.on(ev, listener);
47
+ return () => this.client.off(ev, listener);
48
+ }
49
+
50
+ emit(event: string, payload?: any) {
51
+ this.client.emit('event', {
52
+ type: event,
53
+ payload,
54
+ });
55
+ }
56
+
57
+ once(
58
+ ev: string,
59
+ listener: (eventName: string, eventData: Prismeai.PrismeEvent) => void
60
+ ) {
61
+ this.client.once(ev, listener);
62
+ }
63
+
64
+ close() {
65
+ this.client.close();
66
+ }
67
+ }
68
+
69
+ export default Events;