epicenter-libs 3.11.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +345 -0
  2. package/LICENSE.md +37 -0
  3. package/README.md +134 -0
  4. package/dist/browser/AckExtension-e67c6a28.js +129 -0
  5. package/dist/browser/AckExtension-e67c6a28.js.map +1 -0
  6. package/dist/browser/ReloadExtension-b1e50033.js +253 -0
  7. package/dist/browser/ReloadExtension-b1e50033.js.map +1 -0
  8. package/dist/browser/cometd-eeabdcd4.js +3438 -0
  9. package/dist/browser/cometd-eeabdcd4.js.map +1 -0
  10. package/dist/browser/epicenter-2cce2971.js +6086 -0
  11. package/dist/browser/epicenter-2cce2971.js.map +1 -0
  12. package/dist/browser/epicenter.js +2 -0
  13. package/dist/browser/epicenter.js.map +1 -0
  14. package/dist/cjs/AckExtension-f5178e19.js +131 -0
  15. package/dist/cjs/AckExtension-f5178e19.js.map +1 -0
  16. package/dist/cjs/ReloadExtension-65b036ba.js +255 -0
  17. package/dist/cjs/ReloadExtension-65b036ba.js.map +1 -0
  18. package/dist/cjs/cometd-473408f4.js +3441 -0
  19. package/dist/cjs/cometd-473408f4.js.map +1 -0
  20. package/dist/cjs/epicenter-12ceb814.js +7248 -0
  21. package/dist/cjs/epicenter-12ceb814.js.map +1 -0
  22. package/dist/cjs/epicenter.js +54 -0
  23. package/dist/cjs/epicenter.js.map +1 -0
  24. package/dist/epicenter.js +9895 -0
  25. package/dist/epicenter.js.map +1 -0
  26. package/dist/epicenter.min.js +2 -0
  27. package/dist/epicenter.min.js.map +1 -0
  28. package/dist/module/AckExtension-6181d8b5.js +129 -0
  29. package/dist/module/AckExtension-6181d8b5.js.map +1 -0
  30. package/dist/module/ReloadExtension-eaa8c42c.js +253 -0
  31. package/dist/module/ReloadExtension-eaa8c42c.js.map +1 -0
  32. package/dist/module/cometd-af78008d.js +3438 -0
  33. package/dist/module/cometd-af78008d.js.map +1 -0
  34. package/dist/module/epicenter-9b8c92a9.js +7213 -0
  35. package/dist/module/epicenter-9b8c92a9.js.map +1 -0
  36. package/dist/module/epicenter.js +7 -0
  37. package/dist/module/epicenter.js.map +1 -0
  38. package/dist/types/adapters/account.d.ts +44 -0
  39. package/dist/types/adapters/admin.d.ts +33 -0
  40. package/dist/types/adapters/asset.d.ts +46 -0
  41. package/dist/types/adapters/authentication.d.ts +62 -0
  42. package/dist/types/adapters/channel.d.ts +39 -0
  43. package/dist/types/adapters/chat.d.ts +105 -0
  44. package/dist/types/adapters/cometd.d.ts +25 -0
  45. package/dist/types/adapters/email.d.ts +86 -0
  46. package/dist/types/adapters/episode.d.ts +91 -0
  47. package/dist/types/adapters/group.d.ts +273 -0
  48. package/dist/types/adapters/index.d.ts +21 -0
  49. package/dist/types/adapters/leaderboard.d.ts +68 -0
  50. package/dist/types/adapters/presence.d.ts +35 -0
  51. package/dist/types/adapters/project.d.ts +99 -0
  52. package/dist/types/adapters/recaptcha.d.ts +1 -0
  53. package/dist/types/adapters/run.d.ts +253 -0
  54. package/dist/types/adapters/task.d.ts +154 -0
  55. package/dist/types/adapters/time.d.ts +2 -0
  56. package/dist/types/adapters/user.d.ts +38 -0
  57. package/dist/types/adapters/vault.d.ts +94 -0
  58. package/dist/types/adapters/world.d.ts +230 -0
  59. package/dist/types/epicenter.d.ts +10 -0
  60. package/dist/types/utils/config.d.ts +90 -0
  61. package/dist/types/utils/constants.d.ts +290 -0
  62. package/dist/types/utils/cookies.d.ts +16 -0
  63. package/dist/types/utils/error-manager.d.ts +21 -0
  64. package/dist/types/utils/error.d.ts +4 -0
  65. package/dist/types/utils/fault.d.ts +17 -0
  66. package/dist/types/utils/helpers.d.ts +4 -0
  67. package/dist/types/utils/identification.d.ts +47 -0
  68. package/dist/types/utils/index.d.ts +11 -0
  69. package/dist/types/utils/result.d.ts +6 -0
  70. package/dist/types/utils/router.d.ts +157 -0
  71. package/dist/types/utils/store.d.ts +31 -0
  72. package/package.json +103 -0
  73. package/src/adapters/account.ts +104 -0
  74. package/src/adapters/admin.ts +53 -0
  75. package/src/adapters/asset.ts +195 -0
  76. package/src/adapters/authentication.ts +173 -0
  77. package/src/adapters/channel.ts +83 -0
  78. package/src/adapters/chat.ts +186 -0
  79. package/src/adapters/cometd.ts +297 -0
  80. package/src/adapters/email.ts +146 -0
  81. package/src/adapters/episode.ts +163 -0
  82. package/src/adapters/group.ts +511 -0
  83. package/src/adapters/index.ts +43 -0
  84. package/src/adapters/leaderboard.ts +122 -0
  85. package/src/adapters/presence.ts +63 -0
  86. package/src/adapters/project.ts +123 -0
  87. package/src/adapters/recaptcha.ts +11 -0
  88. package/src/adapters/run.ts +726 -0
  89. package/src/adapters/task.ts +213 -0
  90. package/src/adapters/time.ts +36 -0
  91. package/src/adapters/user.ts +75 -0
  92. package/src/adapters/vault.ts +232 -0
  93. package/src/adapters/world.ts +412 -0
  94. package/src/epicenter.ts +96 -0
  95. package/src/globals.d.ts +16 -0
  96. package/src/utils/config.ts +168 -0
  97. package/src/utils/constants.ts +324 -0
  98. package/src/utils/cookies.ts +71 -0
  99. package/src/utils/error-manager.ts +66 -0
  100. package/src/utils/error.ts +9 -0
  101. package/src/utils/fault.ts +39 -0
  102. package/src/utils/helpers.ts +7 -0
  103. package/src/utils/identification.ts +128 -0
  104. package/src/utils/index.ts +11 -0
  105. package/src/utils/result.ts +15 -0
  106. package/src/utils/router.ts +547 -0
  107. package/src/utils/store.ts +82 -0
