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.
- package/CHANGELOG.md +345 -0
- package/LICENSE.md +37 -0
- package/README.md +134 -0
- package/dist/browser/AckExtension-e67c6a28.js +129 -0
- package/dist/browser/AckExtension-e67c6a28.js.map +1 -0
- package/dist/browser/ReloadExtension-b1e50033.js +253 -0
- package/dist/browser/ReloadExtension-b1e50033.js.map +1 -0
- package/dist/browser/cometd-eeabdcd4.js +3438 -0
- package/dist/browser/cometd-eeabdcd4.js.map +1 -0
- package/dist/browser/epicenter-2cce2971.js +6086 -0
- package/dist/browser/epicenter-2cce2971.js.map +1 -0
- package/dist/browser/epicenter.js +2 -0
- package/dist/browser/epicenter.js.map +1 -0
- package/dist/cjs/AckExtension-f5178e19.js +131 -0
- package/dist/cjs/AckExtension-f5178e19.js.map +1 -0
- package/dist/cjs/ReloadExtension-65b036ba.js +255 -0
- package/dist/cjs/ReloadExtension-65b036ba.js.map +1 -0
- package/dist/cjs/cometd-473408f4.js +3441 -0
- package/dist/cjs/cometd-473408f4.js.map +1 -0
- package/dist/cjs/epicenter-12ceb814.js +7248 -0
- package/dist/cjs/epicenter-12ceb814.js.map +1 -0
- package/dist/cjs/epicenter.js +54 -0
- package/dist/cjs/epicenter.js.map +1 -0
- package/dist/epicenter.js +9895 -0
- package/dist/epicenter.js.map +1 -0
- package/dist/epicenter.min.js +2 -0
- package/dist/epicenter.min.js.map +1 -0
- package/dist/module/AckExtension-6181d8b5.js +129 -0
- package/dist/module/AckExtension-6181d8b5.js.map +1 -0
- package/dist/module/ReloadExtension-eaa8c42c.js +253 -0
- package/dist/module/ReloadExtension-eaa8c42c.js.map +1 -0
- package/dist/module/cometd-af78008d.js +3438 -0
- package/dist/module/cometd-af78008d.js.map +1 -0
- package/dist/module/epicenter-9b8c92a9.js +7213 -0
- package/dist/module/epicenter-9b8c92a9.js.map +1 -0
- package/dist/module/epicenter.js +7 -0
- package/dist/module/epicenter.js.map +1 -0
- package/dist/types/adapters/account.d.ts +44 -0
- package/dist/types/adapters/admin.d.ts +33 -0
- package/dist/types/adapters/asset.d.ts +46 -0
- package/dist/types/adapters/authentication.d.ts +62 -0
- package/dist/types/adapters/channel.d.ts +39 -0
- package/dist/types/adapters/chat.d.ts +105 -0
- package/dist/types/adapters/cometd.d.ts +25 -0
- package/dist/types/adapters/email.d.ts +86 -0
- package/dist/types/adapters/episode.d.ts +91 -0
- package/dist/types/adapters/group.d.ts +273 -0
- package/dist/types/adapters/index.d.ts +21 -0
- package/dist/types/adapters/leaderboard.d.ts +68 -0
- package/dist/types/adapters/presence.d.ts +35 -0
- package/dist/types/adapters/project.d.ts +99 -0
- package/dist/types/adapters/recaptcha.d.ts +1 -0
- package/dist/types/adapters/run.d.ts +253 -0
- package/dist/types/adapters/task.d.ts +154 -0
- package/dist/types/adapters/time.d.ts +2 -0
- package/dist/types/adapters/user.d.ts +38 -0
- package/dist/types/adapters/vault.d.ts +94 -0
- package/dist/types/adapters/world.d.ts +230 -0
- package/dist/types/epicenter.d.ts +10 -0
- package/dist/types/utils/config.d.ts +90 -0
- package/dist/types/utils/constants.d.ts +290 -0
- package/dist/types/utils/cookies.d.ts +16 -0
- package/dist/types/utils/error-manager.d.ts +21 -0
- package/dist/types/utils/error.d.ts +4 -0
- package/dist/types/utils/fault.d.ts +17 -0
- package/dist/types/utils/helpers.d.ts +4 -0
- package/dist/types/utils/identification.d.ts +47 -0
- package/dist/types/utils/index.d.ts +11 -0
- package/dist/types/utils/result.d.ts +6 -0
- package/dist/types/utils/router.d.ts +157 -0
- package/dist/types/utils/store.d.ts +31 -0
- package/package.json +103 -0
- package/src/adapters/account.ts +104 -0
- package/src/adapters/admin.ts +53 -0
- package/src/adapters/asset.ts +195 -0
- package/src/adapters/authentication.ts +173 -0
- package/src/adapters/channel.ts +83 -0
- package/src/adapters/chat.ts +186 -0
- package/src/adapters/cometd.ts +297 -0
- package/src/adapters/email.ts +146 -0
- package/src/adapters/episode.ts +163 -0
- package/src/adapters/group.ts +511 -0
- package/src/adapters/index.ts +43 -0
- package/src/adapters/leaderboard.ts +122 -0
- package/src/adapters/presence.ts +63 -0
- package/src/adapters/project.ts +123 -0
- package/src/adapters/recaptcha.ts +11 -0
- package/src/adapters/run.ts +726 -0
- package/src/adapters/task.ts +213 -0
- package/src/adapters/time.ts +36 -0
- package/src/adapters/user.ts +75 -0
- package/src/adapters/vault.ts +232 -0
- package/src/adapters/world.ts +412 -0
- package/src/epicenter.ts +96 -0
- package/src/globals.d.ts +16 -0
- package/src/utils/config.ts +168 -0
- package/src/utils/constants.ts +324 -0
- package/src/utils/cookies.ts +71 -0
- package/src/utils/error-manager.ts +66 -0
- package/src/utils/error.ts +9 -0
- package/src/utils/fault.ts +39 -0
- package/src/utils/helpers.ts +7 -0
- package/src/utils/identification.ts +128 -0
- package/src/utils/index.ts +11 -0
- package/src/utils/result.ts +15 -0
- package/src/utils/router.ts +547 -0
- 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
|
+
}
|