mailchannels-sdk 0.6.1 → 0.7.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/mailchannels.d.mts +37 -30
- package/dist/mailchannels.d.ts +37 -30
- package/dist/mailchannels.mjs +517 -615
- package/package.json +7 -7
package/dist/mailchannels.mjs
CHANGED
|
@@ -50,6 +50,12 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
50
50
|
ErrorCode2[ErrorCode2["UnprocessableEntity"] = 422] = "UnprocessableEntity";
|
|
51
51
|
return ErrorCode2;
|
|
52
52
|
})(ErrorCode || {});
|
|
53
|
+
const createError = (message, statusCode = null) => {
|
|
54
|
+
return {
|
|
55
|
+
message,
|
|
56
|
+
statusCode
|
|
57
|
+
};
|
|
58
|
+
};
|
|
53
59
|
const getStatusError = (response, errors = {}) => {
|
|
54
60
|
const statusText = errors[response.status] || "Unknown error.";
|
|
55
61
|
const payload = response._data ?? response.data;
|
|
@@ -61,12 +67,11 @@ const getStatusError = (response, errors = {}) => {
|
|
|
61
67
|
} else if (Array.isArray(payload?.errors) && payload.errors?.length) {
|
|
62
68
|
details = payload.errors.join(", ");
|
|
63
69
|
}
|
|
64
|
-
return details ? `${statusText} ${details}` : statusText;
|
|
70
|
+
return createError(details ? `${statusText} ${details}` : statusText, response.status ?? null);
|
|
71
|
+
};
|
|
72
|
+
const getResultError = (e, fallback) => {
|
|
73
|
+
return createError(e instanceof Error ? e.message : fallback);
|
|
65
74
|
};
|
|
66
|
-
function getResultError(result, error, fallback) {
|
|
67
|
-
if (result.error) return result.error;
|
|
68
|
-
return error instanceof Error ? error.message : fallback;
|
|
69
|
-
}
|
|
70
75
|
|
|
71
76
|
const isValidEmail = (email) => {
|
|
72
77
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
@@ -125,13 +130,13 @@ const clean = (data) => {
|
|
|
125
130
|
};
|
|
126
131
|
const validateLimit = (limit, max) => {
|
|
127
132
|
if (typeof limit === "number" && (limit < 1 || max && limit > max)) {
|
|
128
|
-
return "The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed.");
|
|
133
|
+
return createError("The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed."));
|
|
129
134
|
}
|
|
130
135
|
return null;
|
|
131
136
|
};
|
|
132
137
|
const validateOffset = (offset) => {
|
|
133
138
|
if (typeof offset === "number" && offset < 0) {
|
|
134
|
-
return "Offset must be greater than or equal to 0.";
|
|
139
|
+
return createError("Offset must be greater than or equal to 0.");
|
|
135
140
|
}
|
|
136
141
|
return null;
|
|
137
142
|
};
|
|
@@ -159,21 +164,21 @@ class Emails {
|
|
|
159
164
|
* ```
|
|
160
165
|
*/
|
|
161
166
|
async send(options, dryRun = false) {
|
|
167
|
+
let error = null;
|
|
162
168
|
const { cc, bcc, from, to, html, text, mustaches, dkim } = options;
|
|
163
|
-
const result = { success: false, data: null, error: null };
|
|
164
169
|
const parsedFrom = parseRecipient(from);
|
|
165
170
|
if (!parsedFrom || !parsedFrom.email) {
|
|
166
|
-
|
|
167
|
-
return
|
|
171
|
+
error = createError("No sender provided. Use the `from` option to specify a sender");
|
|
172
|
+
return { success: false, data: null, error };
|
|
168
173
|
}
|
|
169
174
|
const parsedTo = parseArrayRecipients(to);
|
|
170
175
|
if (!parsedTo || !parsedTo.length) {
|
|
171
|
-
|
|
172
|
-
return
|
|
176
|
+
error = createError("No recipients provided. Use the `to` option to specify at least one recipient");
|
|
177
|
+
return { success: false, data: null, error };
|
|
173
178
|
}
|
|
174
179
|
if (!text && !html) {
|
|
175
|
-
|
|
176
|
-
return
|
|
180
|
+
error = createError("No email content provided");
|
|
181
|
+
return { success: false, data: null, error };
|
|
177
182
|
}
|
|
178
183
|
const content = [];
|
|
179
184
|
const template_type = mustaches ? "mustache" : void 0;
|
|
@@ -205,33 +210,29 @@ class Emails {
|
|
|
205
210
|
const response = await this.mailchannels.post("/tx/v1/send", {
|
|
206
211
|
query: { "dry-run": dryRun },
|
|
207
212
|
body: payload,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
result.success = true;
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
result.error = getStatusError(response2, {
|
|
213
|
+
onResponseError: async ({ response: response2 }) => {
|
|
214
|
+
error = getStatusError(response2, {
|
|
214
215
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
215
216
|
[ErrorCode.Forbidden]: "User does not have access to this feature.",
|
|
216
217
|
[ErrorCode.PayloadTooLarge]: "The total message size should not exceed 30MB. This includes the message itself, headers, and the combined size of any attachments."
|
|
217
218
|
});
|
|
218
219
|
}
|
|
219
|
-
}).catch((
|
|
220
|
-
|
|
220
|
+
}).catch((e) => {
|
|
221
|
+
error ||= getResultError(e, "Failed to send email.");
|
|
221
222
|
return null;
|
|
222
223
|
});
|
|
223
|
-
if (!response) return
|
|
224
|
-
|
|
224
|
+
if (!response) return { success: false, data: null, error };
|
|
225
|
+
const data = clean({
|
|
225
226
|
rendered: response.data,
|
|
226
227
|
requestId: response.request_id,
|
|
227
|
-
results: response.results?.map((
|
|
228
|
-
index:
|
|
229
|
-
messageId:
|
|
230
|
-
reason:
|
|
231
|
-
status:
|
|
228
|
+
results: response.results?.map((result) => ({
|
|
229
|
+
index: result.index,
|
|
230
|
+
messageId: result.message_id,
|
|
231
|
+
reason: result.reason,
|
|
232
|
+
status: result.status
|
|
232
233
|
}))
|
|
233
234
|
});
|
|
234
|
-
return
|
|
235
|
+
return { success: !!data, data, error: null };
|
|
235
236
|
}
|
|
236
237
|
/**
|
|
237
238
|
* Validates a domain's email authentication setup by retrieving its DKIM, SPF, and Domain Lockdown status. This endpoint checks whether the domain is properly configured for secure email delivery.
|
|
@@ -251,9 +252,9 @@ class Emails {
|
|
|
251
252
|
* ```
|
|
252
253
|
*/
|
|
253
254
|
async checkDomain(options) {
|
|
255
|
+
let error = null;
|
|
254
256
|
const { dkim, domain, senderId } = options;
|
|
255
257
|
const dkimOptions = dkim ? Array.isArray(dkim) ? dkim : [dkim] : void 0;
|
|
256
|
-
const result = { data: null, error: null };
|
|
257
258
|
const payload = {
|
|
258
259
|
dkim_settings: dkimOptions?.map(({ domain: domain2, privateKey, selector }) => ({
|
|
259
260
|
dkim_domain: domain2,
|
|
@@ -266,17 +267,17 @@ class Emails {
|
|
|
266
267
|
const response = await this.mailchannels.post("/tx/v1/check-domain", {
|
|
267
268
|
body: payload,
|
|
268
269
|
onResponseError: async ({ response: response2 }) => {
|
|
269
|
-
|
|
270
|
+
error = getStatusError(response2, {
|
|
270
271
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
271
272
|
[ErrorCode.Forbidden]: "User does not have access to this feature."
|
|
272
273
|
});
|
|
273
274
|
}
|
|
274
|
-
}).catch((
|
|
275
|
-
|
|
275
|
+
}).catch((e) => {
|
|
276
|
+
error ||= getResultError(e, "Failed to check domain.");
|
|
276
277
|
return null;
|
|
277
278
|
});
|
|
278
|
-
if (!response) return
|
|
279
|
-
|
|
279
|
+
if (!response) return { data: null, error };
|
|
280
|
+
const data = clean({
|
|
280
281
|
dkim: response.check_results.dkim.map((dkimResults) => ({
|
|
281
282
|
domain: dkimResults.dkim_domain,
|
|
282
283
|
keyStatus: dkimResults.dkim_key_status,
|
|
@@ -289,7 +290,7 @@ class Emails {
|
|
|
289
290
|
spf: response.check_results.spf,
|
|
290
291
|
references: response.references
|
|
291
292
|
});
|
|
292
|
-
return
|
|
293
|
+
return { data, error: null };
|
|
293
294
|
}
|
|
294
295
|
/**
|
|
295
296
|
* Create a DKIM key pair for a specified domain and selector using the specified algorithm and key length, for the current customer.
|
|
@@ -304,11 +305,11 @@ class Emails {
|
|
|
304
305
|
* ```
|
|
305
306
|
*/
|
|
306
307
|
async createDkimKey(domain, options) {
|
|
307
|
-
|
|
308
|
+
let error = null;
|
|
308
309
|
if (!options.selector || options.selector.length > 63) {
|
|
309
|
-
|
|
310
|
+
error = createError("Selector must be between 1 and 63 characters.");
|
|
311
|
+
return { data: null, error };
|
|
310
312
|
}
|
|
311
|
-
if (result.error) return result;
|
|
312
313
|
const payload = {
|
|
313
314
|
algorithm: options.algorithm,
|
|
314
315
|
key_length: options.length,
|
|
@@ -317,17 +318,17 @@ class Emails {
|
|
|
317
318
|
const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys`, {
|
|
318
319
|
body: payload,
|
|
319
320
|
onResponseError: async ({ response: response2 }) => {
|
|
320
|
-
|
|
321
|
+
error = getStatusError(response2, {
|
|
321
322
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
322
323
|
[ErrorCode.Conflict]: "Key pair already created for domain, and selector."
|
|
323
324
|
});
|
|
324
325
|
}
|
|
325
|
-
}).catch((
|
|
326
|
-
|
|
326
|
+
}).catch((e) => {
|
|
327
|
+
error ||= getResultError(e, "Failed to create DKIM key.");
|
|
327
328
|
return null;
|
|
328
329
|
});
|
|
329
|
-
if (!response) return
|
|
330
|
-
|
|
330
|
+
if (!response) return { data: null, error };
|
|
331
|
+
const data = clean({
|
|
331
332
|
algorithm: response.algorithm,
|
|
332
333
|
createdAt: response.created_at,
|
|
333
334
|
dnsRecords: response.dkim_dns_records,
|
|
@@ -340,7 +341,7 @@ class Emails {
|
|
|
340
341
|
status: response.status,
|
|
341
342
|
statusModifiedAt: response.status_modified_at
|
|
342
343
|
});
|
|
343
|
-
return
|
|
344
|
+
return { data, error: null };
|
|
344
345
|
}
|
|
345
346
|
/**
|
|
346
347
|
* Search for DKIM keys by domain, with optional filters. If selector is provided, at most one key will be returned.
|
|
@@ -355,13 +356,13 @@ class Emails {
|
|
|
355
356
|
* ```
|
|
356
357
|
*/
|
|
357
358
|
async getDkimKeys(domain, options) {
|
|
358
|
-
|
|
359
|
+
let error = null;
|
|
359
360
|
if (options?.selector && options.selector.length > 63) {
|
|
360
|
-
|
|
361
|
-
return
|
|
361
|
+
error = createError("Selector must be between 1 and 63 characters.");
|
|
362
|
+
return { data: null, error };
|
|
362
363
|
}
|
|
363
|
-
|
|
364
|
-
if (
|
|
364
|
+
error = validateLimit(options?.limit, 100) || validateOffset(options?.offset);
|
|
365
|
+
if (error) return { data: null, error };
|
|
365
366
|
const payload = {
|
|
366
367
|
selector: options?.selector,
|
|
367
368
|
status: options?.status,
|
|
@@ -372,16 +373,16 @@ class Emails {
|
|
|
372
373
|
const response = await this.mailchannels.get(`/tx/v1/domains/${domain}/dkim-keys`, {
|
|
373
374
|
query: payload,
|
|
374
375
|
onResponseError: async ({ response: response2 }) => {
|
|
375
|
-
|
|
376
|
+
error = getStatusError(response2, {
|
|
376
377
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
377
378
|
});
|
|
378
379
|
}
|
|
379
|
-
}).catch((
|
|
380
|
-
|
|
380
|
+
}).catch((e) => {
|
|
381
|
+
error ||= getResultError(e, "Failed to fetch DKIM keys.");
|
|
381
382
|
return null;
|
|
382
383
|
});
|
|
383
|
-
if (!response) return
|
|
384
|
-
|
|
384
|
+
if (!response) return { data: null, error };
|
|
385
|
+
const data = clean(response.keys.map((key) => ({
|
|
385
386
|
algorithm: key.algorithm,
|
|
386
387
|
createdAt: key.created_at,
|
|
387
388
|
dnsRecords: key.dkim_dns_records,
|
|
@@ -394,7 +395,7 @@ class Emails {
|
|
|
394
395
|
status: key.status,
|
|
395
396
|
statusModifiedAt: key.status_modified_at
|
|
396
397
|
})));
|
|
397
|
-
return
|
|
398
|
+
return { data, error: null };
|
|
398
399
|
}
|
|
399
400
|
/**
|
|
400
401
|
* Update fields of an existing DKIM key pair for the specified domain and selector, for the current customer. Currently, only the `status` field can be updated.
|
|
@@ -409,31 +410,26 @@ class Emails {
|
|
|
409
410
|
* })
|
|
410
411
|
*/
|
|
411
412
|
async updateDkimKey(domain, options) {
|
|
412
|
-
|
|
413
|
+
let error = null;
|
|
413
414
|
if (!options.selector || options.selector.length > 63) {
|
|
414
|
-
|
|
415
|
-
return
|
|
415
|
+
error = createError("Selector must be between 1 and 63 characters.");
|
|
416
|
+
return { success: false, error };
|
|
416
417
|
}
|
|
417
418
|
const payload = {
|
|
418
419
|
status: options.status
|
|
419
420
|
};
|
|
420
421
|
await this.mailchannels.patch(`/tx/v1/domains/${domain}/dkim-keys/${options.selector}`, {
|
|
421
422
|
body: payload,
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (response.ok) {
|
|
425
|
-
result.success = true;
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
result.error = getStatusError(response, {
|
|
423
|
+
onResponseError: async ({ response }) => {
|
|
424
|
+
error = getStatusError(response, {
|
|
429
425
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
430
426
|
[ErrorCode.NotFound]: "Specified key pair not found, or no active key for rotation. This may also occur if the DKIM domain or selector path parameter is missing."
|
|
431
427
|
});
|
|
432
428
|
}
|
|
433
|
-
}).catch((
|
|
434
|
-
|
|
429
|
+
}).catch((e) => {
|
|
430
|
+
error ||= getResultError(e, "Failed to update DKIM key.");
|
|
435
431
|
});
|
|
436
|
-
return
|
|
432
|
+
return { success: !error, error };
|
|
437
433
|
}
|
|
438
434
|
/**
|
|
439
435
|
* Rotate an active DKIM key pair. Mark the original key as `rotated`, and create a new key pair with the required new key selector, reusing the same algorithm and key length. The rotated key remains valid for signing for a 3-day grace period, and is automatically changed to `retired` 2 weeks after rotation. Publish the new key to its DNS TXT record before rotated key expires for signing as emails sent with an unpublished key will fail DKIM validation by receiving providers. After the grace period, only the new key is valid for signing if published.
|
|
@@ -452,14 +448,14 @@ class Emails {
|
|
|
452
448
|
* ```
|
|
453
449
|
*/
|
|
454
450
|
async rotateDkimKey(domain, selector, options) {
|
|
455
|
-
|
|
451
|
+
let error = null;
|
|
456
452
|
if (!selector || selector.length > 63) {
|
|
457
|
-
|
|
458
|
-
return
|
|
453
|
+
error = createError("Selector must be between 1 and 63 characters.");
|
|
454
|
+
return { data: null, error };
|
|
459
455
|
}
|
|
460
456
|
if (!options.newKey.selector || options.newKey.selector.length > 63) {
|
|
461
|
-
|
|
462
|
-
return
|
|
457
|
+
error = createError("New key selector must be between 1 and 63 characters.");
|
|
458
|
+
return { data: null, error };
|
|
463
459
|
}
|
|
464
460
|
const payload = {
|
|
465
461
|
new_key: {
|
|
@@ -469,18 +465,18 @@ class Emails {
|
|
|
469
465
|
const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys/${selector}/rotate`, {
|
|
470
466
|
body: payload,
|
|
471
467
|
onResponseError: async ({ response: response2 }) => {
|
|
472
|
-
|
|
468
|
+
error = getStatusError(response2, {
|
|
473
469
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
474
470
|
[ErrorCode.NotFound]: "Specified key pair not found.",
|
|
475
471
|
[ErrorCode.Conflict]: "Key pair already created for domain, and provided new key selector."
|
|
476
472
|
});
|
|
477
473
|
}
|
|
478
|
-
}).catch((
|
|
479
|
-
|
|
474
|
+
}).catch((e) => {
|
|
475
|
+
error ||= getResultError(e, "Failed to rotate DKIM key.");
|
|
480
476
|
return null;
|
|
481
477
|
});
|
|
482
|
-
if (!response) return
|
|
483
|
-
|
|
478
|
+
if (!response) return { data: null, error };
|
|
479
|
+
const data = clean({
|
|
484
480
|
new: {
|
|
485
481
|
algorithm: response.new_key.algorithm,
|
|
486
482
|
createdAt: response.new_key.created_at,
|
|
@@ -508,7 +504,7 @@ class Emails {
|
|
|
508
504
|
statusModifiedAt: response.rotated_key.status_modified_at
|
|
509
505
|
}
|
|
510
506
|
});
|
|
511
|
-
return
|
|
507
|
+
return { data, error: null };
|
|
512
508
|
}
|
|
513
509
|
}
|
|
514
510
|
|
|
@@ -526,33 +522,28 @@ class Webhooks {
|
|
|
526
522
|
* ```
|
|
527
523
|
*/
|
|
528
524
|
async enroll(endpoint) {
|
|
529
|
-
|
|
525
|
+
let error = null;
|
|
530
526
|
if (!endpoint) {
|
|
531
|
-
|
|
532
|
-
return
|
|
527
|
+
error = createError("No endpoint provided.");
|
|
528
|
+
return { success: false, error };
|
|
533
529
|
}
|
|
534
530
|
if (endpoint.length > 8e3) {
|
|
535
|
-
|
|
536
|
-
return
|
|
531
|
+
error = createError("The endpoint exceeds the maximum length of 8000 characters.");
|
|
532
|
+
return { success: false, error };
|
|
537
533
|
}
|
|
538
534
|
await this.mailchannels.post("/tx/v1/webhook", {
|
|
539
535
|
query: {
|
|
540
536
|
endpoint
|
|
541
537
|
},
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
if (response.ok) {
|
|
545
|
-
result.success = true;
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
result.error = getStatusError(response, {
|
|
538
|
+
onResponseError: async ({ response }) => {
|
|
539
|
+
error = getStatusError(response, {
|
|
549
540
|
[ErrorCode.Conflict]: `Endpoint '${endpoint}' is already enrolled to receive notifications.`
|
|
550
541
|
});
|
|
551
542
|
}
|
|
552
|
-
}).catch((
|
|
553
|
-
|
|
543
|
+
}).catch((e) => {
|
|
544
|
+
error ||= getResultError(e, "Failed to enroll webhook.");
|
|
554
545
|
});
|
|
555
|
-
return
|
|
546
|
+
return { success: !error, error };
|
|
556
547
|
}
|
|
557
548
|
/**
|
|
558
549
|
* Retrieves all registered webhook endpoints associated with the customer.
|
|
@@ -563,18 +554,18 @@ class Webhooks {
|
|
|
563
554
|
* ```
|
|
564
555
|
*/
|
|
565
556
|
async list() {
|
|
566
|
-
|
|
557
|
+
let error = null;
|
|
567
558
|
const response = await this.mailchannels.get("/tx/v1/webhook", {
|
|
568
559
|
onResponseError: async ({ response: response2 }) => {
|
|
569
|
-
|
|
560
|
+
error = getStatusError(response2);
|
|
570
561
|
}
|
|
571
|
-
}).catch((
|
|
572
|
-
|
|
562
|
+
}).catch((e) => {
|
|
563
|
+
error ||= getResultError(e, "Failed to fetch webhooks.");
|
|
573
564
|
return null;
|
|
574
565
|
});
|
|
575
|
-
if (!response) return
|
|
576
|
-
|
|
577
|
-
return
|
|
566
|
+
if (!response) return { data: null, error };
|
|
567
|
+
const data = clean(response.map(({ webhook }) => webhook));
|
|
568
|
+
return { data, error: null };
|
|
578
569
|
}
|
|
579
570
|
/**
|
|
580
571
|
* Deletes all registered webhook endpoints for the customer.
|
|
@@ -585,20 +576,15 @@ class Webhooks {
|
|
|
585
576
|
* ```
|
|
586
577
|
*/
|
|
587
578
|
async delete() {
|
|
588
|
-
|
|
579
|
+
let error = null;
|
|
589
580
|
await this.mailchannels.delete("/tx/v1/webhook", {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (!response.ok) {
|
|
593
|
-
result.error = getStatusError(response);
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
result.success = true;
|
|
581
|
+
onResponseError: async ({ response }) => {
|
|
582
|
+
error = getStatusError(response);
|
|
597
583
|
}
|
|
598
|
-
}).catch((
|
|
599
|
-
|
|
584
|
+
}).catch((e) => {
|
|
585
|
+
error ||= getResultError(e, "Failed to delete webhooks.");
|
|
600
586
|
});
|
|
601
|
-
return
|
|
587
|
+
return { success: !error, error };
|
|
602
588
|
}
|
|
603
589
|
/**
|
|
604
590
|
* Retrieves the public key used to verify signatures on incoming webhook payloads.
|
|
@@ -610,24 +596,24 @@ class Webhooks {
|
|
|
610
596
|
* ```
|
|
611
597
|
*/
|
|
612
598
|
async getSigningKey(id) {
|
|
613
|
-
|
|
599
|
+
let error = null;
|
|
614
600
|
const response = await this.mailchannels.get("/tx/v1/webhook/public-key", {
|
|
615
601
|
query: {
|
|
616
602
|
id
|
|
617
603
|
},
|
|
618
604
|
onResponseError: async ({ response: response2 }) => {
|
|
619
|
-
|
|
605
|
+
error = getStatusError(response2, {
|
|
620
606
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
621
607
|
[ErrorCode.NotFound]: `The key '${id}' is not found.`
|
|
622
608
|
});
|
|
623
609
|
}
|
|
624
|
-
}).catch((
|
|
625
|
-
|
|
610
|
+
}).catch((e) => {
|
|
611
|
+
error ||= getResultError(e, "Failed to get signing key.");
|
|
626
612
|
return null;
|
|
627
613
|
});
|
|
628
|
-
if (!response) return
|
|
629
|
-
|
|
630
|
-
return
|
|
614
|
+
if (!response) return { data: null, error };
|
|
615
|
+
const data = clean({ key: response.key });
|
|
616
|
+
return { data, error: null };
|
|
631
617
|
}
|
|
632
618
|
/**
|
|
633
619
|
* Validates whether your enrolled webhook(s) respond with an HTTP `2xx` status code. Sends a test request to each webhook containing your customer handle, a hardcoded event type (`test`), a hardcoded sender email (`test@mailchannels.com`), a timestamp, a request ID (provided or generated), and an SMTP ID. The response includes the HTTP status code and body returned by each webhook.
|
|
@@ -639,31 +625,31 @@ class Webhooks {
|
|
|
639
625
|
* ```
|
|
640
626
|
*/
|
|
641
627
|
async validate(requestId) {
|
|
642
|
-
|
|
628
|
+
let error = null;
|
|
643
629
|
if (requestId && requestId.length > 28) {
|
|
644
|
-
|
|
645
|
-
return
|
|
630
|
+
error = createError("The request id should not exceed 28 characters.");
|
|
631
|
+
return { data: null, error };
|
|
646
632
|
}
|
|
647
633
|
const response = await this.mailchannels.post("/tx/v1/webhook/validate", {
|
|
648
634
|
body: {
|
|
649
635
|
request_id: requestId
|
|
650
636
|
},
|
|
651
637
|
onResponseError: async ({ response: response2 }) => {
|
|
652
|
-
|
|
638
|
+
error = getStatusError(response2, {
|
|
653
639
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
654
640
|
[ErrorCode.NotFound]: "No webhooks found for the account."
|
|
655
641
|
});
|
|
656
642
|
}
|
|
657
|
-
}).catch((
|
|
658
|
-
|
|
643
|
+
}).catch((e) => {
|
|
644
|
+
error ||= getResultError(e, "Failed to validate webhooks.");
|
|
659
645
|
return null;
|
|
660
646
|
});
|
|
661
|
-
if (!response) return
|
|
662
|
-
|
|
647
|
+
if (!response) return { data: null, error };
|
|
648
|
+
const data = clean({
|
|
663
649
|
allPassed: response.all_passed,
|
|
664
650
|
results: response.results
|
|
665
651
|
});
|
|
666
|
-
return
|
|
652
|
+
return { data, error: null };
|
|
667
653
|
}
|
|
668
654
|
}
|
|
669
655
|
|
|
@@ -684,17 +670,17 @@ class SubAccounts {
|
|
|
684
670
|
* ```
|
|
685
671
|
*/
|
|
686
672
|
async create(companyName, handle) {
|
|
687
|
-
|
|
673
|
+
let error = null;
|
|
688
674
|
const isValidCompany = SubAccounts.COMPANY_PATTERN.test(companyName);
|
|
689
675
|
if (!isValidCompany) {
|
|
690
|
-
|
|
691
|
-
return
|
|
676
|
+
error = createError("Invalid company name. Company name must be between 3 and 128 characters.");
|
|
677
|
+
return { data: null, error };
|
|
692
678
|
}
|
|
693
679
|
if (handle) {
|
|
694
680
|
const isValidHandle = SubAccounts.HANDLE_PATTERN.test(handle);
|
|
695
681
|
if (!isValidHandle) {
|
|
696
|
-
|
|
697
|
-
return
|
|
682
|
+
error = createError("Invalid handle. Sub-account handle must be between 3 and 128 characters and contain only lowercase letters and numbers.");
|
|
683
|
+
return { data: null, error };
|
|
698
684
|
}
|
|
699
685
|
}
|
|
700
686
|
const response = await this.mailchannels.post("/tx/v1/sub-account", {
|
|
@@ -703,22 +689,22 @@ class SubAccounts {
|
|
|
703
689
|
handle
|
|
704
690
|
},
|
|
705
691
|
onResponseError: async ({ response: response2 }) => {
|
|
706
|
-
|
|
692
|
+
error = getStatusError(response2, {
|
|
707
693
|
[ErrorCode.Forbidden]: "The parent account does not have permission to create sub-accounts.",
|
|
708
694
|
[ErrorCode.Conflict]: `Sub-account with handle '${handle}' already exists.`
|
|
709
695
|
});
|
|
710
696
|
}
|
|
711
|
-
}).catch((
|
|
712
|
-
|
|
697
|
+
}).catch((e) => {
|
|
698
|
+
error ||= getResultError(e, "Failed to create sub-account.");
|
|
713
699
|
return null;
|
|
714
700
|
});
|
|
715
|
-
if (!response) return
|
|
716
|
-
|
|
701
|
+
if (!response) return { data: null, error };
|
|
702
|
+
const data = clean({
|
|
717
703
|
companyName: response.company_name,
|
|
718
704
|
enabled: response.enabled,
|
|
719
705
|
handle: response.handle
|
|
720
706
|
});
|
|
721
|
-
return
|
|
707
|
+
return { data, error: null };
|
|
722
708
|
}
|
|
723
709
|
/**
|
|
724
710
|
* Retrieves all sub-accounts associated with the parent account. The response is paginated with a default limit of 1000 sub-accounts per page and an offset of 0.
|
|
@@ -730,25 +716,25 @@ class SubAccounts {
|
|
|
730
716
|
* ```
|
|
731
717
|
*/
|
|
732
718
|
async list(options) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
if (
|
|
719
|
+
let error = null;
|
|
720
|
+
error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
721
|
+
if (error) return { data: null, error };
|
|
736
722
|
const response = await this.mailchannels.get("/tx/v1/sub-account", {
|
|
737
723
|
query: options,
|
|
738
724
|
onResponseError: async ({ response: response2 }) => {
|
|
739
|
-
|
|
725
|
+
error = getStatusError(response2);
|
|
740
726
|
}
|
|
741
|
-
}).catch((
|
|
742
|
-
|
|
727
|
+
}).catch((e) => {
|
|
728
|
+
error ||= getResultError(e, "Failed to fetch sub-accounts.");
|
|
743
729
|
return null;
|
|
744
730
|
});
|
|
745
|
-
if (!response) return
|
|
746
|
-
|
|
731
|
+
if (!response) return { data: null, error };
|
|
732
|
+
const data = clean(response.map((account) => ({
|
|
747
733
|
companyName: account.company_name,
|
|
748
734
|
enabled: account.enabled,
|
|
749
735
|
handle: account.handle
|
|
750
736
|
})));
|
|
751
|
-
return
|
|
737
|
+
return { data, error: null };
|
|
752
738
|
}
|
|
753
739
|
/**
|
|
754
740
|
* Deletes the sub-account identified by its handle.
|
|
@@ -759,24 +745,19 @@ class SubAccounts {
|
|
|
759
745
|
* ```
|
|
760
746
|
*/
|
|
761
747
|
async delete(handle) {
|
|
762
|
-
|
|
748
|
+
let error = null;
|
|
763
749
|
if (!handle) {
|
|
764
|
-
|
|
765
|
-
return
|
|
750
|
+
error = createError("No handle provided.");
|
|
751
|
+
return { success: false, error };
|
|
766
752
|
}
|
|
767
753
|
await this.mailchannels.delete(`/tx/v1/sub-account/${handle}`, {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
if (!response.ok) {
|
|
771
|
-
result.error = getStatusError(response);
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
result.success = true;
|
|
754
|
+
onResponseError: async ({ response }) => {
|
|
755
|
+
error = getStatusError(response);
|
|
775
756
|
}
|
|
776
|
-
}).catch((
|
|
777
|
-
|
|
757
|
+
}).catch((e) => {
|
|
758
|
+
error ||= getResultError(e, "Failed to delete sub-account.");
|
|
778
759
|
});
|
|
779
|
-
return
|
|
760
|
+
return { success: !error, error };
|
|
780
761
|
}
|
|
781
762
|
/**
|
|
782
763
|
* Suspends the sub-account identified by its handle. This action disables the account, preventing it from sending any emails until it is reactivated.
|
|
@@ -788,26 +769,21 @@ class SubAccounts {
|
|
|
788
769
|
* ```
|
|
789
770
|
*/
|
|
790
771
|
async suspend(handle) {
|
|
791
|
-
|
|
772
|
+
let error = null;
|
|
792
773
|
if (!handle) {
|
|
793
|
-
|
|
794
|
-
return
|
|
774
|
+
error = createError("No handle provided.");
|
|
775
|
+
return { success: false, error };
|
|
795
776
|
}
|
|
796
777
|
await this.mailchannels.post(`/tx/v1/sub-account/${handle}/suspend`, {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if (response.ok) {
|
|
800
|
-
result.success = true;
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
result.error = getStatusError(response, {
|
|
778
|
+
onResponseError: async ({ response }) => {
|
|
779
|
+
error = getStatusError(response, {
|
|
804
780
|
[ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
|
|
805
781
|
});
|
|
806
782
|
}
|
|
807
|
-
}).catch((
|
|
808
|
-
|
|
783
|
+
}).catch((e) => {
|
|
784
|
+
error ||= getResultError(e, "Failed to suspend sub-account.");
|
|
809
785
|
});
|
|
810
|
-
return
|
|
786
|
+
return { success: !error, error };
|
|
811
787
|
}
|
|
812
788
|
/**
|
|
813
789
|
* Activates a suspended sub-account identified by its handle, restoring its ability to send emails.
|
|
@@ -819,27 +795,22 @@ class SubAccounts {
|
|
|
819
795
|
* ```
|
|
820
796
|
*/
|
|
821
797
|
async activate(handle) {
|
|
822
|
-
|
|
798
|
+
let error = null;
|
|
823
799
|
if (!handle) {
|
|
824
|
-
|
|
825
|
-
return
|
|
800
|
+
error = createError("No handle provided.");
|
|
801
|
+
return { success: false, error };
|
|
826
802
|
}
|
|
827
803
|
await this.mailchannels.post(`/tx/v1/sub-account/${handle}/activate`, {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (response.ok) {
|
|
831
|
-
result.success = true;
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
result.error = getStatusError(response, {
|
|
804
|
+
onResponseError: async ({ response }) => {
|
|
805
|
+
error = getStatusError(response, {
|
|
835
806
|
[ErrorCode.Forbidden]: "The parent account does not have permission to activate the sub-account.",
|
|
836
807
|
[ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
|
|
837
808
|
});
|
|
838
809
|
}
|
|
839
|
-
}).catch((
|
|
840
|
-
|
|
810
|
+
}).catch((e) => {
|
|
811
|
+
error ||= getResultError(e, "Failed to activate sub-account.");
|
|
841
812
|
});
|
|
842
|
-
return
|
|
813
|
+
return { success: !error, error };
|
|
843
814
|
}
|
|
844
815
|
/**
|
|
845
816
|
* Creates a new API key for the specified sub-account.
|
|
@@ -851,29 +822,29 @@ class SubAccounts {
|
|
|
851
822
|
* ```
|
|
852
823
|
*/
|
|
853
824
|
async createApiKey(handle) {
|
|
854
|
-
|
|
825
|
+
let error = null;
|
|
855
826
|
if (!handle) {
|
|
856
|
-
|
|
857
|
-
return
|
|
827
|
+
error = createError("No handle provided.");
|
|
828
|
+
return { data: null, error };
|
|
858
829
|
}
|
|
859
830
|
const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/api-key`, {
|
|
860
831
|
onResponseError: async ({ response: response2 }) => {
|
|
861
|
-
|
|
832
|
+
error = getStatusError(response2, {
|
|
862
833
|
[ErrorCode.Forbidden]: "You can't create API keys for this sub-account.",
|
|
863
834
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
|
|
864
835
|
[ErrorCode.UnprocessableEntity]: "You have reached the limit of API keys you can create for this sub-account."
|
|
865
836
|
});
|
|
866
837
|
}
|
|
867
|
-
}).catch((
|
|
868
|
-
|
|
838
|
+
}).catch((e) => {
|
|
839
|
+
error ||= getResultError(e, "Failed to create sub-account API key.");
|
|
869
840
|
return null;
|
|
870
841
|
});
|
|
871
|
-
if (!response) return
|
|
872
|
-
|
|
842
|
+
if (!response) return { data: null, error };
|
|
843
|
+
const data = clean({
|
|
873
844
|
id: response.id,
|
|
874
845
|
value: response.key
|
|
875
846
|
});
|
|
876
|
-
return
|
|
847
|
+
return { data, error: null };
|
|
877
848
|
}
|
|
878
849
|
/**
|
|
879
850
|
* Retrieves details of all API keys associated with the specified sub-account. For security reasons, the full API key is not returned; only the key ID and a partially redacted version are provided.
|
|
@@ -886,29 +857,29 @@ class SubAccounts {
|
|
|
886
857
|
* ```
|
|
887
858
|
*/
|
|
888
859
|
async listApiKeys(handle, options) {
|
|
889
|
-
|
|
860
|
+
let error = null;
|
|
890
861
|
if (!handle) {
|
|
891
|
-
|
|
892
|
-
return
|
|
862
|
+
error = createError("No handle provided.");
|
|
863
|
+
return { data: null, error };
|
|
893
864
|
}
|
|
894
|
-
|
|
895
|
-
if (
|
|
865
|
+
error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
866
|
+
if (error) return { data: null, error };
|
|
896
867
|
const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/api-key`, {
|
|
897
868
|
onResponseError: async ({ response: response2 }) => {
|
|
898
|
-
|
|
869
|
+
error = getStatusError(response2, {
|
|
899
870
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
900
871
|
});
|
|
901
872
|
}
|
|
902
|
-
}).catch((
|
|
903
|
-
|
|
873
|
+
}).catch((e) => {
|
|
874
|
+
error ||= getResultError(e, "Failed to fetch sub-account API keys.");
|
|
904
875
|
return null;
|
|
905
876
|
});
|
|
906
|
-
if (!response) return
|
|
907
|
-
|
|
877
|
+
if (!response) return { data: null, error };
|
|
878
|
+
const data = clean(response.map((key) => ({
|
|
908
879
|
id: key.id,
|
|
909
880
|
value: key.key
|
|
910
881
|
})));
|
|
911
|
-
return
|
|
882
|
+
return { data, error: null };
|
|
912
883
|
}
|
|
913
884
|
/**
|
|
914
885
|
* Deletes the API key identified by its ID for the specified sub-account.
|
|
@@ -921,26 +892,21 @@ class SubAccounts {
|
|
|
921
892
|
* ```
|
|
922
893
|
*/
|
|
923
894
|
async deleteApiKey(handle, id) {
|
|
924
|
-
|
|
895
|
+
let error = null;
|
|
925
896
|
if (!handle) {
|
|
926
|
-
|
|
927
|
-
return
|
|
897
|
+
error = createError("No handle provided.");
|
|
898
|
+
return { success: false, error };
|
|
928
899
|
}
|
|
929
900
|
await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/api-key/${id}`, {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (response.ok) {
|
|
933
|
-
result.success = true;
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
result.error = getStatusError(response, {
|
|
901
|
+
onResponseError: async ({ response }) => {
|
|
902
|
+
error = getStatusError(response, {
|
|
937
903
|
[ErrorCode.BadRequest]: "Missing or invalid API key ID."
|
|
938
904
|
});
|
|
939
905
|
}
|
|
940
|
-
}).catch((
|
|
941
|
-
|
|
906
|
+
}).catch((e) => {
|
|
907
|
+
error ||= getResultError(e, "Failed to delete sub-account API key.");
|
|
942
908
|
});
|
|
943
|
-
return
|
|
909
|
+
return { success: !error, error };
|
|
944
910
|
}
|
|
945
911
|
/**
|
|
946
912
|
* Creates a new SMTP password for the specified sub-account.
|
|
@@ -952,30 +918,30 @@ class SubAccounts {
|
|
|
952
918
|
* ```
|
|
953
919
|
*/
|
|
954
920
|
async createSmtpPassword(handle) {
|
|
955
|
-
|
|
921
|
+
let error = null;
|
|
956
922
|
if (!handle) {
|
|
957
|
-
|
|
958
|
-
return
|
|
923
|
+
error = createError("No handle provided.");
|
|
924
|
+
return { data: null, error };
|
|
959
925
|
}
|
|
960
926
|
const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/smtp-password`, {
|
|
961
927
|
onResponseError: async ({ response: response2 }) => {
|
|
962
|
-
|
|
928
|
+
error = getStatusError(response2, {
|
|
963
929
|
[ErrorCode.Forbidden]: "You can't create SMTP passwords for this sub-account.",
|
|
964
930
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
|
|
965
931
|
[ErrorCode.UnprocessableEntity]: "You have reached the limit of SMTP passwords you can create for this sub-account."
|
|
966
932
|
});
|
|
967
933
|
}
|
|
968
|
-
}).catch((
|
|
969
|
-
|
|
934
|
+
}).catch((e) => {
|
|
935
|
+
error ||= getResultError(e, "Failed to create sub-account SMTP password.");
|
|
970
936
|
return null;
|
|
971
937
|
});
|
|
972
|
-
if (!response) return
|
|
973
|
-
|
|
938
|
+
if (!response) return { data: null, error };
|
|
939
|
+
const data = clean({
|
|
974
940
|
enabled: response.enabled,
|
|
975
941
|
id: response.id,
|
|
976
942
|
value: response.smtp_password
|
|
977
943
|
});
|
|
978
|
-
return
|
|
944
|
+
return { data, error: null };
|
|
979
945
|
}
|
|
980
946
|
/**
|
|
981
947
|
* Retrieves details of all SMTP passwords associated with the specified sub-account. For security, the full SMTP password is not returned; only the password ID and a partially redacted version are provided.
|
|
@@ -987,28 +953,28 @@ class SubAccounts {
|
|
|
987
953
|
* ```
|
|
988
954
|
*/
|
|
989
955
|
async listSmtpPasswords(handle) {
|
|
990
|
-
|
|
956
|
+
let error = null;
|
|
991
957
|
if (!handle) {
|
|
992
|
-
|
|
993
|
-
return
|
|
958
|
+
error = createError("No handle provided.");
|
|
959
|
+
return { data: null, error };
|
|
994
960
|
}
|
|
995
961
|
const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/smtp-password`, {
|
|
996
962
|
onResponseError: async ({ response: response2 }) => {
|
|
997
|
-
|
|
963
|
+
error = getStatusError(response2, {
|
|
998
964
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
999
965
|
});
|
|
1000
966
|
}
|
|
1001
|
-
}).catch((
|
|
1002
|
-
|
|
967
|
+
}).catch((e) => {
|
|
968
|
+
error ||= getResultError(e, "Failed to fetch sub-account SMTP passwords.");
|
|
1003
969
|
return null;
|
|
1004
970
|
});
|
|
1005
|
-
if (!response) return
|
|
1006
|
-
|
|
971
|
+
if (!response) return { data: null, error };
|
|
972
|
+
const data = clean(response.map((password) => ({
|
|
1007
973
|
enabled: password.enabled,
|
|
1008
974
|
id: password.id,
|
|
1009
975
|
value: password.smtp_password
|
|
1010
976
|
})));
|
|
1011
|
-
return
|
|
977
|
+
return { data, error: null };
|
|
1012
978
|
}
|
|
1013
979
|
/**
|
|
1014
980
|
* Deletes the SMTP password identified by its ID for the specified sub-account.
|
|
@@ -1021,26 +987,21 @@ class SubAccounts {
|
|
|
1021
987
|
* ```
|
|
1022
988
|
*/
|
|
1023
989
|
async deleteSmtpPassword(handle, id) {
|
|
1024
|
-
|
|
990
|
+
let error = null;
|
|
1025
991
|
if (!handle) {
|
|
1026
|
-
|
|
1027
|
-
return
|
|
992
|
+
error = createError("No handle provided.");
|
|
993
|
+
return { success: false, error };
|
|
1028
994
|
}
|
|
1029
995
|
await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/smtp-password/${id}`, {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
if (response.ok) {
|
|
1033
|
-
result.success = true;
|
|
1034
|
-
return;
|
|
1035
|
-
}
|
|
1036
|
-
result.error = getStatusError(response, {
|
|
996
|
+
onResponseError: async ({ response }) => {
|
|
997
|
+
error = getStatusError(response, {
|
|
1037
998
|
[ErrorCode.BadRequest]: "Missing or invalid SMTP password ID."
|
|
1038
999
|
});
|
|
1039
1000
|
}
|
|
1040
|
-
}).catch((
|
|
1041
|
-
|
|
1001
|
+
}).catch((e) => {
|
|
1002
|
+
error ||= getResultError(e, "Failed to delete sub-account SMTP password.");
|
|
1042
1003
|
});
|
|
1043
|
-
return
|
|
1004
|
+
return { success: !error, error };
|
|
1044
1005
|
}
|
|
1045
1006
|
/**
|
|
1046
1007
|
* Retrieves the limit of a specified sub-account. A value of `-1` indicates that the sub-account inherits the parent account's limit, allowing the sub-account to utilize any remaining capacity within the parent account's allocation.
|
|
@@ -1052,24 +1013,24 @@ class SubAccounts {
|
|
|
1052
1013
|
* ```
|
|
1053
1014
|
*/
|
|
1054
1015
|
async getLimit(handle) {
|
|
1055
|
-
|
|
1016
|
+
let error = null;
|
|
1056
1017
|
if (!handle) {
|
|
1057
|
-
|
|
1058
|
-
return
|
|
1018
|
+
error = createError("No handle provided.");
|
|
1019
|
+
return { data: null, error };
|
|
1059
1020
|
}
|
|
1060
1021
|
const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/limit`, {
|
|
1061
1022
|
onResponseError: async ({ response: response2 }) => {
|
|
1062
|
-
|
|
1023
|
+
error = getStatusError(response2, {
|
|
1063
1024
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1064
1025
|
});
|
|
1065
1026
|
}
|
|
1066
|
-
}).catch((
|
|
1067
|
-
|
|
1027
|
+
}).catch((e) => {
|
|
1028
|
+
error ||= getResultError(e, "Failed to fetch sub-account limit.");
|
|
1068
1029
|
return null;
|
|
1069
1030
|
});
|
|
1070
|
-
if (!response) return
|
|
1071
|
-
|
|
1072
|
-
return
|
|
1031
|
+
if (!response) return { data: null, error };
|
|
1032
|
+
const data = clean(response);
|
|
1033
|
+
return { data, error: null };
|
|
1073
1034
|
}
|
|
1074
1035
|
/**
|
|
1075
1036
|
* Sets the limit for the specified sub-account.
|
|
@@ -1082,28 +1043,23 @@ class SubAccounts {
|
|
|
1082
1043
|
* ```
|
|
1083
1044
|
*/
|
|
1084
1045
|
async setLimit(handle, limit) {
|
|
1085
|
-
|
|
1046
|
+
let error = null;
|
|
1086
1047
|
if (!handle) {
|
|
1087
|
-
|
|
1088
|
-
return
|
|
1048
|
+
error = createError("No handle provided.");
|
|
1049
|
+
return { success: false, error };
|
|
1089
1050
|
}
|
|
1090
1051
|
await this.mailchannels.put(`/tx/v1/sub-account/${handle}/limit`, {
|
|
1091
1052
|
body: limit,
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
if (response.ok) {
|
|
1095
|
-
result.success = true;
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
result.error = getStatusError(response, {
|
|
1053
|
+
onResponseError: async ({ response }) => {
|
|
1054
|
+
error = getStatusError(response, {
|
|
1099
1055
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
1100
1056
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1101
1057
|
});
|
|
1102
1058
|
}
|
|
1103
|
-
}).catch((
|
|
1104
|
-
|
|
1059
|
+
}).catch((e) => {
|
|
1060
|
+
error ||= getResultError(e, "Failed to set sub-account limit.");
|
|
1105
1061
|
});
|
|
1106
|
-
return
|
|
1062
|
+
return { success: !error, error };
|
|
1107
1063
|
}
|
|
1108
1064
|
/**
|
|
1109
1065
|
* Deletes the limit for the specified sub-account. After a successful deletion, the specified sub-account will be limited to the parent account's limit.
|
|
@@ -1115,26 +1071,21 @@ class SubAccounts {
|
|
|
1115
1071
|
* ```
|
|
1116
1072
|
*/
|
|
1117
1073
|
async deleteLimit(handle) {
|
|
1118
|
-
|
|
1074
|
+
let error = null;
|
|
1119
1075
|
if (!handle) {
|
|
1120
|
-
|
|
1121
|
-
return
|
|
1076
|
+
error = createError("No handle provided.");
|
|
1077
|
+
return { success: false, error };
|
|
1122
1078
|
}
|
|
1123
1079
|
await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/limit`, {
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
if (response.ok) {
|
|
1127
|
-
result.success = true;
|
|
1128
|
-
return;
|
|
1129
|
-
}
|
|
1130
|
-
result.error = getStatusError(response, {
|
|
1080
|
+
onResponseError: async ({ response }) => {
|
|
1081
|
+
error = getStatusError(response, {
|
|
1131
1082
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1132
1083
|
});
|
|
1133
1084
|
}
|
|
1134
|
-
}).catch((
|
|
1135
|
-
|
|
1085
|
+
}).catch((e) => {
|
|
1086
|
+
error ||= getResultError(e, "Failed to delete sub-account limit.");
|
|
1136
1087
|
});
|
|
1137
|
-
return
|
|
1088
|
+
return { success: !error, error };
|
|
1138
1089
|
}
|
|
1139
1090
|
/**
|
|
1140
1091
|
* Retrieves usage statistics for the specified sub-account during the current billing period.
|
|
@@ -1146,28 +1097,28 @@ class SubAccounts {
|
|
|
1146
1097
|
* ```
|
|
1147
1098
|
*/
|
|
1148
1099
|
async getUsage(handle) {
|
|
1149
|
-
|
|
1100
|
+
let error = null;
|
|
1150
1101
|
if (!handle) {
|
|
1151
|
-
|
|
1152
|
-
return
|
|
1102
|
+
error = createError("No handle provided.");
|
|
1103
|
+
return { data: null, error };
|
|
1153
1104
|
}
|
|
1154
1105
|
const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/usage`, {
|
|
1155
1106
|
onResponseError: async ({ response: response2 }) => {
|
|
1156
|
-
|
|
1107
|
+
error = getStatusError(response2, {
|
|
1157
1108
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1158
1109
|
});
|
|
1159
1110
|
}
|
|
1160
|
-
}).catch((
|
|
1161
|
-
|
|
1111
|
+
}).catch((e) => {
|
|
1112
|
+
error ||= getResultError(e, "Failed to fetch sub-account usage.");
|
|
1162
1113
|
return null;
|
|
1163
1114
|
});
|
|
1164
|
-
if (!response) return
|
|
1165
|
-
|
|
1115
|
+
if (!response) return { data: null, error };
|
|
1116
|
+
const data = clean({
|
|
1166
1117
|
endDate: response.period_end_date,
|
|
1167
1118
|
startDate: response.period_start_date,
|
|
1168
1119
|
total: response.total_usage
|
|
1169
1120
|
});
|
|
1170
|
-
return
|
|
1121
|
+
return { data, error: null };
|
|
1171
1122
|
}
|
|
1172
1123
|
}
|
|
1173
1124
|
|
|
@@ -1185,7 +1136,7 @@ class Metrics {
|
|
|
1185
1136
|
* ```
|
|
1186
1137
|
*/
|
|
1187
1138
|
async engagement(options) {
|
|
1188
|
-
|
|
1139
|
+
let error = null;
|
|
1189
1140
|
const response = await this.mailchannels.get("/tx/v1/metrics/engagement", {
|
|
1190
1141
|
query: {
|
|
1191
1142
|
start_time: options?.startTime,
|
|
@@ -1194,16 +1145,16 @@ class Metrics {
|
|
|
1194
1145
|
interval: options?.interval
|
|
1195
1146
|
},
|
|
1196
1147
|
onResponseError: async ({ response: response2 }) => {
|
|
1197
|
-
|
|
1148
|
+
error = getStatusError(response2, {
|
|
1198
1149
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1199
1150
|
});
|
|
1200
1151
|
}
|
|
1201
|
-
}).catch((
|
|
1202
|
-
|
|
1152
|
+
}).catch((e) => {
|
|
1153
|
+
error ||= getResultError(e, "Failed to fetch engagement metrics.");
|
|
1203
1154
|
return null;
|
|
1204
1155
|
});
|
|
1205
|
-
if (!response) return
|
|
1206
|
-
|
|
1156
|
+
if (!response) return { data: null, error };
|
|
1157
|
+
const data = clean({
|
|
1207
1158
|
buckets: {
|
|
1208
1159
|
click: mapBuckets(response.buckets.click),
|
|
1209
1160
|
clickTrackingDelivered: mapBuckets(response.buckets.click_tracking_delivered),
|
|
@@ -1217,7 +1168,7 @@ class Metrics {
|
|
|
1217
1168
|
openTrackingDelivered: response.open_tracking_delivered,
|
|
1218
1169
|
startTime: response.start_time
|
|
1219
1170
|
});
|
|
1220
|
-
return
|
|
1171
|
+
return { data, error: null };
|
|
1221
1172
|
}
|
|
1222
1173
|
/**
|
|
1223
1174
|
* Retrieve performance metrics for messages sent from your account, including counts of processed, delivered, hard-bounced events. Supports optional filters for time range, and campaign ID.
|
|
@@ -1229,7 +1180,7 @@ class Metrics {
|
|
|
1229
1180
|
* ```
|
|
1230
1181
|
*/
|
|
1231
1182
|
async performance(options) {
|
|
1232
|
-
|
|
1183
|
+
let error = null;
|
|
1233
1184
|
const response = await this.mailchannels.get("/tx/v1/metrics/performance", {
|
|
1234
1185
|
query: {
|
|
1235
1186
|
start_time: options?.startTime,
|
|
@@ -1238,16 +1189,16 @@ class Metrics {
|
|
|
1238
1189
|
interval: options?.interval
|
|
1239
1190
|
},
|
|
1240
1191
|
onResponseError: async ({ response: response2 }) => {
|
|
1241
|
-
|
|
1192
|
+
error = getStatusError(response2, {
|
|
1242
1193
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1243
1194
|
});
|
|
1244
1195
|
}
|
|
1245
|
-
}).catch((
|
|
1246
|
-
|
|
1196
|
+
}).catch((e) => {
|
|
1197
|
+
error ||= getResultError(e, "Failed to fetch performance metrics.");
|
|
1247
1198
|
return null;
|
|
1248
1199
|
});
|
|
1249
|
-
if (!response) return
|
|
1250
|
-
|
|
1200
|
+
if (!response) return { data: null, error };
|
|
1201
|
+
const data = clean({
|
|
1251
1202
|
bounced: response.bounced,
|
|
1252
1203
|
buckets: {
|
|
1253
1204
|
bounced: mapBuckets(response.buckets.bounced),
|
|
@@ -1259,7 +1210,7 @@ class Metrics {
|
|
|
1259
1210
|
processed: response.processed,
|
|
1260
1211
|
startTime: response.start_time
|
|
1261
1212
|
});
|
|
1262
|
-
return
|
|
1213
|
+
return { data, error: null };
|
|
1263
1214
|
}
|
|
1264
1215
|
/**
|
|
1265
1216
|
* Retrieve recipient behaviour metrics for messages sent from your account, including counts of unsubscribed events. Supports optional filters for time range, and campaign ID.
|
|
@@ -1271,7 +1222,7 @@ class Metrics {
|
|
|
1271
1222
|
* ```
|
|
1272
1223
|
*/
|
|
1273
1224
|
async recipientBehaviour(options) {
|
|
1274
|
-
|
|
1225
|
+
let error = null;
|
|
1275
1226
|
const response = await this.mailchannels.get("/tx/v1/metrics/recipient-behaviour", {
|
|
1276
1227
|
query: {
|
|
1277
1228
|
start_time: options?.startTime,
|
|
@@ -1280,16 +1231,16 @@ class Metrics {
|
|
|
1280
1231
|
interval: options?.interval
|
|
1281
1232
|
},
|
|
1282
1233
|
onResponseError: async ({ response: response2 }) => {
|
|
1283
|
-
|
|
1234
|
+
error = getStatusError(response2, {
|
|
1284
1235
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1285
1236
|
});
|
|
1286
1237
|
}
|
|
1287
|
-
}).catch((
|
|
1288
|
-
|
|
1238
|
+
}).catch((e) => {
|
|
1239
|
+
error ||= getResultError(e, "Failed to fetch recipient behaviour metrics.");
|
|
1289
1240
|
return null;
|
|
1290
1241
|
});
|
|
1291
|
-
if (!response) return
|
|
1292
|
-
|
|
1242
|
+
if (!response) return { data: null, error };
|
|
1243
|
+
const data = clean({
|
|
1293
1244
|
buckets: {
|
|
1294
1245
|
unsubscribeDelivered: mapBuckets(response.buckets.unsubscribe_delivered),
|
|
1295
1246
|
unsubscribed: mapBuckets(response.buckets.unsubscribed)
|
|
@@ -1299,7 +1250,7 @@ class Metrics {
|
|
|
1299
1250
|
unsubscribeDelivered: response.unsubscribe_delivered,
|
|
1300
1251
|
unsubscribed: response.unsubscribed
|
|
1301
1252
|
});
|
|
1302
|
-
return
|
|
1253
|
+
return { data, error: null };
|
|
1303
1254
|
}
|
|
1304
1255
|
/**
|
|
1305
1256
|
* Retrieve volume metrics for messages sent from your account, including counts of processed, delivered and dropped events. Supports optional filters for time range and campaign ID.
|
|
@@ -1311,7 +1262,7 @@ class Metrics {
|
|
|
1311
1262
|
* ```
|
|
1312
1263
|
*/
|
|
1313
1264
|
async volume(options) {
|
|
1314
|
-
|
|
1265
|
+
let error = null;
|
|
1315
1266
|
const response = await this.mailchannels.get("/tx/v1/metrics/volume", {
|
|
1316
1267
|
query: {
|
|
1317
1268
|
start_time: options?.startTime,
|
|
@@ -1320,16 +1271,16 @@ class Metrics {
|
|
|
1320
1271
|
interval: options?.interval
|
|
1321
1272
|
},
|
|
1322
1273
|
onResponseError: async ({ response: response2 }) => {
|
|
1323
|
-
|
|
1274
|
+
error = getStatusError(response2, {
|
|
1324
1275
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1325
1276
|
});
|
|
1326
1277
|
}
|
|
1327
|
-
}).catch((
|
|
1328
|
-
|
|
1278
|
+
}).catch((e) => {
|
|
1279
|
+
error ||= getResultError(e, "Failed to fetch volume metrics.");
|
|
1329
1280
|
return null;
|
|
1330
1281
|
});
|
|
1331
|
-
if (!response) return
|
|
1332
|
-
|
|
1282
|
+
if (!response) return { data: null, error };
|
|
1283
|
+
const data = clean({
|
|
1333
1284
|
buckets: {
|
|
1334
1285
|
delivered: mapBuckets(response.buckets.delivered),
|
|
1335
1286
|
dropped: mapBuckets(response.buckets.dropped),
|
|
@@ -1341,7 +1292,7 @@ class Metrics {
|
|
|
1341
1292
|
processed: response.processed,
|
|
1342
1293
|
startTime: response.start_time
|
|
1343
1294
|
});
|
|
1344
|
-
return
|
|
1295
|
+
return { data, error: null };
|
|
1345
1296
|
}
|
|
1346
1297
|
/**
|
|
1347
1298
|
* Retrieves usage statistics during the current billing period.
|
|
@@ -1352,22 +1303,22 @@ class Metrics {
|
|
|
1352
1303
|
* ```
|
|
1353
1304
|
*/
|
|
1354
1305
|
async usage() {
|
|
1355
|
-
|
|
1306
|
+
let error = null;
|
|
1356
1307
|
const response = await this.mailchannels.get("/tx/v1/usage", {
|
|
1357
1308
|
onResponseError: async ({ response: response2 }) => {
|
|
1358
|
-
|
|
1309
|
+
error = getStatusError(response2);
|
|
1359
1310
|
}
|
|
1360
|
-
}).catch((
|
|
1361
|
-
|
|
1311
|
+
}).catch((e) => {
|
|
1312
|
+
error ||= getResultError(e, "Failed to fetch usage metrics.");
|
|
1362
1313
|
return null;
|
|
1363
1314
|
});
|
|
1364
|
-
if (!response) return
|
|
1365
|
-
|
|
1315
|
+
if (!response) return { data: null, error };
|
|
1316
|
+
const data = clean({
|
|
1366
1317
|
endDate: response.period_end_date,
|
|
1367
1318
|
startDate: response.period_start_date,
|
|
1368
1319
|
total: response.total_usage
|
|
1369
1320
|
});
|
|
1370
|
-
return
|
|
1321
|
+
return { data, error: null };
|
|
1371
1322
|
}
|
|
1372
1323
|
/**
|
|
1373
1324
|
* Retrieves a list of senders, either sub-accounts or campaigns, with their associated message metrics. Sorted by total # of sent messages (processed + dropped). Supports optional filter for time range, and optional settings for limit, offset, and sort order. Note: senders without any messages in the given time range will not be included in the results. The default time range is from one month ago to now, and the default sort order is descending.
|
|
@@ -1380,9 +1331,9 @@ class Metrics {
|
|
|
1380
1331
|
* ```
|
|
1381
1332
|
*/
|
|
1382
1333
|
async senders(type, options) {
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
if (
|
|
1334
|
+
let error = null;
|
|
1335
|
+
error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
1336
|
+
if (error) return { data: null, error };
|
|
1386
1337
|
const response = await this.mailchannels.get(`/tx/v1/metrics/senders/${type}`, {
|
|
1387
1338
|
query: {
|
|
1388
1339
|
start_time: options?.startTime,
|
|
@@ -1392,16 +1343,16 @@ class Metrics {
|
|
|
1392
1343
|
sort_order: options?.sortOrder
|
|
1393
1344
|
},
|
|
1394
1345
|
onResponseError: async ({ response: response2 }) => {
|
|
1395
|
-
|
|
1346
|
+
error = getStatusError(response2, {
|
|
1396
1347
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1397
1348
|
});
|
|
1398
1349
|
}
|
|
1399
|
-
}).catch((
|
|
1400
|
-
|
|
1350
|
+
}).catch((e) => {
|
|
1351
|
+
error ||= getResultError(e, "Failed to fetch senders metrics.");
|
|
1401
1352
|
return null;
|
|
1402
1353
|
});
|
|
1403
|
-
if (!response) return
|
|
1404
|
-
|
|
1354
|
+
if (!response) return { data: null, error };
|
|
1355
|
+
const data = clean({
|
|
1405
1356
|
endTime: response.end_time,
|
|
1406
1357
|
limit: response.limit,
|
|
1407
1358
|
offset: response.offset,
|
|
@@ -1409,7 +1360,7 @@ class Metrics {
|
|
|
1409
1360
|
startTime: response.start_time,
|
|
1410
1361
|
total: response.total
|
|
1411
1362
|
});
|
|
1412
|
-
return
|
|
1363
|
+
return { data, error: null };
|
|
1413
1364
|
}
|
|
1414
1365
|
}
|
|
1415
1366
|
|
|
@@ -1428,7 +1379,7 @@ class Suppressions {
|
|
|
1428
1379
|
* });
|
|
1429
1380
|
*/
|
|
1430
1381
|
async create(options) {
|
|
1431
|
-
|
|
1382
|
+
let error = null;
|
|
1432
1383
|
const { addToSubAccounts, entries } = options;
|
|
1433
1384
|
const payload = {
|
|
1434
1385
|
add_to_sub_accounts: addToSubAccounts,
|
|
@@ -1441,22 +1392,17 @@ class Suppressions {
|
|
|
1441
1392
|
};
|
|
1442
1393
|
await this.mailchannels.post("/tx/v1/suppression-list", {
|
|
1443
1394
|
body: payload,
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
if (response.ok) {
|
|
1447
|
-
result.success = true;
|
|
1448
|
-
return;
|
|
1449
|
-
}
|
|
1450
|
-
result.error = getStatusError(response, {
|
|
1395
|
+
onResponseError: async ({ response }) => {
|
|
1396
|
+
error = getStatusError(response, {
|
|
1451
1397
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
1452
1398
|
[ErrorCode.Conflict]: "Conflict. One or more suppression entries in the request already exist and cannot be created again.",
|
|
1453
1399
|
[ErrorCode.PayloadTooLarge]: "Payload too large. The request exceeds the maximum allowed total of 1000 suppression entries for the parent account and/or its sub-accounts."
|
|
1454
1400
|
});
|
|
1455
1401
|
}
|
|
1456
|
-
}).catch((
|
|
1457
|
-
|
|
1402
|
+
}).catch((e) => {
|
|
1403
|
+
error ||= getResultError(e, "Failed to create suppression entries.");
|
|
1458
1404
|
});
|
|
1459
|
-
return
|
|
1405
|
+
return { success: !error, error };
|
|
1460
1406
|
}
|
|
1461
1407
|
/**
|
|
1462
1408
|
* Deletes suppression entry associated with the account based on the specified recipient and source.
|
|
@@ -1469,25 +1415,20 @@ class Suppressions {
|
|
|
1469
1415
|
* ```
|
|
1470
1416
|
*/
|
|
1471
1417
|
async delete(recipient, source) {
|
|
1472
|
-
|
|
1418
|
+
let error = null;
|
|
1473
1419
|
await this.mailchannels.delete(`/tx/v1/suppression-list/recipients/${recipient}`, {
|
|
1474
1420
|
query: {
|
|
1475
1421
|
source
|
|
1476
1422
|
},
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
if (response.ok) {
|
|
1480
|
-
result.success = true;
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1483
|
-
result.error = getStatusError(response, {
|
|
1423
|
+
onResponseError: async ({ response }) => {
|
|
1424
|
+
error = getStatusError(response, {
|
|
1484
1425
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1485
1426
|
});
|
|
1486
1427
|
}
|
|
1487
|
-
}).catch((
|
|
1488
|
-
|
|
1428
|
+
}).catch((e) => {
|
|
1429
|
+
error ||= getResultError(e, "Failed to delete suppression entry.");
|
|
1489
1430
|
});
|
|
1490
|
-
return
|
|
1431
|
+
return { success: !error, error };
|
|
1491
1432
|
}
|
|
1492
1433
|
/**
|
|
1493
1434
|
* Retrieve suppression entries associated with the specified account. Supports filtering by recipient, source and creation date range. The response is paginated, with a default limit of `1000` entries per page and an offset of `0`.
|
|
@@ -1499,9 +1440,9 @@ class Suppressions {
|
|
|
1499
1440
|
* @param options - Options to filter and customize the suppression entries retrieval.
|
|
1500
1441
|
*/
|
|
1501
1442
|
async list(options) {
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
if (
|
|
1443
|
+
let error = null;
|
|
1444
|
+
error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
1445
|
+
if (error) return { data: null, error };
|
|
1505
1446
|
const payload = {
|
|
1506
1447
|
recipient: options?.recipient,
|
|
1507
1448
|
source: options?.source,
|
|
@@ -1513,16 +1454,16 @@ class Suppressions {
|
|
|
1513
1454
|
const response = await this.mailchannels.get("/tx/v1/suppression-list", {
|
|
1514
1455
|
query: payload,
|
|
1515
1456
|
onResponseError: async ({ response: response2 }) => {
|
|
1516
|
-
|
|
1457
|
+
error = getStatusError(response2, {
|
|
1517
1458
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1518
1459
|
});
|
|
1519
1460
|
}
|
|
1520
|
-
}).catch((
|
|
1521
|
-
|
|
1461
|
+
}).catch((e) => {
|
|
1462
|
+
error ||= getResultError(e, "Failed to fetch suppression entries.");
|
|
1522
1463
|
return null;
|
|
1523
1464
|
});
|
|
1524
|
-
if (!response) return
|
|
1525
|
-
|
|
1465
|
+
if (!response) return { data: null, error };
|
|
1466
|
+
const data = clean(response.suppression_list.map((entry) => ({
|
|
1526
1467
|
createdAt: entry.created_at,
|
|
1527
1468
|
notes: entry.notes,
|
|
1528
1469
|
recipient: entry.recipient,
|
|
@@ -1530,7 +1471,7 @@ class Suppressions {
|
|
|
1530
1471
|
source: entry.source,
|
|
1531
1472
|
types: entry.suppression_types
|
|
1532
1473
|
})));
|
|
1533
|
-
return
|
|
1474
|
+
return { data, error: null };
|
|
1534
1475
|
}
|
|
1535
1476
|
}
|
|
1536
1477
|
|
|
@@ -1551,8 +1492,8 @@ class Domains {
|
|
|
1551
1492
|
* ```
|
|
1552
1493
|
*/
|
|
1553
1494
|
async provision(options) {
|
|
1495
|
+
let error = null;
|
|
1554
1496
|
const { associateKey, overwrite, ...payload } = options;
|
|
1555
|
-
const result = { data: null, error: null };
|
|
1556
1497
|
const response = await this.mailchannels.post("/inbound/v1/domains", {
|
|
1557
1498
|
query: {
|
|
1558
1499
|
"associate-key": associateKey,
|
|
@@ -1560,18 +1501,19 @@ class Domains {
|
|
|
1560
1501
|
},
|
|
1561
1502
|
body: payload,
|
|
1562
1503
|
onResponseError: async ({ response: response2 }) => {
|
|
1563
|
-
|
|
1504
|
+
error = getStatusError(response2, {
|
|
1564
1505
|
[ErrorCode.BadRequest]: "Bad Request, returned in the case that an error occurs while converting an A-label domain to a U-label domain name.",
|
|
1565
1506
|
[ErrorCode.Forbidden]: "The limit on associated domains is reached or you are attempting to associate a domain with a subscription that is not your own.",
|
|
1566
1507
|
[ErrorCode.Conflict]: `The domain '${options.domain}' is already provisioned, and is associated with a different customer.`
|
|
1567
1508
|
});
|
|
1568
1509
|
}
|
|
1569
|
-
}).catch((
|
|
1570
|
-
|
|
1510
|
+
}).catch((e) => {
|
|
1511
|
+
error ||= getResultError(e, "Failed to provision domain.");
|
|
1571
1512
|
return null;
|
|
1572
1513
|
});
|
|
1573
|
-
|
|
1574
|
-
|
|
1514
|
+
if (!response) return { data: null, error };
|
|
1515
|
+
const data = clean(response);
|
|
1516
|
+
return { data, error: null };
|
|
1575
1517
|
}
|
|
1576
1518
|
/**
|
|
1577
1519
|
* Provision up to 1000 domains to use MailChannels Inbound.
|
|
@@ -1594,15 +1536,15 @@ class Domains {
|
|
|
1594
1536
|
* ```
|
|
1595
1537
|
*/
|
|
1596
1538
|
async bulkProvision(options, domains) {
|
|
1539
|
+
let error = null;
|
|
1597
1540
|
const { associateKey, overwrite, subscriptionHandle } = options;
|
|
1598
|
-
const result = { data: null, error: null };
|
|
1599
1541
|
if (!domains || !domains.length) {
|
|
1600
|
-
|
|
1601
|
-
return
|
|
1542
|
+
error = createError("No domains provided.");
|
|
1543
|
+
return { data: null, error };
|
|
1602
1544
|
}
|
|
1603
1545
|
if (domains.length > 1e3) {
|
|
1604
|
-
|
|
1605
|
-
return
|
|
1546
|
+
error = createError("The maximum number of domains to be provisioned is 1000.");
|
|
1547
|
+
return { data: null, error };
|
|
1606
1548
|
}
|
|
1607
1549
|
const response = await this.mailchannels.post("/inbound/v1/domains/batch", {
|
|
1608
1550
|
query: {
|
|
@@ -1612,18 +1554,18 @@ class Domains {
|
|
|
1612
1554
|
},
|
|
1613
1555
|
body: { domains },
|
|
1614
1556
|
onResponseError: async ({ response: response2 }) => {
|
|
1615
|
-
|
|
1557
|
+
error = getStatusError(response2, {
|
|
1616
1558
|
[ErrorCode.BadRequest]: "Bad Request, returned in the case that a domain name fails RFC 5891 validation.",
|
|
1617
1559
|
[ErrorCode.Forbidden]: "The limit on associated domains is reached or you are attempting to associate a domain with a subscription that is not your own."
|
|
1618
1560
|
});
|
|
1619
1561
|
}
|
|
1620
|
-
}).catch((
|
|
1621
|
-
|
|
1562
|
+
}).catch((e) => {
|
|
1563
|
+
error ||= getResultError(e, "Failed to provision domains.");
|
|
1622
1564
|
return null;
|
|
1623
1565
|
});
|
|
1624
|
-
if (!response) return
|
|
1625
|
-
|
|
1626
|
-
return
|
|
1566
|
+
if (!response) return { data: null, error };
|
|
1567
|
+
const data = clean(response);
|
|
1568
|
+
return { data, error: null };
|
|
1627
1569
|
}
|
|
1628
1570
|
/**
|
|
1629
1571
|
* Fetch a list of all domains associated with this API key.
|
|
@@ -1635,24 +1577,24 @@ class Domains {
|
|
|
1635
1577
|
* ```
|
|
1636
1578
|
*/
|
|
1637
1579
|
async list(options) {
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
if (
|
|
1580
|
+
let error = null;
|
|
1581
|
+
error = validateLimit(options?.limit, 5e3) || validateOffset(options?.offset);
|
|
1582
|
+
if (error) return { data: null, error };
|
|
1641
1583
|
const response = await this.mailchannels.get("/inbound/v1/domains", {
|
|
1642
1584
|
query: options,
|
|
1643
1585
|
onResponseError: async ({ response: response2 }) => {
|
|
1644
|
-
|
|
1586
|
+
error = getStatusError(response2);
|
|
1645
1587
|
}
|
|
1646
|
-
}).catch((
|
|
1647
|
-
|
|
1588
|
+
}).catch((e) => {
|
|
1589
|
+
error ||= getResultError(e, "Failed to fetch domains.");
|
|
1648
1590
|
return null;
|
|
1649
1591
|
});
|
|
1650
|
-
if (!response) return
|
|
1651
|
-
|
|
1592
|
+
if (!response) return { data: null, error };
|
|
1593
|
+
const data = clean({
|
|
1652
1594
|
domains: response.domains,
|
|
1653
1595
|
total: response.total
|
|
1654
1596
|
});
|
|
1655
|
-
return
|
|
1597
|
+
return { data, error: null };
|
|
1656
1598
|
}
|
|
1657
1599
|
/**
|
|
1658
1600
|
* De-provision a domain to cease protecting it with MailChannels Inbound.
|
|
@@ -1664,27 +1606,22 @@ class Domains {
|
|
|
1664
1606
|
* ```
|
|
1665
1607
|
*/
|
|
1666
1608
|
async delete(domain) {
|
|
1667
|
-
|
|
1609
|
+
let error = null;
|
|
1668
1610
|
if (!domain) {
|
|
1669
|
-
|
|
1670
|
-
return
|
|
1611
|
+
error = createError("No domain provided.");
|
|
1612
|
+
return { success: false, error };
|
|
1671
1613
|
}
|
|
1672
1614
|
await this.mailchannels.delete(`/inbound/v1/domains/${domain}`, {
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
if (response.ok) {
|
|
1676
|
-
result.success = true;
|
|
1677
|
-
return;
|
|
1678
|
-
}
|
|
1679
|
-
result.error = getStatusError(response, {
|
|
1615
|
+
onResponseError: async ({ response }) => {
|
|
1616
|
+
error = getStatusError(response, {
|
|
1680
1617
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, or the domain in the request is an alias domain.",
|
|
1681
1618
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1682
1619
|
});
|
|
1683
1620
|
}
|
|
1684
|
-
}).catch((
|
|
1685
|
-
|
|
1621
|
+
}).catch((e) => {
|
|
1622
|
+
error ||= getResultError(e, "Failed to delete domain.");
|
|
1686
1623
|
});
|
|
1687
|
-
return
|
|
1624
|
+
return { success: !error, error };
|
|
1688
1625
|
}
|
|
1689
1626
|
/**
|
|
1690
1627
|
* Add an entry to a domain blocklist or safelist.
|
|
@@ -1701,34 +1638,34 @@ class Domains {
|
|
|
1701
1638
|
*/
|
|
1702
1639
|
async addListEntry(domain, options) {
|
|
1703
1640
|
const { listName, item } = options;
|
|
1704
|
-
|
|
1641
|
+
let error = null;
|
|
1705
1642
|
if (!domain) {
|
|
1706
|
-
|
|
1707
|
-
return
|
|
1643
|
+
error = createError("No domain provided.");
|
|
1644
|
+
return { data: null, error };
|
|
1708
1645
|
}
|
|
1709
1646
|
if (!listName) {
|
|
1710
|
-
|
|
1711
|
-
return
|
|
1647
|
+
error = createError("No list name provided.");
|
|
1648
|
+
return { data: null, error };
|
|
1712
1649
|
}
|
|
1713
1650
|
const response = await this.mailchannels.post(`/inbound/v1/domains/${domain}/lists/${listName}`, {
|
|
1714
1651
|
body: { item },
|
|
1715
1652
|
onResponseError: async ({ response: response2 }) => {
|
|
1716
|
-
|
|
1653
|
+
error = getStatusError(response2, {
|
|
1717
1654
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1718
1655
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1719
1656
|
});
|
|
1720
1657
|
}
|
|
1721
|
-
}).catch((
|
|
1722
|
-
|
|
1658
|
+
}).catch((e) => {
|
|
1659
|
+
error ||= getResultError(e, "Failed to add domain list entry.");
|
|
1723
1660
|
return null;
|
|
1724
1661
|
});
|
|
1725
|
-
if (!response) return
|
|
1726
|
-
|
|
1662
|
+
if (!response) return { data: null, error };
|
|
1663
|
+
const data = clean({
|
|
1727
1664
|
action: response.action,
|
|
1728
1665
|
item: response.item,
|
|
1729
1666
|
type: response.item_type
|
|
1730
1667
|
});
|
|
1731
|
-
return
|
|
1668
|
+
return { data, error: null };
|
|
1732
1669
|
}
|
|
1733
1670
|
/**
|
|
1734
1671
|
* Get domain list entries.
|
|
@@ -1741,33 +1678,33 @@ class Domains {
|
|
|
1741
1678
|
* ```
|
|
1742
1679
|
*/
|
|
1743
1680
|
async listEntries(domain, listName) {
|
|
1744
|
-
|
|
1681
|
+
let error = null;
|
|
1745
1682
|
if (!domain) {
|
|
1746
|
-
|
|
1747
|
-
return
|
|
1683
|
+
error = createError("No domain provided.");
|
|
1684
|
+
return { data: null, error };
|
|
1748
1685
|
}
|
|
1749
1686
|
if (!listName) {
|
|
1750
|
-
|
|
1751
|
-
return
|
|
1687
|
+
error = createError("No list name provided.");
|
|
1688
|
+
return { data: null, error };
|
|
1752
1689
|
}
|
|
1753
1690
|
const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/lists/${listName}`, {
|
|
1754
1691
|
onResponseError: async ({ response: response2 }) => {
|
|
1755
|
-
|
|
1692
|
+
error = getStatusError(response2, {
|
|
1756
1693
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1757
1694
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1758
1695
|
});
|
|
1759
1696
|
}
|
|
1760
|
-
}).catch((
|
|
1761
|
-
|
|
1697
|
+
}).catch((e) => {
|
|
1698
|
+
error ||= getResultError(e, "Failed to fetch domain list entries.");
|
|
1762
1699
|
return null;
|
|
1763
1700
|
});
|
|
1764
|
-
if (!response) return
|
|
1765
|
-
|
|
1701
|
+
if (!response) return { data: null, error };
|
|
1702
|
+
const data = clean(response.map(({ action, item, item_type }) => ({
|
|
1766
1703
|
action,
|
|
1767
1704
|
item,
|
|
1768
1705
|
type: item_type
|
|
1769
1706
|
})));
|
|
1770
|
-
return
|
|
1707
|
+
return { data, error: null };
|
|
1771
1708
|
}
|
|
1772
1709
|
/**
|
|
1773
1710
|
* Delete item from domain list.
|
|
@@ -1784,32 +1721,27 @@ class Domains {
|
|
|
1784
1721
|
*/
|
|
1785
1722
|
async deleteListEntry(domain, options) {
|
|
1786
1723
|
const { listName, item } = options;
|
|
1787
|
-
|
|
1724
|
+
let error = null;
|
|
1788
1725
|
if (!domain) {
|
|
1789
|
-
|
|
1790
|
-
return
|
|
1726
|
+
error = createError("No domain provided.");
|
|
1727
|
+
return { success: false, error };
|
|
1791
1728
|
}
|
|
1792
1729
|
if (!listName) {
|
|
1793
|
-
|
|
1794
|
-
return
|
|
1730
|
+
error = createError("No list name provided.");
|
|
1731
|
+
return { success: false, error };
|
|
1795
1732
|
}
|
|
1796
1733
|
await this.mailchannels.delete(`/inbound/v1/domains/${domain}/lists/${listName}`, {
|
|
1797
1734
|
query: { item },
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
if (response.ok) {
|
|
1801
|
-
result.success = true;
|
|
1802
|
-
return;
|
|
1803
|
-
}
|
|
1804
|
-
result.error = getStatusError(response, {
|
|
1735
|
+
onResponseError: async ({ response }) => {
|
|
1736
|
+
error = getStatusError(response, {
|
|
1805
1737
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1806
1738
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1807
1739
|
});
|
|
1808
1740
|
}
|
|
1809
|
-
}).catch((
|
|
1810
|
-
|
|
1741
|
+
}).catch((e) => {
|
|
1742
|
+
error ||= getResultError(e, "Failed to delete domain list entry.");
|
|
1811
1743
|
});
|
|
1812
|
-
return
|
|
1744
|
+
return { success: !error, error };
|
|
1813
1745
|
}
|
|
1814
1746
|
/**
|
|
1815
1747
|
* Generate a link that allows a user to log in as a domain administrator.
|
|
@@ -1821,28 +1753,28 @@ class Domains {
|
|
|
1821
1753
|
* ```
|
|
1822
1754
|
*/
|
|
1823
1755
|
async createLoginLink(domain) {
|
|
1824
|
-
|
|
1756
|
+
let error = null;
|
|
1825
1757
|
if (!domain) {
|
|
1826
|
-
|
|
1827
|
-
return
|
|
1758
|
+
error = createError("No domain provided.");
|
|
1759
|
+
return { data: null, error };
|
|
1828
1760
|
}
|
|
1829
1761
|
const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/login-link`, {
|
|
1830
1762
|
onResponseError: async ({ response: response2 }) => {
|
|
1831
|
-
|
|
1763
|
+
error = getStatusError(response2, {
|
|
1832
1764
|
[ErrorCode.Unauthorized]: "The domain does not belong to this customer.",
|
|
1833
1765
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1834
1766
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1835
1767
|
});
|
|
1836
1768
|
}
|
|
1837
|
-
}).catch((
|
|
1838
|
-
|
|
1769
|
+
}).catch((e) => {
|
|
1770
|
+
error ||= getResultError(e, "Failed to create login link.");
|
|
1839
1771
|
return null;
|
|
1840
1772
|
});
|
|
1841
|
-
if (!response) return
|
|
1842
|
-
|
|
1773
|
+
if (!response) return { data: null, error };
|
|
1774
|
+
const data = clean({
|
|
1843
1775
|
link: response.loginLink
|
|
1844
1776
|
});
|
|
1845
|
-
return
|
|
1777
|
+
return { data, error: null };
|
|
1846
1778
|
}
|
|
1847
1779
|
/**
|
|
1848
1780
|
* Sets the list of downstream addresses for the domain. This action deletes any existing downstream address for the domain before creating new ones. If the `records` parameter is an empty array, all downstream address records will be deleted.
|
|
@@ -1862,32 +1794,27 @@ class Domains {
|
|
|
1862
1794
|
* ```
|
|
1863
1795
|
*/
|
|
1864
1796
|
async setDownstreamAddress(domain, records = []) {
|
|
1865
|
-
|
|
1797
|
+
let error = null;
|
|
1866
1798
|
if (!domain) {
|
|
1867
|
-
|
|
1868
|
-
return
|
|
1799
|
+
error = createError("No domain provided.");
|
|
1800
|
+
return { success: false, error };
|
|
1869
1801
|
}
|
|
1870
1802
|
if (records.length > 10) {
|
|
1871
|
-
|
|
1872
|
-
return
|
|
1803
|
+
error = createError("The maximum of records to be set is 10.");
|
|
1804
|
+
return { success: false, error };
|
|
1873
1805
|
}
|
|
1874
1806
|
await this.mailchannels.put(`/inbound/v1/domains/${domain}/downstream-address`, {
|
|
1875
1807
|
body: { records },
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
if (response.ok) {
|
|
1879
|
-
result.success = true;
|
|
1880
|
-
return;
|
|
1881
|
-
}
|
|
1882
|
-
result.error = getStatusError(response, {
|
|
1808
|
+
onResponseError: async ({ response }) => {
|
|
1809
|
+
error = getStatusError(response, {
|
|
1883
1810
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1884
1811
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1885
1812
|
});
|
|
1886
1813
|
}
|
|
1887
|
-
}).catch((
|
|
1888
|
-
|
|
1814
|
+
}).catch((e) => {
|
|
1815
|
+
error ||= getResultError(e, "Failed to set downstream address.");
|
|
1889
1816
|
});
|
|
1890
|
-
return
|
|
1817
|
+
return { success: !error, error };
|
|
1891
1818
|
}
|
|
1892
1819
|
/**
|
|
1893
1820
|
* Retrieve stored downstream addresses for the domain.
|
|
@@ -1900,28 +1827,28 @@ class Domains {
|
|
|
1900
1827
|
* ```
|
|
1901
1828
|
*/
|
|
1902
1829
|
async listDownstreamAddresses(domain, options) {
|
|
1903
|
-
|
|
1830
|
+
let error = null;
|
|
1904
1831
|
if (!domain) {
|
|
1905
|
-
|
|
1906
|
-
return
|
|
1832
|
+
error = createError("No domain provided.");
|
|
1833
|
+
return { data: null, error };
|
|
1907
1834
|
}
|
|
1908
|
-
|
|
1909
|
-
if (
|
|
1835
|
+
error = validateLimit(options?.limit) || validateOffset(options?.offset);
|
|
1836
|
+
if (error) return { data: null, error };
|
|
1910
1837
|
const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/downstream-address`, {
|
|
1911
1838
|
query: options,
|
|
1912
1839
|
onResponseError: async ({ response: response2 }) => {
|
|
1913
|
-
|
|
1840
|
+
error = getStatusError(response2, {
|
|
1914
1841
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1915
1842
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1916
1843
|
});
|
|
1917
1844
|
}
|
|
1918
|
-
}).catch((
|
|
1919
|
-
|
|
1845
|
+
}).catch((e) => {
|
|
1846
|
+
error ||= getResultError(e, "Failed to list downstream addresses.");
|
|
1920
1847
|
return null;
|
|
1921
1848
|
});
|
|
1922
|
-
if (!response) return
|
|
1923
|
-
|
|
1924
|
-
return
|
|
1849
|
+
if (!response) return { data: null, error };
|
|
1850
|
+
const data = clean(response.records);
|
|
1851
|
+
return { data, error: null };
|
|
1925
1852
|
}
|
|
1926
1853
|
/**
|
|
1927
1854
|
* Update the API key that is associated with a domain.
|
|
@@ -1934,32 +1861,27 @@ class Domains {
|
|
|
1934
1861
|
* ```
|
|
1935
1862
|
*/
|
|
1936
1863
|
async updateApiKey(domain, key) {
|
|
1937
|
-
|
|
1864
|
+
let error = null;
|
|
1938
1865
|
if (!domain) {
|
|
1939
|
-
|
|
1940
|
-
return
|
|
1866
|
+
error = createError("No domain provided.");
|
|
1867
|
+
return { success: false, error };
|
|
1941
1868
|
}
|
|
1942
1869
|
if (!key) {
|
|
1943
|
-
|
|
1944
|
-
return
|
|
1870
|
+
error = createError("No API key provided.");
|
|
1871
|
+
return { success: false, error };
|
|
1945
1872
|
}
|
|
1946
1873
|
await this.mailchannels.put(`/inbound/v1/domains/${domain}/api-key`, {
|
|
1947
1874
|
body: { apiKey: key },
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
if (response.ok) {
|
|
1951
|
-
result.success = true;
|
|
1952
|
-
return;
|
|
1953
|
-
}
|
|
1954
|
-
result.error = getStatusError(response, {
|
|
1875
|
+
onResponseError: async ({ response }) => {
|
|
1876
|
+
error = getStatusError(response, {
|
|
1955
1877
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1956
1878
|
[ErrorCode.NotFound]: "The domain does not exist."
|
|
1957
1879
|
});
|
|
1958
1880
|
}
|
|
1959
|
-
}).catch((
|
|
1960
|
-
|
|
1881
|
+
}).catch((e) => {
|
|
1882
|
+
error ||= getResultError(e, "Failed to update domain API key.");
|
|
1961
1883
|
});
|
|
1962
|
-
return
|
|
1884
|
+
return { success: !error, error };
|
|
1963
1885
|
}
|
|
1964
1886
|
/**
|
|
1965
1887
|
* Generate a batch of links that allow a user to log in as a domain administrator to their different domains.
|
|
@@ -1971,31 +1893,31 @@ class Domains {
|
|
|
1971
1893
|
* ```
|
|
1972
1894
|
*/
|
|
1973
1895
|
async bulkCreateLoginLinks(domains) {
|
|
1974
|
-
|
|
1896
|
+
let error = null;
|
|
1975
1897
|
if (!domains || !domains.length) {
|
|
1976
|
-
|
|
1977
|
-
return
|
|
1898
|
+
error = createError("No domains provided.");
|
|
1899
|
+
return { data: null, error };
|
|
1978
1900
|
}
|
|
1979
1901
|
if (domains.length > 1e3) {
|
|
1980
|
-
|
|
1981
|
-
return
|
|
1902
|
+
error = createError("The maximum number of domains to create login links for is 1000.");
|
|
1903
|
+
return { data: null, error };
|
|
1982
1904
|
}
|
|
1983
1905
|
const response = await this.mailchannels.post("/inbound/v1/domains/batch/login-link", {
|
|
1984
1906
|
body: {
|
|
1985
1907
|
domains: domains.map((domain) => ({ domain }))
|
|
1986
1908
|
},
|
|
1987
1909
|
onResponseError: async ({ response: response2 }) => {
|
|
1988
|
-
|
|
1910
|
+
error = getStatusError(response2, {
|
|
1989
1911
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1990
1912
|
});
|
|
1991
1913
|
}
|
|
1992
|
-
}).catch((
|
|
1993
|
-
|
|
1914
|
+
}).catch((e) => {
|
|
1915
|
+
error ||= getResultError(e, "Failed to create login links.");
|
|
1994
1916
|
return null;
|
|
1995
1917
|
});
|
|
1996
|
-
if (!response) return
|
|
1997
|
-
|
|
1998
|
-
return
|
|
1918
|
+
if (!response) return { data: null, error };
|
|
1919
|
+
const data = clean(response);
|
|
1920
|
+
return { data, error: null };
|
|
1999
1921
|
}
|
|
2000
1922
|
}
|
|
2001
1923
|
|
|
@@ -2016,28 +1938,28 @@ class Lists {
|
|
|
2016
1938
|
* ```
|
|
2017
1939
|
*/
|
|
2018
1940
|
async addListEntry(options) {
|
|
1941
|
+
let error = null;
|
|
2019
1942
|
const { listName, item } = options;
|
|
2020
|
-
const result = { data: null, error: null };
|
|
2021
1943
|
if (!listName) {
|
|
2022
|
-
|
|
2023
|
-
return
|
|
1944
|
+
error = createError("No list name provided.");
|
|
1945
|
+
return { data: null, error };
|
|
2024
1946
|
}
|
|
2025
1947
|
const response = await this.mailchannels.post(`/inbound/v1/lists/${listName}`, {
|
|
2026
1948
|
body: { item },
|
|
2027
1949
|
onResponseError: async ({ response: response2 }) => {
|
|
2028
|
-
|
|
1950
|
+
error = getStatusError(response2);
|
|
2029
1951
|
}
|
|
2030
|
-
}).catch((
|
|
2031
|
-
|
|
1952
|
+
}).catch((e) => {
|
|
1953
|
+
error ||= getResultError(e, "Failed to add list entry.");
|
|
2032
1954
|
return null;
|
|
2033
1955
|
});
|
|
2034
|
-
if (!response) return
|
|
2035
|
-
|
|
1956
|
+
if (!response) return { data: null, error };
|
|
1957
|
+
const data = clean({
|
|
2036
1958
|
action: response.action,
|
|
2037
1959
|
item: response.item,
|
|
2038
1960
|
type: response.item_type
|
|
2039
1961
|
});
|
|
2040
|
-
return
|
|
1962
|
+
return { data, error: null };
|
|
2041
1963
|
}
|
|
2042
1964
|
/**
|
|
2043
1965
|
* Get account-level list entries.
|
|
@@ -2049,26 +1971,26 @@ class Lists {
|
|
|
2049
1971
|
* ```
|
|
2050
1972
|
*/
|
|
2051
1973
|
async listEntries(listName) {
|
|
2052
|
-
|
|
1974
|
+
let error = null;
|
|
2053
1975
|
if (!listName) {
|
|
2054
|
-
|
|
2055
|
-
return
|
|
1976
|
+
error = createError("No list name provided.");
|
|
1977
|
+
return { data: null, error };
|
|
2056
1978
|
}
|
|
2057
1979
|
const response = await this.mailchannels.get(`/inbound/v1/lists/${listName}`, {
|
|
2058
1980
|
onResponseError: async ({ response: response2 }) => {
|
|
2059
|
-
|
|
1981
|
+
error = getStatusError(response2);
|
|
2060
1982
|
}
|
|
2061
|
-
}).catch((
|
|
2062
|
-
|
|
1983
|
+
}).catch((e) => {
|
|
1984
|
+
error ||= getResultError(e, "Failed to fetch list entries.");
|
|
2063
1985
|
return null;
|
|
2064
1986
|
});
|
|
2065
|
-
if (!response) return
|
|
2066
|
-
|
|
1987
|
+
if (!response) return { data: null, error };
|
|
1988
|
+
const data = clean(response.map(({ action, item, item_type }) => ({
|
|
2067
1989
|
action,
|
|
2068
1990
|
item,
|
|
2069
1991
|
type: item_type
|
|
2070
1992
|
})));
|
|
2071
|
-
return
|
|
1993
|
+
return { data, error: null };
|
|
2072
1994
|
}
|
|
2073
1995
|
/**
|
|
2074
1996
|
* Delete item from account-level list.
|
|
@@ -2084,25 +2006,20 @@ class Lists {
|
|
|
2084
2006
|
*/
|
|
2085
2007
|
async deleteListEntry(options) {
|
|
2086
2008
|
const { listName, item } = options;
|
|
2087
|
-
|
|
2009
|
+
let error = null;
|
|
2088
2010
|
if (!listName) {
|
|
2089
|
-
|
|
2090
|
-
return
|
|
2011
|
+
error = createError("No list name provided.");
|
|
2012
|
+
return { success: false, error };
|
|
2091
2013
|
}
|
|
2092
2014
|
await this.mailchannels.delete(`/inbound/v1/lists/${listName}`, {
|
|
2093
2015
|
query: { item },
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
if (response.ok) {
|
|
2097
|
-
result.success = true;
|
|
2098
|
-
return;
|
|
2099
|
-
}
|
|
2100
|
-
result.error = getStatusError(response);
|
|
2016
|
+
onResponseError: async ({ response }) => {
|
|
2017
|
+
error = getStatusError(response);
|
|
2101
2018
|
}
|
|
2102
|
-
}).catch((
|
|
2103
|
-
|
|
2019
|
+
}).catch((e) => {
|
|
2020
|
+
error ||= getResultError(e, "Failed to delete list entry.");
|
|
2104
2021
|
});
|
|
2105
|
-
return
|
|
2022
|
+
return { success: !error, error };
|
|
2106
2023
|
}
|
|
2107
2024
|
}
|
|
2108
2025
|
|
|
@@ -2124,10 +2041,10 @@ class Users {
|
|
|
2124
2041
|
*/
|
|
2125
2042
|
async create(email, options) {
|
|
2126
2043
|
const { admin, filter, listEntries } = options || {};
|
|
2127
|
-
|
|
2044
|
+
let error = null;
|
|
2128
2045
|
if (!email) {
|
|
2129
|
-
|
|
2130
|
-
return
|
|
2046
|
+
error = createError("No email address provided.");
|
|
2047
|
+
return { data: null, error };
|
|
2131
2048
|
}
|
|
2132
2049
|
const response = await this.mailchannels.put("/inbound/v1/users", {
|
|
2133
2050
|
query: {
|
|
@@ -2139,16 +2056,16 @@ class Users {
|
|
|
2139
2056
|
list_entries: listEntries
|
|
2140
2057
|
},
|
|
2141
2058
|
onResponseError: async ({ response: response2 }) => {
|
|
2142
|
-
|
|
2059
|
+
error = getStatusError(response2, {
|
|
2143
2060
|
[ErrorCode.BadRequest]: `The email address '${email}' is invalid.`
|
|
2144
2061
|
});
|
|
2145
2062
|
}
|
|
2146
|
-
}).catch((
|
|
2147
|
-
|
|
2063
|
+
}).catch((e) => {
|
|
2064
|
+
error ||= getResultError(e, "Failed to create user.");
|
|
2148
2065
|
return null;
|
|
2149
2066
|
});
|
|
2150
|
-
if (!response) return
|
|
2151
|
-
|
|
2067
|
+
if (!response) return { data: null, error };
|
|
2068
|
+
const data = clean({
|
|
2152
2069
|
email: response.recipient.email_address,
|
|
2153
2070
|
roles: response.recipient.roles,
|
|
2154
2071
|
filter: response.recipient.filter,
|
|
@@ -2158,7 +2075,7 @@ class Users {
|
|
|
2158
2075
|
action
|
|
2159
2076
|
}))
|
|
2160
2077
|
});
|
|
2161
|
-
return
|
|
2078
|
+
return { data, error: null };
|
|
2162
2079
|
}
|
|
2163
2080
|
/**
|
|
2164
2081
|
* Add item to recipient user list
|
|
@@ -2175,34 +2092,34 @@ class Users {
|
|
|
2175
2092
|
*/
|
|
2176
2093
|
async addListEntry(email, options) {
|
|
2177
2094
|
const { listName, item } = options;
|
|
2178
|
-
|
|
2095
|
+
let error = null;
|
|
2179
2096
|
if (!email) {
|
|
2180
|
-
|
|
2181
|
-
return
|
|
2097
|
+
error = createError("No email provided.");
|
|
2098
|
+
return { data: null, error };
|
|
2182
2099
|
}
|
|
2183
2100
|
if (!listName) {
|
|
2184
|
-
|
|
2185
|
-
return
|
|
2101
|
+
error = createError("No list name provided.");
|
|
2102
|
+
return { data: null, error };
|
|
2186
2103
|
}
|
|
2187
2104
|
const response = await this.mailchannels.post(`/inbound/v1/users/${email}/lists/${listName}`, {
|
|
2188
2105
|
body: { item },
|
|
2189
2106
|
onResponseError: async ({ response: response2 }) => {
|
|
2190
|
-
|
|
2107
|
+
error = getStatusError(response2, {
|
|
2191
2108
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
2192
2109
|
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
2193
2110
|
});
|
|
2194
2111
|
}
|
|
2195
|
-
}).catch((
|
|
2196
|
-
|
|
2112
|
+
}).catch((e) => {
|
|
2113
|
+
error ||= getResultError(e, "Failed to add user list entry.");
|
|
2197
2114
|
return null;
|
|
2198
2115
|
});
|
|
2199
|
-
if (!response) return
|
|
2200
|
-
|
|
2116
|
+
if (!response) return { data: null, error };
|
|
2117
|
+
const data = clean({
|
|
2201
2118
|
action: response.action,
|
|
2202
2119
|
item: response.item,
|
|
2203
2120
|
type: response.item_type
|
|
2204
2121
|
});
|
|
2205
|
-
return
|
|
2122
|
+
return { data, error: null };
|
|
2206
2123
|
}
|
|
2207
2124
|
/**
|
|
2208
2125
|
* Get recipient list entries.
|
|
@@ -2215,33 +2132,33 @@ class Users {
|
|
|
2215
2132
|
* ```
|
|
2216
2133
|
*/
|
|
2217
2134
|
async listEntries(email, listName) {
|
|
2218
|
-
|
|
2135
|
+
let error = null;
|
|
2219
2136
|
if (!email) {
|
|
2220
|
-
|
|
2221
|
-
return
|
|
2137
|
+
error = createError("No email provided.");
|
|
2138
|
+
return { data: null, error };
|
|
2222
2139
|
}
|
|
2223
2140
|
if (!listName) {
|
|
2224
|
-
|
|
2225
|
-
return
|
|
2141
|
+
error = createError("No list name provided.");
|
|
2142
|
+
return { data: null, error };
|
|
2226
2143
|
}
|
|
2227
2144
|
const response = await this.mailchannels.get(`/inbound/v1/users/${email}/lists/${listName}`, {
|
|
2228
2145
|
onResponseError: async ({ response: response2 }) => {
|
|
2229
|
-
|
|
2146
|
+
error = getStatusError(response2, {
|
|
2230
2147
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
2231
2148
|
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
2232
2149
|
});
|
|
2233
2150
|
}
|
|
2234
|
-
}).catch((
|
|
2235
|
-
|
|
2151
|
+
}).catch((e) => {
|
|
2152
|
+
error ||= getResultError(e, "Failed to fetch user list entries.");
|
|
2236
2153
|
return null;
|
|
2237
2154
|
});
|
|
2238
|
-
if (!response) return
|
|
2239
|
-
|
|
2155
|
+
if (!response) return { data: null, error };
|
|
2156
|
+
const data = clean(response.map(({ action, item, item_type }) => ({
|
|
2240
2157
|
action,
|
|
2241
2158
|
item,
|
|
2242
2159
|
type: item_type
|
|
2243
2160
|
})));
|
|
2244
|
-
return
|
|
2161
|
+
return { data, error: null };
|
|
2245
2162
|
}
|
|
2246
2163
|
/**
|
|
2247
2164
|
* Delete item from recipient list.
|
|
@@ -2258,32 +2175,27 @@ class Users {
|
|
|
2258
2175
|
*/
|
|
2259
2176
|
async deleteListEntry(email, options) {
|
|
2260
2177
|
const { listName, item } = options;
|
|
2261
|
-
|
|
2178
|
+
let error = null;
|
|
2262
2179
|
if (!email) {
|
|
2263
|
-
|
|
2264
|
-
return
|
|
2180
|
+
error = createError("No email provided.");
|
|
2181
|
+
return { success: false, error };
|
|
2265
2182
|
}
|
|
2266
2183
|
if (!listName) {
|
|
2267
|
-
|
|
2268
|
-
return
|
|
2184
|
+
error = createError("No list name provided.");
|
|
2185
|
+
return { success: false, error };
|
|
2269
2186
|
}
|
|
2270
2187
|
await this.mailchannels.delete(`/inbound/v1/users/${email}/lists/${listName}`, {
|
|
2271
2188
|
query: { item },
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
if (response.ok) {
|
|
2275
|
-
result.success = true;
|
|
2276
|
-
return;
|
|
2277
|
-
}
|
|
2278
|
-
result.error = getStatusError(response, {
|
|
2189
|
+
onResponseError: async ({ response }) => {
|
|
2190
|
+
error = getStatusError(response, {
|
|
2279
2191
|
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
2280
2192
|
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
2281
2193
|
});
|
|
2282
2194
|
}
|
|
2283
|
-
}).catch((
|
|
2284
|
-
|
|
2195
|
+
}).catch((e) => {
|
|
2196
|
+
error ||= getResultError(e, "Failed to delete user list entry.");
|
|
2285
2197
|
});
|
|
2286
|
-
return
|
|
2198
|
+
return { success: !error, error };
|
|
2287
2199
|
}
|
|
2288
2200
|
}
|
|
2289
2201
|
|
|
@@ -2300,20 +2212,15 @@ class Service {
|
|
|
2300
2212
|
* ```
|
|
2301
2213
|
*/
|
|
2302
2214
|
async status() {
|
|
2303
|
-
|
|
2215
|
+
let error = null;
|
|
2304
2216
|
await this.mailchannels.get("/inbound/v1/status", {
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
if (response.ok) {
|
|
2308
|
-
result.success = true;
|
|
2309
|
-
return;
|
|
2310
|
-
}
|
|
2311
|
-
result.error = getStatusError(response);
|
|
2217
|
+
onResponseError: async ({ response }) => {
|
|
2218
|
+
error = getStatusError(response);
|
|
2312
2219
|
}
|
|
2313
|
-
}).catch((
|
|
2314
|
-
|
|
2220
|
+
}).catch((e) => {
|
|
2221
|
+
error ||= getResultError(e, "Failed to fetch service status.");
|
|
2315
2222
|
});
|
|
2316
|
-
return
|
|
2223
|
+
return { success: !error, error };
|
|
2317
2224
|
}
|
|
2318
2225
|
/**
|
|
2319
2226
|
* Get a list of your subscriptions to MailChannels Inbound
|
|
@@ -2324,20 +2231,20 @@ class Service {
|
|
|
2324
2231
|
* ```
|
|
2325
2232
|
*/
|
|
2326
2233
|
async subscriptions() {
|
|
2327
|
-
|
|
2234
|
+
let error = null;
|
|
2328
2235
|
const response = await this.mailchannels.get("/inbound/v1/subscriptions", {
|
|
2329
2236
|
onResponseError: async ({ response: response2 }) => {
|
|
2330
|
-
|
|
2237
|
+
error = getStatusError(response2, {
|
|
2331
2238
|
[ErrorCode.NotFound]: "We could not find a customer that matched the customerHandle."
|
|
2332
2239
|
});
|
|
2333
2240
|
}
|
|
2334
|
-
}).catch((
|
|
2335
|
-
|
|
2241
|
+
}).catch((e) => {
|
|
2242
|
+
error ||= getResultError(e, "Failed to fetch subscriptions.");
|
|
2336
2243
|
return null;
|
|
2337
2244
|
});
|
|
2338
|
-
if (!response) return
|
|
2339
|
-
|
|
2340
|
-
return
|
|
2245
|
+
if (!response) return { data: null, error };
|
|
2246
|
+
const data = clean(response);
|
|
2247
|
+
return { data, error: null };
|
|
2341
2248
|
}
|
|
2342
2249
|
/**
|
|
2343
2250
|
* Submit a false negative or false positive report.
|
|
@@ -2351,25 +2258,20 @@ class Service {
|
|
|
2351
2258
|
* ```
|
|
2352
2259
|
*/
|
|
2353
2260
|
async report(options) {
|
|
2354
|
-
|
|
2261
|
+
let error = null;
|
|
2355
2262
|
const { type, ...payload } = options;
|
|
2356
2263
|
await this.mailchannels.post("/inbound/v1/report", {
|
|
2357
2264
|
query: {
|
|
2358
2265
|
report_type: type
|
|
2359
2266
|
},
|
|
2360
2267
|
body: payload,
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
if (response.ok) {
|
|
2364
|
-
result.success = true;
|
|
2365
|
-
return;
|
|
2366
|
-
}
|
|
2367
|
-
result.error = getStatusError(response);
|
|
2268
|
+
onResponseError: async ({ response }) => {
|
|
2269
|
+
error = getStatusError(response);
|
|
2368
2270
|
}
|
|
2369
|
-
}).catch((
|
|
2370
|
-
|
|
2271
|
+
}).catch((e) => {
|
|
2272
|
+
error ||= getResultError(e, "Failed to submit report.");
|
|
2371
2273
|
});
|
|
2372
|
-
return
|
|
2274
|
+
return { success: !error, error };
|
|
2373
2275
|
}
|
|
2374
2276
|
}
|
|
2375
2277
|
|