@suprsend/node-sdk 0.1.0 → 1.0.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/dist/attachment.js +26 -0
- package/dist/bulk_response.js +60 -0
- package/dist/constants.js +40 -0
- package/dist/event.js +154 -85
- package/dist/events_bulk.js +424 -0
- package/dist/index.js +89 -39
- package/dist/request_json/event.json +5 -0
- package/dist/request_json/workflow.json +116 -9
- package/dist/{identity.js → subscriber.js} +195 -50
- package/dist/{identity_helper.js → subscriber_helper.js} +191 -59
- package/dist/subscribers_bulk.js +447 -0
- package/dist/utils.js +210 -1
- package/dist/workflow.js +124 -59
- package/dist/workflows_bulk.js +424 -0
- package/package.json +7 -4
- package/src/attachment.js +12 -0
- package/src/bulk_response.js +35 -0
- package/src/constants.js +28 -0
- package/src/event.js +118 -75
- package/src/events_bulk.js +234 -0
- package/src/index.js +67 -33
- package/src/request_json/event.json +5 -0
- package/src/request_json/workflow.json +116 -9
- package/src/{identity.js → subscriber.js} +118 -34
- package/src/{identity_helper.js → subscriber_helper.js} +196 -53
- package/src/subscribers_bulk.js +235 -0
- package/src/utils.js +136 -0
- package/src/workflow.js +94 -45
- package/src/workflows_bulk.js +234 -0
- package/dist/config.js +0 -13
- package/src/config.js +0 -6
|
@@ -7,22 +7,71 @@ import {
|
|
|
7
7
|
is_string,
|
|
8
8
|
} from "./utils";
|
|
9
9
|
|
|
10
|
+
// ---------- Identity keys
|
|
11
|
+
const IDENT_KEY_EMAIL = "$email";
|
|
12
|
+
const IDENT_KEY_SMS = "$sms";
|
|
13
|
+
const IDENT_KEY_ANDROIDPUSH = "$androidpush";
|
|
14
|
+
const IDENT_KEY_IOSPUSH = "$iospush";
|
|
15
|
+
const IDENT_KEY_WHATSAPP = "$whatsapp";
|
|
16
|
+
const IDENT_KEY_WEBPUSH = "$webpush";
|
|
17
|
+
const IDENT_KEY_SLACK = "$slack";
|
|
18
|
+
|
|
19
|
+
const IDENT_KEYS_ALL = [
|
|
20
|
+
IDENT_KEY_EMAIL,
|
|
21
|
+
IDENT_KEY_SMS,
|
|
22
|
+
IDENT_KEY_ANDROIDPUSH,
|
|
23
|
+
IDENT_KEY_IOSPUSH,
|
|
24
|
+
IDENT_KEY_WHATSAPP,
|
|
25
|
+
IDENT_KEY_WEBPUSH,
|
|
26
|
+
IDENT_KEY_SLACK,
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const KEY_PUSHVENDOR = "$pushvendor";
|
|
30
|
+
|
|
31
|
+
const OTHER_RESERVED_KEYS = [
|
|
32
|
+
"$messenger",
|
|
33
|
+
"$inbox",
|
|
34
|
+
KEY_PUSHVENDOR,
|
|
35
|
+
"$device_id",
|
|
36
|
+
"$insert_id",
|
|
37
|
+
"$time",
|
|
38
|
+
"$set",
|
|
39
|
+
"$set_once",
|
|
40
|
+
"$add",
|
|
41
|
+
"$append",
|
|
42
|
+
"$remove",
|
|
43
|
+
"$unset",
|
|
44
|
+
"$identify",
|
|
45
|
+
"$anon_id",
|
|
46
|
+
"$identified_id",
|
|
47
|
+
"$notification_delivered",
|
|
48
|
+
"$notification_dismiss",
|
|
49
|
+
"$notification_clicked",
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const SUPER_PROPERTY_KEYS = [
|
|
53
|
+
"$app_version_string",
|
|
54
|
+
"$app_build_number",
|
|
55
|
+
"$brand",
|
|
56
|
+
"$carrier",
|
|
57
|
+
"$manufacturer",
|
|
58
|
+
"$model",
|
|
59
|
+
"$os",
|
|
60
|
+
"$ss_sdk_version",
|
|
61
|
+
"$insert_id",
|
|
62
|
+
"$time",
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const ALL_RESERVED_KEYS = [
|
|
66
|
+
...SUPER_PROPERTY_KEYS,
|
|
67
|
+
...OTHER_RESERVED_KEYS,
|
|
68
|
+
...IDENT_KEYS_ALL,
|
|
69
|
+
];
|
|
70
|
+
|
|
10
71
|
const EMAIL_REGEX = /\S+@\S+\.\S+/;
|
|
11
72
|
const MOBILE_REGEX = /^\+[0-9\s]+/;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
EMAIL: "$email",
|
|
15
|
-
SMS: "$sms",
|
|
16
|
-
WHATSAPP: "$whatsapp",
|
|
17
|
-
ANDROID_PUSH: "$androidpush",
|
|
18
|
-
IOS_PUSH: "$iospush",
|
|
19
|
-
WEB_PUSH: "$webpush",
|
|
20
|
-
};
|
|
21
|
-
const ANDROID_PUSH_VENDORS = ["fcm", "xiaomi", "oppo"];
|
|
22
|
-
const IOS_PUSH_VENDORS = ["apns"];
|
|
23
|
-
const WEB_PUSH_VENDORS = ["vapid"];
|
|
24
|
-
|
|
25
|
-
export default class _IdentityEventInternalHelper {
|
|
73
|
+
|
|
74
|
+
export default class _SubscriberInternalHelper {
|
|
26
75
|
constructor(distinct_id, workspace_key) {
|
|
27
76
|
this.distinct_id = distinct_id;
|
|
28
77
|
this.workspace_key = workspace_key;
|
|
@@ -69,7 +118,11 @@ export default class _IdentityEventInternalHelper {
|
|
|
69
118
|
}
|
|
70
119
|
|
|
71
120
|
__form_event() {
|
|
72
|
-
if (
|
|
121
|
+
if (
|
|
122
|
+
!is_empty(this.__dict_append) ||
|
|
123
|
+
!is_empty(this.__dict_remove) ||
|
|
124
|
+
!is_empty(this.__list_unset)
|
|
125
|
+
) {
|
|
73
126
|
let event = {
|
|
74
127
|
$insert_id: uuid(),
|
|
75
128
|
$time: epoch_milliseconds(),
|
|
@@ -84,9 +137,14 @@ export default class _IdentityEventInternalHelper {
|
|
|
84
137
|
event["$remove"] = this.__dict_remove;
|
|
85
138
|
this.__remove_count += 1;
|
|
86
139
|
}
|
|
140
|
+
if (!is_empty(this.__list_unset)) {
|
|
141
|
+
event["$unset"] = this.__list_unset;
|
|
142
|
+
this.__unset_count += 1;
|
|
143
|
+
}
|
|
87
144
|
return event;
|
|
145
|
+
} else {
|
|
146
|
+
return;
|
|
88
147
|
}
|
|
89
|
-
return;
|
|
90
148
|
}
|
|
91
149
|
|
|
92
150
|
__validate_key_basic(key, caller) {
|
|
@@ -105,7 +163,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
105
163
|
}
|
|
106
164
|
|
|
107
165
|
__validate_key_prefix(key, caller) {
|
|
108
|
-
if (!
|
|
166
|
+
if (!ALL_RESERVED_KEYS.includes(key)) {
|
|
109
167
|
if (has_special_char(key)) {
|
|
110
168
|
this.__info.push(
|
|
111
169
|
`[${caller}] skipping key: ${key}. key starting with [$,ss_] are reserved`
|
|
@@ -117,68 +175,79 @@ export default class _IdentityEventInternalHelper {
|
|
|
117
175
|
}
|
|
118
176
|
|
|
119
177
|
__is_identity_key(key) {
|
|
120
|
-
return
|
|
178
|
+
return IDENT_KEYS_ALL.includes(key);
|
|
121
179
|
}
|
|
122
180
|
|
|
123
181
|
_append_kv(key, value, args = {}, caller = "append") {
|
|
124
|
-
const [validated_key,
|
|
125
|
-
if (!
|
|
182
|
+
const [validated_key, is_k_valid] = this.__validate_key_basic(key, caller);
|
|
183
|
+
if (!is_k_valid) {
|
|
126
184
|
return;
|
|
127
185
|
}
|
|
128
186
|
if (this.__is_identity_key(validated_key)) {
|
|
129
187
|
this.__add_identity(validated_key, value, args, caller);
|
|
130
188
|
} else {
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
189
|
+
const is_k_valid = this.__validate_key_prefix(validated_key, caller);
|
|
190
|
+
if (is_k_valid) {
|
|
133
191
|
this.__dict_append[validated_key] = value;
|
|
134
192
|
}
|
|
135
193
|
}
|
|
136
194
|
}
|
|
137
195
|
|
|
138
196
|
_remove_kv(key, value, args = {}, caller = "remove") {
|
|
139
|
-
const [validated_key,
|
|
140
|
-
if (!
|
|
197
|
+
const [validated_key, is_k_valid] = this.__validate_key_basic(key, caller);
|
|
198
|
+
if (!is_k_valid) {
|
|
141
199
|
return;
|
|
142
200
|
}
|
|
143
201
|
if (this.__is_identity_key(validated_key)) {
|
|
144
202
|
this.__remove_identity(validated_key, value, args, caller);
|
|
145
203
|
} else {
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
204
|
+
const is_k_valid = this.__validate_key_prefix(validated_key, caller);
|
|
205
|
+
if (is_k_valid) {
|
|
148
206
|
this.__dict_remove[validated_key] = value;
|
|
149
207
|
}
|
|
150
208
|
}
|
|
151
209
|
}
|
|
152
210
|
|
|
211
|
+
_unset_k(key, caller = "unset") {
|
|
212
|
+
const [validated_key, is_k_valid] = this.__validate_key_basic(key, caller);
|
|
213
|
+
if (!is_k_valid) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
this.__list_unset.push(validated_key);
|
|
217
|
+
}
|
|
218
|
+
|
|
153
219
|
__add_identity(key, value, args, caller) {
|
|
154
220
|
switch (key) {
|
|
155
|
-
case
|
|
221
|
+
case IDENT_KEY_EMAIL:
|
|
156
222
|
this._add_email(value, caller);
|
|
157
223
|
break;
|
|
158
|
-
case
|
|
224
|
+
case IDENT_KEY_SMS:
|
|
159
225
|
this._add_sms(value, caller);
|
|
160
226
|
break;
|
|
161
|
-
case
|
|
227
|
+
case IDENT_KEY_WHATSAPP:
|
|
162
228
|
this._add_whatsapp(value, caller);
|
|
163
229
|
break;
|
|
164
|
-
case
|
|
230
|
+
case IDENT_KEY_ANDROIDPUSH:
|
|
165
231
|
this._add_androidpush(value, args[KEY_PUSHVENDOR], caller);
|
|
166
232
|
if (this.__dict_append[KEY_PUSHVENDOR]) {
|
|
167
233
|
args[KEY_PUSHVENDOR] = this.__dict_append[KEY_PUSHVENDOR];
|
|
168
234
|
}
|
|
169
235
|
break;
|
|
170
|
-
case
|
|
236
|
+
case IDENT_KEY_IOSPUSH:
|
|
171
237
|
this._add_iospush(value, args[KEY_PUSHVENDOR], caller);
|
|
172
238
|
if (this.__dict_append[KEY_PUSHVENDOR]) {
|
|
173
239
|
args[KEY_PUSHVENDOR] = this.__dict_append[KEY_PUSHVENDOR];
|
|
174
240
|
}
|
|
175
241
|
break;
|
|
176
|
-
case
|
|
242
|
+
case IDENT_KEY_WEBPUSH:
|
|
177
243
|
this._add_webpush(value, args[KEY_PUSHVENDOR], caller);
|
|
178
244
|
if (this.__dict_append[KEY_PUSHVENDOR]) {
|
|
179
245
|
args[KEY_PUSHVENDOR] = this.__dict_append[KEY_PUSHVENDOR];
|
|
180
246
|
}
|
|
181
247
|
break;
|
|
248
|
+
case IDENT_KEY_SLACK:
|
|
249
|
+
this._add_slack(value, caller);
|
|
250
|
+
break;
|
|
182
251
|
default:
|
|
183
252
|
break;
|
|
184
253
|
}
|
|
@@ -186,33 +255,36 @@ export default class _IdentityEventInternalHelper {
|
|
|
186
255
|
|
|
187
256
|
__remove_identity(key, value, args, caller) {
|
|
188
257
|
switch (key) {
|
|
189
|
-
case
|
|
258
|
+
case IDENT_KEY_EMAIL:
|
|
190
259
|
this._remove_email(value, caller);
|
|
191
260
|
break;
|
|
192
|
-
case
|
|
261
|
+
case IDENT_KEY_SMS:
|
|
193
262
|
this._remove_sms(value, caller);
|
|
194
263
|
break;
|
|
195
|
-
case
|
|
264
|
+
case IDENT_KEY_WHATSAPP:
|
|
196
265
|
this._remove_whatsapp(value, caller);
|
|
197
266
|
break;
|
|
198
|
-
case
|
|
267
|
+
case IDENT_KEY_ANDROIDPUSH:
|
|
199
268
|
this._remove_androidpush(value, args[KEY_PUSHVENDOR], caller);
|
|
200
269
|
if (this.__dict_remove[KEY_PUSHVENDOR]) {
|
|
201
270
|
args[KEY_PUSHVENDOR] = this.__dict_remove[KEY_PUSHVENDOR];
|
|
202
271
|
}
|
|
203
272
|
break;
|
|
204
|
-
case
|
|
273
|
+
case IDENT_KEY_IOSPUSH:
|
|
205
274
|
this._remove_iospush(value, args[KEY_PUSHVENDOR], caller);
|
|
206
275
|
if (this.__dict_remove[KEY_PUSHVENDOR]) {
|
|
207
276
|
args[KEY_PUSHVENDOR] = this.__dict_remove[KEY_PUSHVENDOR];
|
|
208
277
|
}
|
|
209
278
|
break;
|
|
210
|
-
case
|
|
279
|
+
case IDENT_KEY_WEBPUSH:
|
|
211
280
|
this._remove_webpush(value, args[KEY_PUSHVENDOR], caller);
|
|
212
281
|
if (this.__dict_remove[KEY_PUSHVENDOR]) {
|
|
213
282
|
args[KEY_PUSHVENDOR] = this.__dict_remove[KEY_PUSHVENDOR];
|
|
214
283
|
}
|
|
215
284
|
break;
|
|
285
|
+
case IDENT_KEY_SLACK:
|
|
286
|
+
this._remove_slack(val, caller);
|
|
287
|
+
break;
|
|
216
288
|
default:
|
|
217
289
|
break;
|
|
218
290
|
}
|
|
@@ -260,7 +332,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
260
332
|
if (!is_valid) {
|
|
261
333
|
return;
|
|
262
334
|
}
|
|
263
|
-
this.__dict_append[
|
|
335
|
+
this.__dict_append[IDENT_KEY_EMAIL] = value;
|
|
264
336
|
}
|
|
265
337
|
|
|
266
338
|
_remove_email(email, caller) {
|
|
@@ -268,7 +340,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
268
340
|
if (!is_valid) {
|
|
269
341
|
return;
|
|
270
342
|
}
|
|
271
|
-
this.__dict_remove[
|
|
343
|
+
this.__dict_remove[IDENT_KEY_EMAIL] = value;
|
|
272
344
|
}
|
|
273
345
|
|
|
274
346
|
// mobile methods
|
|
@@ -299,7 +371,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
299
371
|
if (!is_valid) {
|
|
300
372
|
return;
|
|
301
373
|
}
|
|
302
|
-
this.__dict_append[
|
|
374
|
+
this.__dict_append[IDENT_KEY_SMS] = value;
|
|
303
375
|
}
|
|
304
376
|
|
|
305
377
|
_remove_sms(mobile_no, caller) {
|
|
@@ -307,7 +379,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
307
379
|
if (!is_valid) {
|
|
308
380
|
return;
|
|
309
381
|
}
|
|
310
|
-
this.__dict_remove[
|
|
382
|
+
this.__dict_remove[IDENT_KEY_SMS] = value;
|
|
311
383
|
}
|
|
312
384
|
|
|
313
385
|
_add_whatsapp(mobile_no, caller) {
|
|
@@ -315,7 +387,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
315
387
|
if (!is_valid) {
|
|
316
388
|
return;
|
|
317
389
|
}
|
|
318
|
-
this.__dict_append[
|
|
390
|
+
this.__dict_append[IDENT_KEY_WHATSAPP] = value;
|
|
319
391
|
}
|
|
320
392
|
|
|
321
393
|
_remove_whatsapp(mobile_no, caller) {
|
|
@@ -323,7 +395,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
323
395
|
if (!is_valid) {
|
|
324
396
|
return;
|
|
325
397
|
}
|
|
326
|
-
this.__dict_remove[
|
|
398
|
+
this.__dict_remove[IDENT_KEY_WHATSAPP] = value;
|
|
327
399
|
}
|
|
328
400
|
|
|
329
401
|
// android push methods
|
|
@@ -335,7 +407,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
335
407
|
if (!provider) {
|
|
336
408
|
provider = "fcm";
|
|
337
409
|
}
|
|
338
|
-
if (!
|
|
410
|
+
if (!["fcm", "xiaomi", "oppo"].includes(provider)) {
|
|
339
411
|
this.__errors.push(
|
|
340
412
|
`[${caller}] unsupported androidpush provider ${provider}`
|
|
341
413
|
);
|
|
@@ -353,7 +425,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
353
425
|
if (!is_valid) {
|
|
354
426
|
return;
|
|
355
427
|
}
|
|
356
|
-
this.__dict_append[
|
|
428
|
+
this.__dict_append[IDENT_KEY_ANDROIDPUSH] = value;
|
|
357
429
|
this.__dict_append[KEY_PUSHVENDOR] = vendor;
|
|
358
430
|
}
|
|
359
431
|
|
|
@@ -367,7 +439,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
367
439
|
if (!is_valid) {
|
|
368
440
|
return;
|
|
369
441
|
}
|
|
370
|
-
this.__dict_remove[
|
|
442
|
+
this.__dict_remove[IDENT_KEY_ANDROIDPUSH] = value;
|
|
371
443
|
this.__dict_remove[KEY_PUSHVENDOR] = vendor;
|
|
372
444
|
}
|
|
373
445
|
|
|
@@ -380,7 +452,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
380
452
|
if (!provider) {
|
|
381
453
|
provider = "apns";
|
|
382
454
|
}
|
|
383
|
-
if (!
|
|
455
|
+
if (!["apns"].includes(provider)) {
|
|
384
456
|
this.__errors.push(
|
|
385
457
|
`[${caller}] unsupported iospush provider ${provider}`
|
|
386
458
|
);
|
|
@@ -398,7 +470,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
398
470
|
if (!is_valid) {
|
|
399
471
|
return;
|
|
400
472
|
}
|
|
401
|
-
this.__dict_append[
|
|
473
|
+
this.__dict_append[IDENT_KEY_IOSPUSH] = value;
|
|
402
474
|
this.__dict_append[KEY_PUSHVENDOR] = vendor;
|
|
403
475
|
}
|
|
404
476
|
|
|
@@ -411,7 +483,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
411
483
|
if (!is_valid) {
|
|
412
484
|
return;
|
|
413
485
|
}
|
|
414
|
-
this.__dict_remove[
|
|
486
|
+
this.__dict_remove[IDENT_KEY_IOSPUSH] = value;
|
|
415
487
|
this.__dict_remove[KEY_PUSHVENDOR] = vendor;
|
|
416
488
|
}
|
|
417
489
|
|
|
@@ -426,7 +498,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
426
498
|
if (!provider) {
|
|
427
499
|
provider = "vapid";
|
|
428
500
|
}
|
|
429
|
-
if (!
|
|
501
|
+
if (!["vapid"].includes(provider)) {
|
|
430
502
|
this.__errors.push(
|
|
431
503
|
`[${caller}] unsupported webpush provider ${provider}`
|
|
432
504
|
);
|
|
@@ -444,7 +516,7 @@ export default class _IdentityEventInternalHelper {
|
|
|
444
516
|
if (!is_valid) {
|
|
445
517
|
return;
|
|
446
518
|
}
|
|
447
|
-
this.__dict_append[
|
|
519
|
+
this.__dict_append[IDENT_KEY_WEBPUSH] = value;
|
|
448
520
|
this.__dict_append[KEY_PUSHVENDOR] = vendor;
|
|
449
521
|
}
|
|
450
522
|
|
|
@@ -457,7 +529,78 @@ export default class _IdentityEventInternalHelper {
|
|
|
457
529
|
if (!is_valid) {
|
|
458
530
|
return;
|
|
459
531
|
}
|
|
460
|
-
this.__dict_remove[
|
|
532
|
+
this.__dict_remove[IDENT_KEY_WEBPUSH] = value;
|
|
461
533
|
this.__dict_remove[KEY_PUSHVENDOR] = vendor;
|
|
462
534
|
}
|
|
535
|
+
|
|
536
|
+
__validate_slack_userid(userid, caller) {
|
|
537
|
+
let [validated_userid, is_valid] = this.__check_ident_val_string(
|
|
538
|
+
userid,
|
|
539
|
+
caller
|
|
540
|
+
);
|
|
541
|
+
if (!is_valid) {
|
|
542
|
+
return [validated_userid, false];
|
|
543
|
+
}
|
|
544
|
+
validated_userid = validated_userid.toUpperCase();
|
|
545
|
+
if (
|
|
546
|
+
!validated_userid.startsWith("U") &&
|
|
547
|
+
!validated_userid.startsWith("W")
|
|
548
|
+
) {
|
|
549
|
+
this.__errors.push(
|
|
550
|
+
`[${caller}] invalid value ${validated_userid}. Slack user/member_id starts with a U or W`
|
|
551
|
+
);
|
|
552
|
+
return [validated_userid, false];
|
|
553
|
+
}
|
|
554
|
+
return [validated_userid, true];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
__check_slack_dict(value, caller) {
|
|
558
|
+
const msg =
|
|
559
|
+
"value must be a valid dict/json with one of these keys: [email, user_id]";
|
|
560
|
+
if (!(value && value instanceof Object)) {
|
|
561
|
+
this.__errors.push(`[${caller}] ${msg}`);
|
|
562
|
+
return [value, false];
|
|
563
|
+
}
|
|
564
|
+
let user_id = value.user_id;
|
|
565
|
+
let email = value.email;
|
|
566
|
+
if (user_id && user_id.trim()) {
|
|
567
|
+
user_id = user_id.trim();
|
|
568
|
+
let [validated_user_id, is_valid] = this.__validate_slack_userid(
|
|
569
|
+
user_id,
|
|
570
|
+
caller
|
|
571
|
+
);
|
|
572
|
+
if (!is_valid) {
|
|
573
|
+
return [value, false];
|
|
574
|
+
} else {
|
|
575
|
+
return [{ user_id: validated_user_id }, true];
|
|
576
|
+
}
|
|
577
|
+
} else if (email && email.trim()) {
|
|
578
|
+
email = email.trim();
|
|
579
|
+
let [validated_email, is_valid] = this.__validate_email(email, caller);
|
|
580
|
+
if (!is_valid) {
|
|
581
|
+
return [value, false];
|
|
582
|
+
} else {
|
|
583
|
+
return [{ email: validated_email }, true];
|
|
584
|
+
}
|
|
585
|
+
} else {
|
|
586
|
+
this.__errors.push(`[${caller}] ${msg}`);
|
|
587
|
+
return [value, false];
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
_add_slack(value, caller) {
|
|
592
|
+
const [validated_value, is_valid] = this.__check_slack_dict(value, caller);
|
|
593
|
+
if (!is_valid) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
this.__dict_append[IDENT_KEY_SLACK] = validated_value;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
_remove_slack(value, caller) {
|
|
600
|
+
const [validated_value, is_valid] = this.__check_slack_dict(value, caller);
|
|
601
|
+
if (!is_valid) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
this.__dict_remove[IDENT_KEY_SLACK] = validated_value;
|
|
605
|
+
}
|
|
463
606
|
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BODY_MAX_APPARENT_SIZE_IN_BYTES,
|
|
3
|
+
BODY_MAX_APPARENT_SIZE_IN_BYTES_READABLE,
|
|
4
|
+
MAX_IDENTITY_EVENTS_IN_BULK_API,
|
|
5
|
+
} from "./constants";
|
|
6
|
+
import get_request_signature from "./signature";
|
|
7
|
+
import BulkResponse from "./bulk_response";
|
|
8
|
+
import { Subscriber } from "./subscriber";
|
|
9
|
+
import { cloneDeep } from "lodash";
|
|
10
|
+
import axios from "axios";
|
|
11
|
+
|
|
12
|
+
export default class BulkSubscribersFactory {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
new_instance() {
|
|
18
|
+
return new BulkSubscribers(this.config);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class _BulkSubscribersChunk {
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.__chunk = [];
|
|
26
|
+
this.__url = this.__get_url();
|
|
27
|
+
this.__headers = this.__common_headers();
|
|
28
|
+
|
|
29
|
+
this.__running_size = 0;
|
|
30
|
+
this.__running_length = 0;
|
|
31
|
+
this.response;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
__get_url() {
|
|
35
|
+
let url_template = "event/";
|
|
36
|
+
if (this.config.include_signature_param) {
|
|
37
|
+
if (this.config.auth_enabled) {
|
|
38
|
+
url_template = url_template + "?verify=true";
|
|
39
|
+
} else {
|
|
40
|
+
url_template = url_template + "?verify=false";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const url_formatted = `${this.config.base_url}${url_template}`;
|
|
44
|
+
return url_formatted;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
__common_headers() {
|
|
48
|
+
return {
|
|
49
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
50
|
+
"User-Agent": this.config.user_agent,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
__dynamic_headers() {
|
|
55
|
+
return {
|
|
56
|
+
Date: new Date().toUTCString(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
__add_event_to_chunk(event, event_size) {
|
|
61
|
+
// First add size, then body to reduce effects of race condition
|
|
62
|
+
this.__running_size += event_size;
|
|
63
|
+
this.__chunk.push(event);
|
|
64
|
+
this.__running_length += 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
__check_limit_reached() {
|
|
68
|
+
if (
|
|
69
|
+
this.__running_length >= MAX_IDENTITY_EVENTS_IN_BULK_API ||
|
|
70
|
+
this.__running_size >= BODY_MAX_APPARENT_SIZE_IN_BYTES
|
|
71
|
+
) {
|
|
72
|
+
return true;
|
|
73
|
+
} else {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try_to_add_into_chunk(event, event_size) {
|
|
79
|
+
if (!event) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (this.__check_limit_reached()) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (event_size > BODY_MAX_APPARENT_SIZE_IN_BYTES) {
|
|
86
|
+
throw new SuprsendError(
|
|
87
|
+
`workflow body (discounting attachment if any) too big - ${event_size} Bytes, must not cross ${BODY_MAX_APPARENT_SIZE_IN_BYTES_READABLE}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (this.__running_size + event_size > BODY_MAX_APPARENT_SIZE_IN_BYTES) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.__add_event_to_chunk(event, event_size);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async trigger() {
|
|
99
|
+
const headers = { ...this.__headers, ...this.__dynamic_headers() };
|
|
100
|
+
const content_text = JSON.stringify(this.__chunk);
|
|
101
|
+
// Based on whether signature is required or not, add Authorization header
|
|
102
|
+
if (this.config.auth_enabled) {
|
|
103
|
+
const signature = get_request_signature(
|
|
104
|
+
this.__url,
|
|
105
|
+
"POST",
|
|
106
|
+
content_text,
|
|
107
|
+
headers,
|
|
108
|
+
this.config.workspace_secret
|
|
109
|
+
);
|
|
110
|
+
headers["Authorization"] = `${this.config.workspace_key}:${signature}`;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const response = await axios.post(this.__url, content_text, { headers });
|
|
114
|
+
const ok_response = Math.floor(response.status / 100) == 2;
|
|
115
|
+
if (ok_response) {
|
|
116
|
+
this.response = {
|
|
117
|
+
status: "success",
|
|
118
|
+
status_code: response.status,
|
|
119
|
+
total: this.__chunk.length,
|
|
120
|
+
success: this.__chunk.length,
|
|
121
|
+
failure: 0,
|
|
122
|
+
failed_records: [],
|
|
123
|
+
};
|
|
124
|
+
} else {
|
|
125
|
+
this.response = {
|
|
126
|
+
status: "fail",
|
|
127
|
+
status_code: response.status,
|
|
128
|
+
total: this.__chunk.length,
|
|
129
|
+
success: 0,
|
|
130
|
+
failure: this.__chunk.length,
|
|
131
|
+
failed_records: this.__chunk.map((item) => ({
|
|
132
|
+
record: item,
|
|
133
|
+
error: response.statusText,
|
|
134
|
+
code: response.status,
|
|
135
|
+
})),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
const error_status = err.status || 500;
|
|
140
|
+
return {
|
|
141
|
+
status: "fail",
|
|
142
|
+
status_code: error_status,
|
|
143
|
+
message: err.message,
|
|
144
|
+
total: this.__chunk.length,
|
|
145
|
+
success: 0,
|
|
146
|
+
failure: this.__chunk.length,
|
|
147
|
+
failed_records: this.__chunk.map((item) => ({
|
|
148
|
+
record: item,
|
|
149
|
+
error: err.message,
|
|
150
|
+
code: error_status,
|
|
151
|
+
})),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
class BulkSubscribers {
|
|
158
|
+
constructor(config) {
|
|
159
|
+
this.config = config;
|
|
160
|
+
this.__subscribers = [];
|
|
161
|
+
this.__pending_records = [];
|
|
162
|
+
this.chunks = [];
|
|
163
|
+
this.response = new BulkResponse();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
__validate_subscriber_events() {
|
|
167
|
+
if (!this.__subscribers) {
|
|
168
|
+
throw new SuprsendError("users list is empty in bulk request");
|
|
169
|
+
}
|
|
170
|
+
for (let sub of this.__subscribers) {
|
|
171
|
+
const is_part_of_bulk = true;
|
|
172
|
+
const warnings_list = sub.validate_body(is_part_of_bulk);
|
|
173
|
+
if (warnings_list) {
|
|
174
|
+
this.response.warnings = [...this.response.warnings, ...warnings_list];
|
|
175
|
+
}
|
|
176
|
+
const ev_arr = sub.events();
|
|
177
|
+
for (let ev of ev_arr) {
|
|
178
|
+
const [ev_json, body_size] = sub.validate_event_size(ev);
|
|
179
|
+
this.__pending_records.push([ev_json, body_size]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
__chunkify(start_idx = 0) {
|
|
185
|
+
const curr_chunk = new _BulkSubscribersChunk(this.config);
|
|
186
|
+
this.chunks.push(curr_chunk);
|
|
187
|
+
const entries = this.__pending_records.slice(start_idx).entries();
|
|
188
|
+
for (const [rel_idx, rec] of entries) {
|
|
189
|
+
const is_added = curr_chunk.try_to_add_into_chunk(rec[0], rec[1]);
|
|
190
|
+
if (!is_added) {
|
|
191
|
+
// create chunks from remaining records
|
|
192
|
+
this.__chunkify(start_idx + rel_idx);
|
|
193
|
+
// Don't forget to break. As current loop must not continue further
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
append(...subscribers) {
|
|
200
|
+
if (!subscribers) {
|
|
201
|
+
throw SuprsendError("users list empty. must pass one or more users");
|
|
202
|
+
}
|
|
203
|
+
for (let sub of subscribers) {
|
|
204
|
+
if (!sub) {
|
|
205
|
+
throw new SuprsendError("null/empty element found in bulk instance");
|
|
206
|
+
}
|
|
207
|
+
if (!(sub instanceof Subscriber)) {
|
|
208
|
+
throw new SuprsendError(
|
|
209
|
+
"element must be an instance of suprsend.Subscriber"
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
const sub_copy = cloneDeep(sub);
|
|
213
|
+
this.__subscribers.push(sub_copy);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
trigger() {
|
|
218
|
+
this.save();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async save() {
|
|
222
|
+
this.__validate_subscriber_events();
|
|
223
|
+
this.__chunkify();
|
|
224
|
+
for (const [c_idx, ch] of this.chunks.entries()) {
|
|
225
|
+
if (this.config.req_log_level > 0) {
|
|
226
|
+
console.log(`DEBUG: triggering api call for chunk: ${c_idx}`);
|
|
227
|
+
}
|
|
228
|
+
// do api call
|
|
229
|
+
await ch.trigger();
|
|
230
|
+
// merge response
|
|
231
|
+
this.response.merge_chunk_response(ch.response);
|
|
232
|
+
}
|
|
233
|
+
return this.response;
|
|
234
|
+
}
|
|
235
|
+
}
|