@suprsend/node-sdk 0.0.5 → 0.1.1
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/event.js +205 -0
- package/dist/identity.js +417 -0
- package/dist/identity_helper.js +648 -0
- package/dist/index.js +12 -0
- package/dist/request_json/event.json +47 -0
- package/dist/request_json/workflow.json +49 -3
- package/dist/signature.js +1 -1
- package/dist/utils.js +42 -32
- package/dist/workflow.js +4 -2
- package/package.json +10 -7
- package/src/event.js +148 -0
- package/src/identity.js +281 -0
- package/src/identity_helper.js +463 -0
- package/src/index.js +8 -0
- package/src/request_json/event.json +47 -0
- package/src/request_json/workflow.json +49 -3
- package/src/signature.js +1 -4
- package/src/utils.js +28 -23
- package/src/workflow.js +5 -3
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import {
|
|
2
|
+
is_object,
|
|
3
|
+
has_special_char,
|
|
4
|
+
epoch_milliseconds,
|
|
5
|
+
uuid,
|
|
6
|
+
is_empty,
|
|
7
|
+
is_string,
|
|
8
|
+
} from "./utils";
|
|
9
|
+
|
|
10
|
+
const EMAIL_REGEX = /\S+@\S+\.\S+/;
|
|
11
|
+
const MOBILE_REGEX = /^\+[0-9\s]+/;
|
|
12
|
+
const KEY_PUSHVENDOR = "$pushvendor";
|
|
13
|
+
const CHANNEL_MAP = {
|
|
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 {
|
|
26
|
+
constructor(distinct_id, workspace_key) {
|
|
27
|
+
this.distinct_id = distinct_id;
|
|
28
|
+
this.workspace_key = workspace_key;
|
|
29
|
+
|
|
30
|
+
this.__dict_append = {};
|
|
31
|
+
this.__append_count = 0;
|
|
32
|
+
|
|
33
|
+
this.__dict_remove = {};
|
|
34
|
+
this.__remove_count = 0;
|
|
35
|
+
|
|
36
|
+
this.__list_unset = [];
|
|
37
|
+
this.__unset_count = 0;
|
|
38
|
+
|
|
39
|
+
this.__errors = [];
|
|
40
|
+
this.__info = [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
reset() {
|
|
44
|
+
this.__dict_append = {};
|
|
45
|
+
this.__append_count = 0;
|
|
46
|
+
|
|
47
|
+
this.__dict_remove = {};
|
|
48
|
+
this.__remove_count = 0;
|
|
49
|
+
|
|
50
|
+
this.__list_unset = [];
|
|
51
|
+
this.__unset_count = 0;
|
|
52
|
+
|
|
53
|
+
this.__errors = [];
|
|
54
|
+
this.__info = [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get_identity_events() {
|
|
58
|
+
const evt = this.__form_event();
|
|
59
|
+
const ret_val = {
|
|
60
|
+
errors: this.__errors,
|
|
61
|
+
info: this.__info,
|
|
62
|
+
event: evt,
|
|
63
|
+
append: this.__append_count,
|
|
64
|
+
remove: this.__remove_count,
|
|
65
|
+
unset: this.__unset_count,
|
|
66
|
+
};
|
|
67
|
+
this.reset();
|
|
68
|
+
return ret_val;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
__form_event() {
|
|
72
|
+
if (!is_empty(this.__dict_append) || !is_empty(this.__dict_remove)) {
|
|
73
|
+
let event = {
|
|
74
|
+
$insert_id: uuid(),
|
|
75
|
+
$time: epoch_milliseconds(),
|
|
76
|
+
env: this.workspace_key,
|
|
77
|
+
distinct_id: this.distinct_id,
|
|
78
|
+
};
|
|
79
|
+
if (!is_empty(this.__dict_append)) {
|
|
80
|
+
event["$append"] = this.__dict_append;
|
|
81
|
+
this.__append_count += 1;
|
|
82
|
+
}
|
|
83
|
+
if (!is_empty(this.__dict_remove)) {
|
|
84
|
+
event["$remove"] = this.__dict_remove;
|
|
85
|
+
this.__remove_count += 1;
|
|
86
|
+
}
|
|
87
|
+
return event;
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
__validate_key_basic(key, caller) {
|
|
93
|
+
if (!is_string(key)) {
|
|
94
|
+
this.__info.push(
|
|
95
|
+
`[${caller}] skipping key: ${key}. key must be a string`
|
|
96
|
+
);
|
|
97
|
+
return [key, false];
|
|
98
|
+
}
|
|
99
|
+
key = key.trim();
|
|
100
|
+
if (!key) {
|
|
101
|
+
this.__info.push(`[${caller}] skipping key: empty string`);
|
|
102
|
+
return [key, false];
|
|
103
|
+
}
|
|
104
|
+
return [key, true];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
__validate_key_prefix(key, caller) {
|
|
108
|
+
if (!this.__is_identity_key(key)) {
|
|
109
|
+
if (has_special_char(key)) {
|
|
110
|
+
this.__info.push(
|
|
111
|
+
`[${caller}] skipping key: ${key}. key starting with [$,ss_] are reserved`
|
|
112
|
+
);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
__is_identity_key(key) {
|
|
120
|
+
return Object.values(CHANNEL_MAP).includes(key);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_append_kv(key, value, args = {}, caller = "append") {
|
|
124
|
+
const [validated_key, is_valid] = this.__validate_key_basic(key, caller);
|
|
125
|
+
if (!is_valid) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (this.__is_identity_key(validated_key)) {
|
|
129
|
+
this.__add_identity(validated_key, value, args, caller);
|
|
130
|
+
} else {
|
|
131
|
+
const is_valid = this.__validate_key_prefix(validated_key, caller);
|
|
132
|
+
if (is_valid) {
|
|
133
|
+
this.__dict_append[validated_key] = value;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_remove_kv(key, value, args = {}, caller = "remove") {
|
|
139
|
+
const [validated_key, is_valid] = this.__validate_key_basic(key, caller);
|
|
140
|
+
if (!is_valid) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (this.__is_identity_key(validated_key)) {
|
|
144
|
+
this.__remove_identity(validated_key, value, args, caller);
|
|
145
|
+
} else {
|
|
146
|
+
const is_valid = this.__validate_key_prefix(validated_key, caller);
|
|
147
|
+
if (is_valid) {
|
|
148
|
+
this.__dict_remove[validated_key] = value;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
__add_identity(key, value, args, caller) {
|
|
154
|
+
switch (key) {
|
|
155
|
+
case CHANNEL_MAP.EMAIL:
|
|
156
|
+
this._add_email(value, caller);
|
|
157
|
+
break;
|
|
158
|
+
case CHANNEL_MAP.SMS:
|
|
159
|
+
this._add_sms(value, caller);
|
|
160
|
+
break;
|
|
161
|
+
case CHANNEL_MAP.WHATSAPP:
|
|
162
|
+
this._add_whatsapp(value, caller);
|
|
163
|
+
break;
|
|
164
|
+
case CHANNEL_MAP.ANDROID_PUSH:
|
|
165
|
+
this._add_androidpush(value, args[KEY_PUSHVENDOR], caller);
|
|
166
|
+
if (this.__dict_append[KEY_PUSHVENDOR]) {
|
|
167
|
+
args[KEY_PUSHVENDOR] = this.__dict_append[KEY_PUSHVENDOR];
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
case CHANNEL_MAP.IOS_PUSH:
|
|
171
|
+
this._add_iospush(value, args[KEY_PUSHVENDOR], caller);
|
|
172
|
+
if (this.__dict_append[KEY_PUSHVENDOR]) {
|
|
173
|
+
args[KEY_PUSHVENDOR] = this.__dict_append[KEY_PUSHVENDOR];
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
case CHANNEL_MAP.WEB_PUSH:
|
|
177
|
+
this._add_webpush(value, args[KEY_PUSHVENDOR], caller);
|
|
178
|
+
if (this.__dict_append[KEY_PUSHVENDOR]) {
|
|
179
|
+
args[KEY_PUSHVENDOR] = this.__dict_append[KEY_PUSHVENDOR];
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
__remove_identity(key, value, args, caller) {
|
|
188
|
+
switch (key) {
|
|
189
|
+
case CHANNEL_MAP.EMAIL:
|
|
190
|
+
this._remove_email(value, caller);
|
|
191
|
+
break;
|
|
192
|
+
case CHANNEL_MAP.SMS:
|
|
193
|
+
this._remove_sms(value, caller);
|
|
194
|
+
break;
|
|
195
|
+
case CHANNEL_MAP.WHATSAPP:
|
|
196
|
+
this._remove_whatsapp(value, caller);
|
|
197
|
+
break;
|
|
198
|
+
case CHANNEL_MAP.ANDROID_PUSH:
|
|
199
|
+
this._remove_androidpush(value, args[KEY_PUSHVENDOR], caller);
|
|
200
|
+
if (this.__dict_remove[KEY_PUSHVENDOR]) {
|
|
201
|
+
args[KEY_PUSHVENDOR] = this.__dict_remove[KEY_PUSHVENDOR];
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
case CHANNEL_MAP.IOS_PUSH:
|
|
205
|
+
this._remove_iospush(value, args[KEY_PUSHVENDOR], caller);
|
|
206
|
+
if (this.__dict_remove[KEY_PUSHVENDOR]) {
|
|
207
|
+
args[KEY_PUSHVENDOR] = this.__dict_remove[KEY_PUSHVENDOR];
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
case CHANNEL_MAP.WEB_PUSH:
|
|
211
|
+
this._remove_webpush(value, args[KEY_PUSHVENDOR], caller);
|
|
212
|
+
if (this.__dict_remove[KEY_PUSHVENDOR]) {
|
|
213
|
+
args[KEY_PUSHVENDOR] = this.__dict_remove[KEY_PUSHVENDOR];
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
default:
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
__check_ident_val_string(value, caller) {
|
|
222
|
+
const message = "value must a string with proper value";
|
|
223
|
+
if (!is_string(value)) {
|
|
224
|
+
this.__errors.push(`[${caller}] ${message}`);
|
|
225
|
+
return [value, false];
|
|
226
|
+
}
|
|
227
|
+
value = value.trim();
|
|
228
|
+
if (!value) {
|
|
229
|
+
this.__errors.push(`[${caller}] ${message}`);
|
|
230
|
+
return [value, false];
|
|
231
|
+
}
|
|
232
|
+
return [value, true];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// email methods
|
|
236
|
+
__validate_email(value, caller) {
|
|
237
|
+
const [email, is_valid] = this.__check_ident_val_string(value, caller);
|
|
238
|
+
if (!is_valid) {
|
|
239
|
+
return [email, false];
|
|
240
|
+
}
|
|
241
|
+
const message = "value in email format required. e.g. user@example.com";
|
|
242
|
+
const min_length = 6;
|
|
243
|
+
const max_length = 127;
|
|
244
|
+
const is_valid_email = EMAIL_REGEX.test(email);
|
|
245
|
+
if (!is_valid_email) {
|
|
246
|
+
this.__errors.push(`[${caller}] invalid value ${email}. ${message}`);
|
|
247
|
+
return [email, false];
|
|
248
|
+
}
|
|
249
|
+
if (email.length < min_length || email.length > max_length) {
|
|
250
|
+
this.__errors.push(
|
|
251
|
+
`[${caller}] invalid value ${email}. must be 6 <= email.length <= 127`
|
|
252
|
+
);
|
|
253
|
+
return [email, false];
|
|
254
|
+
}
|
|
255
|
+
return [email, true];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_add_email(email, caller) {
|
|
259
|
+
const [value, is_valid] = this.__validate_email(email, caller);
|
|
260
|
+
if (!is_valid) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.__dict_append[CHANNEL_MAP.EMAIL] = value;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
_remove_email(email, caller) {
|
|
267
|
+
const [value, is_valid] = this.__validate_email(email, caller);
|
|
268
|
+
if (!is_valid) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
this.__dict_remove[CHANNEL_MAP.EMAIL] = value;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// mobile methods
|
|
275
|
+
__validate_mobile_no(value, caller) {
|
|
276
|
+
const [mobile, is_valid] = this.__check_ident_val_string(value, caller);
|
|
277
|
+
if (!is_valid) {
|
|
278
|
+
return [mobile, false];
|
|
279
|
+
}
|
|
280
|
+
const message =
|
|
281
|
+
"number must start with + and must contain country code. e.g. +41446681800";
|
|
282
|
+
const min_length = 8;
|
|
283
|
+
const is_valid_mobile = MOBILE_REGEX.test(mobile);
|
|
284
|
+
if (!is_valid_mobile) {
|
|
285
|
+
this.__errors.push(`[${caller}] invalid value ${mobile}. ${message}`);
|
|
286
|
+
return [mobile, false];
|
|
287
|
+
}
|
|
288
|
+
if (mobile.length < min_length) {
|
|
289
|
+
this.__errors.push(
|
|
290
|
+
`[${caller}] invalid value ${mobile}. mobile_no.length must be >= 8`
|
|
291
|
+
);
|
|
292
|
+
return [mobile, false];
|
|
293
|
+
}
|
|
294
|
+
return [mobile, true];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
_add_sms(mobile_no, caller) {
|
|
298
|
+
const [value, is_valid] = this.__validate_mobile_no(mobile_no, caller);
|
|
299
|
+
if (!is_valid) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
this.__dict_append[CHANNEL_MAP.SMS] = value;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
_remove_sms(mobile_no, caller) {
|
|
306
|
+
const [value, is_valid] = this.__validate_mobile_no(mobile_no, caller);
|
|
307
|
+
if (!is_valid) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
this.__dict_remove[CHANNEL_MAP.SMS] = value;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_add_whatsapp(mobile_no, caller) {
|
|
314
|
+
const [value, is_valid] = this.__validate_mobile_no(mobile_no, caller);
|
|
315
|
+
if (!is_valid) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
this.__dict_append[CHANNEL_MAP.WHATSAPP] = value;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
_remove_whatsapp(mobile_no, caller) {
|
|
322
|
+
const [value, is_valid] = this.__validate_mobile_no(mobile_no, caller);
|
|
323
|
+
if (!is_valid) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
this.__dict_remove[CHANNEL_MAP.WHATSAPP] = value;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// android push methods
|
|
330
|
+
__check_androidpush_value(value, provider, caller) {
|
|
331
|
+
const [push_token, is_valid] = this.__check_ident_val_string(value, caller);
|
|
332
|
+
if (!is_valid) {
|
|
333
|
+
return [push_token, provider, false];
|
|
334
|
+
}
|
|
335
|
+
if (!provider) {
|
|
336
|
+
provider = "fcm";
|
|
337
|
+
}
|
|
338
|
+
if (!ANDROID_PUSH_VENDORS.includes(provider)) {
|
|
339
|
+
this.__errors.push(
|
|
340
|
+
`[${caller}] unsupported androidpush provider ${provider}`
|
|
341
|
+
);
|
|
342
|
+
return [push_token, provider, false];
|
|
343
|
+
}
|
|
344
|
+
return [push_token, provider, true];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
_add_androidpush(push_token, provider = "fcm", caller) {
|
|
348
|
+
const [value, vendor, is_valid] = this.__check_androidpush_value(
|
|
349
|
+
push_token,
|
|
350
|
+
provider,
|
|
351
|
+
caller
|
|
352
|
+
);
|
|
353
|
+
if (!is_valid) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.__dict_append[CHANNEL_MAP.ANDROID_PUSH] = value;
|
|
357
|
+
this.__dict_append[KEY_PUSHVENDOR] = vendor;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
_remove_androidpush(push_token, provider = "fcm") {
|
|
361
|
+
const caller = "remove_androidpush";
|
|
362
|
+
const [value, vendor, is_valid] = this.__check_androidpush_value(
|
|
363
|
+
push_token,
|
|
364
|
+
provider,
|
|
365
|
+
caller
|
|
366
|
+
);
|
|
367
|
+
if (!is_valid) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
this.__dict_remove[CHANNEL_MAP.ANDROID_PUSH] = value;
|
|
371
|
+
this.__dict_remove[KEY_PUSHVENDOR] = vendor;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ios push methods
|
|
375
|
+
__check_iospush_value(value, provider, caller) {
|
|
376
|
+
const [push_token, is_valid] = this.__check_ident_val_string(value, caller);
|
|
377
|
+
if (!is_valid) {
|
|
378
|
+
return [push_token, provider, false];
|
|
379
|
+
}
|
|
380
|
+
if (!provider) {
|
|
381
|
+
provider = "apns";
|
|
382
|
+
}
|
|
383
|
+
if (!IOS_PUSH_VENDORS.includes(provider)) {
|
|
384
|
+
this.__errors.push(
|
|
385
|
+
`[${caller}] unsupported iospush provider ${provider}`
|
|
386
|
+
);
|
|
387
|
+
return [push_token, provider, false];
|
|
388
|
+
}
|
|
389
|
+
return [push_token, provider, true];
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
_add_iospush(push_token, provider = "apns", caller) {
|
|
393
|
+
const [value, vendor, is_valid] = this.__check_iospush_value(
|
|
394
|
+
push_token,
|
|
395
|
+
provider,
|
|
396
|
+
caller
|
|
397
|
+
);
|
|
398
|
+
if (!is_valid) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
this.__dict_append[CHANNEL_MAP.IOS_PUSH] = value;
|
|
402
|
+
this.__dict_append[KEY_PUSHVENDOR] = vendor;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
_remove_iospush(push_token, provider = "apns", caller) {
|
|
406
|
+
const [value, vendor, is_valid] = this.__check_iospush_value(
|
|
407
|
+
push_token,
|
|
408
|
+
provider,
|
|
409
|
+
caller
|
|
410
|
+
);
|
|
411
|
+
if (!is_valid) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
this.__dict_remove[CHANNEL_MAP.IOS_PUSH] = value;
|
|
415
|
+
this.__dict_remove[KEY_PUSHVENDOR] = vendor;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// web push methods
|
|
419
|
+
__check_webpush_dict(value, provider, caller) {
|
|
420
|
+
if (!is_object(value)) {
|
|
421
|
+
this.__errors.push(
|
|
422
|
+
`[${caller}] value must be a valid dict representing webpush-token`
|
|
423
|
+
);
|
|
424
|
+
return [value, provider, false];
|
|
425
|
+
}
|
|
426
|
+
if (!provider) {
|
|
427
|
+
provider = "vapid";
|
|
428
|
+
}
|
|
429
|
+
if (!WEB_PUSH_VENDORS.includes(provider)) {
|
|
430
|
+
this.__errors.push(
|
|
431
|
+
`[${caller}] unsupported webpush provider ${provider}`
|
|
432
|
+
);
|
|
433
|
+
return [value, provider, false];
|
|
434
|
+
}
|
|
435
|
+
return [value, provider, true];
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
_add_webpush(push_token, provider = "vapid", caller) {
|
|
439
|
+
const [value, vendor, is_valid] = this.__check_webpush_dict(
|
|
440
|
+
push_token,
|
|
441
|
+
provider,
|
|
442
|
+
caller
|
|
443
|
+
);
|
|
444
|
+
if (!is_valid) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
this.__dict_append[CHANNEL_MAP.WEB_PUSH] = value;
|
|
448
|
+
this.__dict_append[KEY_PUSHVENDOR] = vendor;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
_remove_webpush(push_token, provider = "vapid", caller) {
|
|
452
|
+
const [value, vendor, is_valid] = this.__check_webpush_dict(
|
|
453
|
+
push_token,
|
|
454
|
+
provider,
|
|
455
|
+
caller
|
|
456
|
+
);
|
|
457
|
+
if (!is_valid) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
this.__dict_remove[CHANNEL_MAP.WEB_PUSH] = value;
|
|
461
|
+
this.__dict_remove[KEY_PUSHVENDOR] = vendor;
|
|
462
|
+
}
|
|
463
|
+
}
|
package/src/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import Workflow from "./workflow";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import mime from "mime-types";
|
|
5
5
|
import { base64Encode, resolveTilde, SuprsendError } from "./utils";
|
|
6
|
+
import UserIdentityFactory from "./identity";
|
|
7
|
+
import EventCollector from "./event";
|
|
6
8
|
|
|
7
9
|
const package_json = require("../package.json");
|
|
8
10
|
|
|
@@ -17,6 +19,8 @@ class Suprsend {
|
|
|
17
19
|
this.user_agent = `suprsend/${
|
|
18
20
|
package_json.version
|
|
19
21
|
};node/${process.version.slice(1)}`;
|
|
22
|
+
this.user = new UserIdentityFactory(this);
|
|
23
|
+
this._eventcollector = new EventCollector(this);
|
|
20
24
|
this._validate();
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -77,6 +81,10 @@ class Suprsend {
|
|
|
77
81
|
wf.validate_data();
|
|
78
82
|
return wf.execute_workflow();
|
|
79
83
|
}
|
|
84
|
+
|
|
85
|
+
track(distinct_id, event_name, properties = {}) {
|
|
86
|
+
return this._eventcollector.collect(distinct_id, event_name, properties);
|
|
87
|
+
}
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
export default Suprsend;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "http://github.com/suprsend/suprsend-node-sdk/request_json/event.json",
|
|
4
|
+
"title": "track_event",
|
|
5
|
+
"description": "Json schema for Track event",
|
|
6
|
+
"$comment": "Json schema for Track event",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"$insert_id": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 36,
|
|
12
|
+
"description": "unique uuid generated per request"
|
|
13
|
+
},
|
|
14
|
+
"$time": {
|
|
15
|
+
"type": "integer",
|
|
16
|
+
"minimum": 1640995200000,
|
|
17
|
+
"description": "Timestamp: unix epoch in milliseconds"
|
|
18
|
+
},
|
|
19
|
+
"event": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 2,
|
|
22
|
+
"description": "Event Name to track"
|
|
23
|
+
},
|
|
24
|
+
"env": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"minLength": 20,
|
|
27
|
+
"description": "Workspace key"
|
|
28
|
+
},
|
|
29
|
+
"distinct_id": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1,
|
|
32
|
+
"description": "distinct_id: Id which uniquely identify a user in your app"
|
|
33
|
+
},
|
|
34
|
+
"properties": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"description": "Properties related to event"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": [
|
|
40
|
+
"$insert_id",
|
|
41
|
+
"$time",
|
|
42
|
+
"event",
|
|
43
|
+
"env",
|
|
44
|
+
"distinct_id",
|
|
45
|
+
"properties"
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
"$ref": "#/definitions/non_empty_string",
|
|
28
28
|
"description": "timestamp in ISO-8601 format. e.g 2021-08-27T20:14:51.643Z"
|
|
29
29
|
},
|
|
30
|
-
"
|
|
30
|
+
"delivery": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"$ref": "#/definitions/delivery_setting",
|
|
33
|
+
"description": "set parameters e.g smart (true/false), success metric, TTL, mandatory channels etc"
|
|
34
|
+
},
|
|
31
35
|
"users": {
|
|
32
36
|
"type": "array",
|
|
33
37
|
"items": { "$ref": "#/definitions/user_setting" },
|
|
@@ -51,7 +55,7 @@
|
|
|
51
55
|
"minLength": 8,
|
|
52
56
|
"pattern": "^\\+[0-9\\s]+",
|
|
53
57
|
"message": {
|
|
54
|
-
"required": "Either a mobile-number or an array of mobile-numbers. e.g [\"41446681800\"]",
|
|
58
|
+
"required": "Either a mobile-number or an array of mobile-numbers. e.g [\"+41446681800\"]",
|
|
55
59
|
"pattern": "number must start with + and must contain country code. e.g. +41446681800"
|
|
56
60
|
}
|
|
57
61
|
},
|
|
@@ -70,7 +74,11 @@
|
|
|
70
74
|
"user_setting": {
|
|
71
75
|
"type": "object",
|
|
72
76
|
"properties": {
|
|
73
|
-
"distinct_id": {
|
|
77
|
+
"distinct_id": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"minLength": 1,
|
|
80
|
+
"description": "distinct_id: Id which uniquely identify a user in your app"
|
|
81
|
+
},
|
|
74
82
|
"$email": {
|
|
75
83
|
"oneOf": [
|
|
76
84
|
{ "$ref": "#/definitions/email_pattern" },
|
|
@@ -170,6 +178,44 @@
|
|
|
170
178
|
},
|
|
171
179
|
"required": ["distinct_id"],
|
|
172
180
|
"additionalProperties": false
|
|
181
|
+
},
|
|
182
|
+
"delivery_setting": {
|
|
183
|
+
"type": "object",
|
|
184
|
+
"properties": {
|
|
185
|
+
"smart": {
|
|
186
|
+
"type": "boolean",
|
|
187
|
+
"default": false,
|
|
188
|
+
"description": "If false, notifications are sent to all channels at once. If true, notifications are sent one-by-one until success metric is satisfied"
|
|
189
|
+
},
|
|
190
|
+
"success": {
|
|
191
|
+
"type": "string",
|
|
192
|
+
"default": "seen",
|
|
193
|
+
"description": "possible values: seen/interaction/<user_defined_event>."
|
|
194
|
+
},
|
|
195
|
+
"time_to_live": {
|
|
196
|
+
"type": "string",
|
|
197
|
+
"default": "1h",
|
|
198
|
+
"description": "Used if smart=true. format [XX]d[XX]h[XX]m[XX]s e.g 1d2h30m10s(for 1day 2hours 30minutes 10sec)"
|
|
199
|
+
},
|
|
200
|
+
"mandatory_channels": {
|
|
201
|
+
"type": "array",
|
|
202
|
+
"items": {
|
|
203
|
+
"type": "string",
|
|
204
|
+
"enum": [
|
|
205
|
+
"androidpush",
|
|
206
|
+
"iospush",
|
|
207
|
+
"webpush",
|
|
208
|
+
"email",
|
|
209
|
+
"sms",
|
|
210
|
+
"whatsapp"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
"minItems": 0,
|
|
214
|
+
"description": "e.g ['email', 'sms']. Used if smart=true, notification on these channels must be sent, independent of success metric outcome"
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
"required": [],
|
|
218
|
+
"additionalProperties": false
|
|
173
219
|
}
|
|
174
220
|
}
|
|
175
221
|
}
|
package/src/signature.js
CHANGED
|
@@ -15,10 +15,7 @@ export default function get_request_signature(
|
|
|
15
15
|
if (http_verb === "GET") {
|
|
16
16
|
return "";
|
|
17
17
|
}
|
|
18
|
-
const content_md5 = crypto
|
|
19
|
-
.createHash("md5")
|
|
20
|
-
.update(JSON.stringify(content))
|
|
21
|
-
.digest("hex");
|
|
18
|
+
const content_md5 = crypto.createHash("md5").update(content).digest("hex");
|
|
22
19
|
const request_uri = get_path(url);
|
|
23
20
|
const sign_string =
|
|
24
21
|
http_verb +
|
package/src/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from "os";
|
|
2
2
|
import fs from "fs";
|
|
3
|
-
import
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
4
|
|
|
5
5
|
export function base64Encode(file) {
|
|
6
6
|
var body = fs.readFileSync(file);
|
|
@@ -19,32 +19,37 @@ export function resolveTilde(filePath) {
|
|
|
19
19
|
return filePath;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!schema_body) {
|
|
27
|
-
schema_body = _load_json_schema(schema_name);
|
|
28
|
-
__JSON_SCHEMAS[schema_name] = schema_body;
|
|
22
|
+
export class SuprsendError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "SuprsendError";
|
|
29
26
|
}
|
|
30
|
-
return schema_body;
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
var json_schema;
|
|
36
|
-
try {
|
|
37
|
-
var schema_string = fs.readFileSync(file_path, "utf8");
|
|
38
|
-
json_schema = JSON.parse(schema_string);
|
|
39
|
-
} catch (e) {
|
|
40
|
-
throw new SuprsendError("Missing Schema");
|
|
41
|
-
}
|
|
42
|
-
return json_schema;
|
|
29
|
+
export function is_string(value) {
|
|
30
|
+
return typeof value === "string";
|
|
43
31
|
}
|
|
44
32
|
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
export function is_object(value) {
|
|
34
|
+
return typeof value === "object" && !Array.isArray(value) && value !== null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function is_empty(value) {
|
|
38
|
+
if (is_object(value)) {
|
|
39
|
+
return Object.keys(value) <= 0;
|
|
40
|
+
} else if (Array.isArray(value)) {
|
|
41
|
+
return value.length <= 0;
|
|
49
42
|
}
|
|
50
43
|
}
|
|
44
|
+
|
|
45
|
+
export const has_special_char = (str) => {
|
|
46
|
+
return str.startsWith("$") || str?.toLowerCase()?.startsWith("ss_");
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function uuid() {
|
|
50
|
+
return uuidv4();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function epoch_milliseconds() {
|
|
54
|
+
return Math.round(Date.now());
|
|
55
|
+
}
|