@@ -0,0 +1,297 @@
1
+ import type { CometD, Message, SubscribeMessage, SubscriptionHandle } from 'cometd';
2
+ import type Channel from './channel';
3
+
4
+ import { EpicenterError, Fault, identification, isBrowser, errorManager, config } from '../utils';
5
+ import { channelsEnabled } from './project';
6
+
7
+ const AUTH_TOKEN_KEY = 'com.forio.epicenter.token';
8
+
9
+ const DISCONNECTED = 'disconnected';
10
+ const CONNECTED = 'connected';
11
+ const UNAUTHORIZED = 401;
12
+
13
+ interface ChannelUpdate {
14
+ data: string | Record<string, unknown>,
15
+ }
16
+
17
+ let cometdInstance: CometD | undefined;
18
+ class CometdAdapter {
19
+
20
+ url = '';
21
+ initialization = false;
22
+ subscriptions = new Map();
23
+
24
+ get cometd() {
25
+ if (!cometdInstance) {
26
+ throw new EpicenterError('Tried to get non-existent cometd');
27
+ }
28
+ return cometdInstance;
29
+ }
30
+ set cometd(instance) {
31
+ cometdInstance = instance;
32
+ }
33
+
34
+ async startup() {
35
+ const enabled = await channelsEnabled();
36
+ if (!enabled) throw new EpicenterError('Push Channels are not enabled on this project');
37
+
38
+ const { CometD } = await import('cometd');
39
+ const AckExtension = (await import('cometd/AckExtension')).default;
40
+ const ReloadExtension = (await import('cometd/ReloadExtension')).default;
41
+
42
+ this.cometd = new CometD();
43
+ const { apiProtocol, apiHost, apiVersion, accountShortName, projectShortName } = config;
44
+ const accountProject = (accountShortName && projectShortName) ?
45
+ `/${accountShortName}/${projectShortName}` :
46
+ '/epicenter/manager';
47
+ this.url = `${apiProtocol}://${apiHost}/push/v${apiVersion}${accountProject}/cometd`;
48
+
49
+ this.cometd.registerExtension('ack', new AckExtension());
50
+ if (isBrowser()) {
51
+ this.cometd.registerExtension('reload', new ReloadExtension());
52
+
53
+ window.onunload = () => {
54
+ if (this.cometd.getStatus() === CONNECTED) {
55
+ if (this.cometd.reload) this.cometd.reload();
56
+ const transport = this.cometd.getTransport();
57
+ if (transport) transport.abort();
58
+ }
59
+ };
60
+ }
61
+
62
+ // Current don't want to support CometD on node servers
63
+ // if (isNode()) {
64
+ // const { adapt } = await import('cometd-nodejs-client');
65
+ // adapt();
66
+ // }
67
+
68
+ this.cometd.configure({
69
+ url: this.url,
70
+ logLevel: 'debug',
71
+ });
72
+ return true;
73
+ }
74
+
75
+ async init() {
76
+ if (!this.initialization) {
77
+ this.initialization = await this.startup();
78
+ }
79
+ return this.initialization;
80
+ }
81
+
82
+ // Connects to CometD server
83
+ async handshake(options: { inert?: boolean } = {}) {
84
+ await this.init();
85
+
86
+ if (this.cometd.getStatus() !== DISCONNECTED) {
87
+ return Promise.resolve();
88
+ }
89
+
90
+ let handshakeProps = {};
91
+ const { session } = identification;
92
+
93
+ if (session) {
94
+ handshakeProps = {
95
+ ext: {
96
+ [AUTH_TOKEN_KEY]: session.token,
97
+ ack: true,
98
+ },
99
+ };
100
+ }
101
+
102
+ // TODO -- this was line in the old libs, don't know why; probably not needed so commenting out for now.
103
+ // this.cometd.ackEnabled = true;
104
+ this.cometd.websocketEnabled = true;
105
+ return new Promise((resolve, reject) => this.cometd.handshake(handshakeProps, (handshakeReply) => {
106
+ if (handshakeReply.successful) {
107
+ resolve(handshakeReply);
108
+ return;
109
+ }
110
+
111
+ const errorMessage = handshakeReply.error ?? '';
112
+ const error = new Fault({
113
+ status: errorMessage.includes('403') ? UNAUTHORIZED : undefined,
114
+ message: errorMessage,
115
+ information: {
116
+ code: 'COMETD_ERROR',
117
+ ...handshakeReply,
118
+ },
119
+ });
120
+
121
+ if (options.inert) {
122
+ reject(error);
123
+ return;
124
+ }
125
+
126
+ const retry = () => this.handshake({ inert: true });
127
+ try {
128
+ const result = errorManager.handle(error, retry);
129
+ resolve(result);
130
+ } catch (e) {
131
+ reject(e);
132
+ }
133
+ }));
134
+ }
135
+
136
+ async disconnect() {
137
+ if (!this.initialization) return Promise.resolve();
138
+
139
+ await this.init();
140
+ await this.empty();
141
+ if (this.cometd.getStatus() !== CONNECTED) return Promise.resolve();
142
+
143
+ return new Promise((resolve, reject) => this.cometd.disconnect((disconnectReply: Message) => {
144
+ if (!disconnectReply.successful) {
145
+ reject(new EpicenterError('Unable to disconnect from CometD server'));
146
+ } else {
147
+ resolve(undefined);
148
+ }
149
+ }));
150
+ }
151
+
152
+ async add(
153
+ channel: Channel | Channel[],
154
+ update: (data: unknown) => unknown,
155
+ options: { inert?: boolean } = {}
156
+ ): Promise<SubscriptionHandle | SubscriptionHandle[]> {
157
+ await this.init();
158
+ const channels = ([] as Channel[]).concat(channel);
159
+
160
+ if (this.cometd?.getStatus() !== CONNECTED) {
161
+ await this.handshake();
162
+ }
163
+ const { session } = identification;
164
+ const subscriptionProps = !session ? {} :
165
+ { ext: { [AUTH_TOKEN_KEY]: session.token } };
166
+
167
+ const handleCometdUpdate = ({ data }: ChannelUpdate) => {
168
+ // TODO -- figure out why there's ambiguity here and try to remove it
169
+ data = typeof data === 'string' ? JSON.parse(data) : data;
170
+ return update(data);
171
+ };
172
+
173
+ const promises: Promise<SubscriptionHandle>[] = [];
174
+ this.cometd.batch(() => channels.forEach(({ path }) => promises.push(new Promise((resolve, reject) => {
175
+ const subscription = this.cometd.subscribe(path, handleCometdUpdate, subscriptionProps, (subscribeReply: SubscribeMessage) => {
176
+ if (subscribeReply.successful) {
177
+ this.subscriptions.set(subscription.channel, subscription);
178
+ resolve(subscription);
179
+ return;
180
+ }
181
+
182
+ const errorMessage = subscribeReply.error ?? '';
183
+ const error = new Fault({
184
+ status: errorMessage.includes('403') ? UNAUTHORIZED : undefined,
185
+ message: errorMessage,
186
+ information: {
187
+ code: 'COMETD_ERROR',
188
+ ...subscribeReply,
189
+ },
190
+ });
191
+
192
+ if (options.inert) {
193
+ reject(error);
194
+ return;
195
+ }
196
+
197
+ const retry = () => this.add(channel, update, { inert: true });
198
+ try {
199
+ const result = errorManager.handle<SubscriptionHandle | SubscriptionHandle[]>(error, retry);
200
+ const sub = Array.isArray(result) ? result[0] : result;
201
+ resolve(sub);
202
+ } catch (e) {
203
+ reject(e);
204
+ }
205
+ });
206
+ }))));
207
+ return promises.length === 1 ?
208
+ Promise.all(promises).then(([res]) => res) :
209
+ Promise.all(promises);
210
+ }
211
+
212
+ async publish(
213
+ channel: Channel | Channel[],
214
+ content: Record<string, unknown>,
215
+ options: { inert?: boolean } = {}
216
+ ) {
217
+ await this.init();
218
+ const channels = ([] as Channel[]).concat(channel);
219
+
220
+ if (this.cometd.getStatus() !== CONNECTED) {
221
+ await this.handshake();
222
+ }
223
+ const { session } = identification;
224
+ const publishProps = {
225
+ ext: session ? { [AUTH_TOKEN_KEY]: session.token } : undefined,
226
+ };
227
+ const promises: Promise<Message>[] = [];
228
+ this.cometd.batch(() => channels.forEach(({ path }) => promises.push(new Promise((resolve, reject) => {
229
+ this.cometd.publish(path, content, publishProps, (publishReply: Message) => {
230
+ if (publishReply.successful) {
231
+ resolve(publishReply);
232
+ return;
233
+ }
234
+
235
+ const errorMessage = publishReply.error ?? '';
236
+ const error = new Fault({
237
+ status: errorMessage.includes('403') ? UNAUTHORIZED : undefined,
238
+ message: errorMessage,
239
+ information: {
240
+ code: 'COMETD_ERROR',
241
+ ...publishReply,
242
+ },
243
+ });
244
+
245
+ if (options.inert) {
246
+ reject(error);
247
+ return;
248
+ }
249
+
250
+ const retry = () => this.publish(channel, content, { inert: true });
251
+ try {
252
+ const result = errorManager.handle<Message | Message[]>(error, retry);
253
+ const message = Array.isArray(result) ? result[0] : result;
254
+ resolve(message);
255
+ } catch (e) {
256
+ reject(e);
257
+ }
258
+ });
259
+ }))));
260
+ return promises.length === 1 ?
261
+ Promise.all(promises).then(([res]) => res) :
262
+ Promise.all(promises);
263
+ }
264
+
265
+ async remove(subscription: SubscriptionHandle) {
266
+ await this.init();
267
+ this.subscriptions.delete(subscription.channel);
268
+ return new Promise((resolve, reject) => this.cometd.unsubscribe(subscription, (unsubscribeReply: Message) => {
269
+ if (unsubscribeReply.successful) {
270
+ resolve(unsubscribeReply);
271
+ }
272
+ const errorMessage = unsubscribeReply.error ?? '';
273
+ const error = new Fault({
274
+ status: errorMessage.includes('403') ? UNAUTHORIZED : undefined,
275
+ message: errorMessage,
276
+ information: {
277
+ code: 'COMETD_ERROR',
278
+ ...unsubscribeReply,
279
+ },
280
+ });
281
+ reject(error);
282
+ /* Not using error handling here yet -- should we? */
283
+ }));
284
+ }
285
+
286
+ async empty() {
287
+ await this.init();
288
+ const promises: Promise<unknown>[] = [];
289
+ this.cometd.batch(() => this.subscriptions.forEach((subscription) => {
290
+ promises.push(this.remove(subscription));
291
+ }));
292
+ return Promise.all(promises);
293
+ }
294
+ }
295
+ const cometdAdapter = new CometdAdapter();
296
+ export default cometdAdapter;
297
+
@@ -0,0 +1,146 @@
1
+ import type { RoutingOptions } from '../utils/router';
2
+ import { Router } from 'utils/index';
3
+
4
+ enum ENCODING {
5
+ HEX = 'HEX',
6
+ BASE_64 = 'BASE_64',
7
+ }
8
+
9
+ /**
10
+ * Sends an email to an individual user; available to participants
11
+ * @example
12
+ * //Sends an email with a smiley face png attachment
13
+ * const groupKey = epicenter.authAdapter.getLocalSession().groupKey;
14
+ * const subject = "check out this drawing!"
15
+ * const body = "I hope you enjoy this smiley face!"
16
+ * const attachements = [{
17
+ * encoding: 'BASE_64',
18
+ * name: 'testPic',
19
+ * contentType: 'image/png',
20
+ * data: 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAIxJREFUWEdjZBhgwDjA9jMQ5YAvzmb/efaewquWGDXYPDvqgNEQGA2B0RAYGiFAy+KaqBAYdcDICgFQtQryMaHql1qhgjURklu3IzuKWDNwOoCSUCAlFHFmQ2J9gB4VpFgO0kvVZhaplhPlAJgPCSVKciwn6ACY5TDD8aV8Qg7EpXe0KB4NgdEQGA0BAF7VgCFTeobfAAAAAElFTkSuQmCC',
21
+ * }];
22
+ *
23
+ * // Sends an email to the address associated with the provided user key, sender will be seen as "System"
24
+ * epicenter.emailAdapter.sendEmail(groupKey, '000001796733eef0842f4d6d960997018a3b', subject, body, { attachments });
25
+ * // Sends an email to the address associated with the provided user key, sender will be seen as be the address provided by the "from" field
26
+ * epicenter.emailAdapter.sendEmail(groupKey, '000001796733eef0842f4d6d960997018a3b', subject, body, { attachments, from: 'sender@test.com' });
27
+ * // Sends an email to the address defined at "replyTo", sender will be seen as be the address provided by the "from" field
28
+ * epicenter.emailAdapter.sendEmail(groupKey, '000001796733eef0842f4d6d960997018a3b', subject, body, { attachments, from: 'sender@test.com', replyTo: 'receiver@test.com' });
29
+ * // Sends an email to the address associated with the provided user key, sender will be seen as the user associated with the "fromUserKey"
30
+ * epicenter.emailAdapter.sendEmail(groupKey, '000001796733eef0842f4d6d960997018a3b', subject, body, { attachments, fromUserKey: '000001796733eef0842f4d6d960997018a33' });
31
+ * @param groupKey The groupKey in which the email target user exists
32
+ * @param userKey The unique userKey for the email target user
33
+ * @param subject The subject line for the email.
34
+ * @param emailBody The body for the email
35
+ * @param [optionals] Optional parameters
36
+ * @param [optionals.familyNameFirst] Specifies whether email target's family name will come before their given name. Defaults to false.
37
+ * @param [optionals.html] Whether to treat the body as HTML (true) or as plain text (false). Defaults to false.
38
+ * @param [optionals.body] The content of the email.
39
+ * @param [optionals.from] The email address from which the message will appear to have been sent from. Will be overriden by fromUserKey.
40
+ * @param [optionals.replyTo] The email address that will be replyed to by the recipient. Must be used in conjunction with optionals.from.
41
+ * @param [optionals.fromUserKey] The userKey from which the email will appear to have been sent. The default response address will also be this email.
42
+ * @param [optionals.attachments] An array of (binary) objects to include as attachments. All four properties must be included.
43
+ * @param [optionals.attachments[].encoding] A string specifying the encoding method. See ENCODING for possible values.
44
+ * @param [optionals.attachments[].data] A string containing the data for the attachment.
45
+ * @param [optionals.attachments[].name] A string containing the name of the attachment.
46
+ * @param [optionals.attachments[].contentType] A string specifying the attachment MIME Type.
47
+ * @returns undefined indicating success
48
+ */
49
+ export async function sendEmail(
50
+ groupKey: string,
51
+ userKey: string,
52
+ subject: string,
53
+ emailBody: string,
54
+ optionals: {
55
+ familyNameFirst?: string,
56
+ html?: boolean,
57
+ body?: string,
58
+ from?: string,
59
+ replyTo?: string,
60
+ fromUserKey?: string,
61
+ attachments?: {
62
+ encoding: keyof typeof ENCODING,
63
+ data: string,
64
+ name: string,
65
+ contentType: string,
66
+ }[]
67
+ } & RoutingOptions = {}
68
+ ): Promise<void> {
69
+ const { accountShortName, projectShortName, server, familyNameFirst, html, attachments, from, replyTo, fromUserKey } =
70
+ optionals;
71
+
72
+ let fromString = '';
73
+ if (fromUserKey) {
74
+ fromString += `/as/${fromUserKey}`;
75
+ } else if (from) {
76
+ fromString += `/from/${from}`;
77
+ if (replyTo) {
78
+ fromString += `/${replyTo}`;
79
+ }
80
+ }
81
+
82
+ return await new Router()
83
+ .withServer(server)
84
+ .withAccountShortName(accountShortName)
85
+ .withProjectShortName(projectShortName)
86
+ .post(`/email/user/${groupKey}/${userKey}${fromString}`, {
87
+ body: {
88
+ subject,
89
+ body: emailBody,
90
+ familyNameFirst,
91
+ html,
92
+ attachments,
93
+ },
94
+ })
95
+ .then(({ body }) => body);
96
+ }
97
+
98
+ /**
99
+ * Sends an email to an individual admin (someone with epicenter account); Requires support level authentication
100
+ * @param adminKey The unique adminKey for the email target
101
+ * @param subject The subject line for the email.
102
+ * @param [optionals] Optional parameter
103
+ * @param [optionals.familyNameFirst] Specifies whether email target's family name will come before their given name. Defaults to false.
104
+ * @param [optionals.html] Whether to treat the body as HTML (true) or as plain text (false). Defaults to false.
105
+ * @param [optionals.body] The content of the email.
106
+ * @param [optionals.attachments] An array of (binary) objects to include as attachments. All four properties must be included.
107
+ * @param [optionals.attachments[].encoding] A string specifying the encoding method. See ENCODING for possible values.
108
+ * @param [optionals.attachments[].data] A string containing the data for the attachment.
109
+ * @param [optionals.attachments[].name] A string containing the name of the attachment.
110
+ * @param [optionals.attachments[].contentType] A string specifying the attachment MIME Type.
111
+ * @returns undefined indicating success
112
+ */
113
+ export async function sendEmailToAdmin(
114
+ adminKey: string,
115
+ subject: string,
116
+ emailBody: string,
117
+ optionals: {
118
+ familyNameFirst?: string,
119
+ html?: boolean,
120
+ body?: string,
121
+ attachments?: {
122
+ encoding: keyof typeof ENCODING,
123
+ data: string,
124
+ name: string,
125
+ contentType: string,
126
+ }
127
+ } & RoutingOptions = {}
128
+ ): Promise<void> {
129
+ const { accountShortName, projectShortName, server, familyNameFirst, html, attachments } =
130
+ optionals;
131
+
132
+ return await new Router()
133
+ .withServer(server)
134
+ .withAccountShortName(accountShortName)
135
+ .withProjectShortName(projectShortName)
136
+ .post(`/email/admin/${adminKey}`, {
137
+ body: {
138
+ subject,
139
+ body: emailBody,
140
+ familyNameFirst,
141
+ html,
142
+ attachments,
143
+ },
144
+ })
145
+ .then(({ body }) => body);
146
+ }
@@ -0,0 +1,163 @@
1
+ import type { UserSession } from 'utils/identification';
2
+ import type { RoutingOptions, Page, GenericSearchOptions } from 'utils/router';
3
+ import { identification, Router } from 'utils';
4
+
5
+
6
+ interface Episode {
7
+ name: string,
8
+ episodeKey: string,
9
+ }
10
+
11
+ /**
12
+ * Create an episode.
13
+ * @example
14
+ * epicenter.episodeAdapter.create('myEpisode', 'myGroupName', {
15
+ * runLimit: 20,
16
+ * draft: true,
17
+ * });
18
+ * @param name Episode name
19
+ * @param groupName Group to make the episode under
20
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
21
+ * @param [optionals.draft] Flag to indicate the episode is a draft (intended when used for settings scoping)
22
+ * @param [optionals.runLimit] Optional argument to define the number of runs that can be made using this episode as the scope
23
+ * @returns promise that resolves to the newly created episode
24
+ */
25
+ export async function create(
26
+ name: string,
27
+ groupName: string,
28
+ optionals: {
29
+ draft?: boolean,
30
+ runLimit?: number,
31
+ } & RoutingOptions = {}
32
+ ): Promise<Episode> {
33
+ const {
34
+ draft, runLimit,
35
+ ...routingOptions
36
+ } = optionals;
37
+ return await new Router()
38
+ .post(`/episode/${groupName}`, {
39
+ body: { name, draft, runLimit },
40
+ ...routingOptions,
41
+ }).then(({ body }) => body);
42
+ }
43
+
44
+ /**
45
+ * Gets a specific episode.
46
+ * @example
47
+ * epicenter.episodeAdapter.get('000001796733eef0842f4d6d960997018a37');
48
+ *
49
+ * @param episodeKey The episode key
50
+ * @param [optionals] Optional arguments; pass network call options overrides here.
51
+ * @returns promise that resolves to an episode
52
+ */
53
+ export async function get(
54
+ episodeKey: string,
55
+ optionals: RoutingOptions = {}
56
+ ): Promise<Episode> {
57
+ return await new Router()
58
+ .get(`/episode/${episodeKey}`, optionals)
59
+ .then(({ body }) => body);
60
+ }
61
+
62
+ /**
63
+ * Gets episodes.
64
+ * @example
65
+ * const filter = [
66
+ * 'name|=one|two', // searches only for episodes named 'one' or 'two'
67
+ * 'draft=false', // searches only for episodes that aren't drafts
68
+ * 'created>=2022-01-03T20:30:53.054Z', // looks for any episodes created after Jan 3rd 2022
69
+ * // 'account.shortName=acme' // specifies the account, intended for admin use
70
+ * // 'project.shortName=simulations' // specifies the project, intended for admin use
71
+ * // 'group.name=my-group-name', // specifies a group name, intended for admin use
72
+ * // 'group.groupKey=0000017dd3bf540e5ada5b1e058f08f20461', // specifies a group key, intended for admin use
73
+ * ];
74
+ * epicenter.episodeAdapter.query({
75
+ * filter,
76
+ * sort: ['+episode.created'], // sort all findings by the 'created' field (ascending)
77
+ * first: 3, // page should start with the 4th item found (will default to 0)
78
+ * max: 10, // page should only include the first 10 items
79
+ * });
80
+ * @param searchOptions Search options for the query
81
+ * @param [searchOptions.filter] Filters for searching
82
+ * @param [searchOptions.sort] Sorting criteria
83
+ * @param [searchOptions.first] The starting index of the page returned
84
+ * @param [searchOptions.max] The number of entries per page
85
+ * @param [optionals] Optional arguments; pass network call options overrides here.
86
+ * @returns promise that resolves to a page of episodes
87
+ */
88
+ export async function query(
89
+ searchOptions: GenericSearchOptions,
90
+ optionals: RoutingOptions = {}
91
+ ): Promise<Page<Episode>> {
92
+ const { filter = [], sort = [], first = 0, max } = searchOptions;
93
+
94
+ return await new Router()
95
+ .withSearchParams({
96
+ filter: filter.join(';') || undefined,
97
+ sort: sort.join(';') || undefined,
98
+ first, max,
99
+ })
100
+ .get('/episode/search', {
101
+ paginated: true,
102
+ ...optionals,
103
+ })
104
+ .then(({ body }) => body);
105
+ }
106
+
107
+ /**
108
+ * Gets episodes based on a group key
109
+ * @example
110
+ * epicenter.episodeAdapter.withGroup('0000017dd3bf540e5ada5b1e058f08f20461');
111
+ *
112
+ * @param groupKey The group key
113
+ * @param [optionals] Optional arguments; pass network call options overrides here.
114
+ * @returns promise resolving to a list of episodes
115
+ */
116
+ export async function forGroup(
117
+ groupKey: string,
118
+ optionals: RoutingOptions = {}
119
+ ): Promise<Episode[]> {
120
+ return await new Router()
121
+ .get(`/episode/in/${groupKey}`, optionals)
122
+ .then(({ body }) => body);
123
+ }
124
+
125
+ /**
126
+ * Gets episode based on group name and episode name
127
+ * @example
128
+ * epicenter.episodeAdapter.withName('myEpisodeName');
129
+ * @param name The episode name
130
+ * @param [optionals] Optional arguments; pass network call options overrides here. Special arguments specific to this method are listed below if they exist.
131
+ * @param [optionals.groupName] Name of the group, if omitted will use the group name associated with the current session
132
+ * @returns promise that resolves to an episode
133
+ */
134
+ export async function withName(
135
+ name: string,
136
+ optionals: { groupName?: string } & RoutingOptions = {}
137
+ ): Promise<Episode> {
138
+ const {
139
+ groupName,
140
+ ...routingOptions
141
+ } = optionals;
142
+
143
+ const session = identification.session as UserSession;
144
+ return await new Router()
145
+ .get(`/episode/with/${groupName ?? session?.groupName}/${name}`, routingOptions)
146
+ .then(({ body }) => body);
147
+ }
148
+
149
+ /**
150
+ * Deletes an episode
151
+ * @example
152
+ * epicenter.episodeAdapter.remove('000001796733eef0842f4d6d960997018a3b');
153
+ * @param [optionals] Optional arguments; pass network call options overrides here.
154
+ * @returns promise that resolves to undefined if successful
155
+ */
156
+ export async function remove(
157
+ episodeKey: string,
158
+ optionals: RoutingOptions = {}
159
+ ): Promise<void> {
160
+ return await new Router()
161
+ .delete(`/episode/${episodeKey}`, optionals)
162
+ .then(({ body }) => body);
163
+ }