mailgun.js 5.0.0 → 5.0.3

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.
@@ -2,6 +2,4 @@
2
2
 
3
3
  /*! https://mths.be/base64 v1.0.0 by @mathias | MIT license */
4
4
 
5
- /*! https://mths.be/punycode v1.3.2 by @mathias */
6
-
7
- /*! mailgun.js v4.2.3 */
5
+ /*! mailgun.js v5.0.2 */
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-console */
2
2
  const fs = require('fs');
3
- const mailgun = require('../index');
3
+ const mailgun = require('../lib/index');
4
4
 
5
5
  const mg = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY || '', timeout: 60000 });
6
6
 
package/lib/domains.ts CHANGED
@@ -208,7 +208,7 @@ export default class DomainClient {
208
208
 
209
209
  updateDKIMAuthority(domain: string, data: DKIMAuthorityInfo): Promise<UpdatedDKIMAuthority> {
210
210
  return this.request.put(`/v3/domains/${domain}/dkim_authority`, {}, { query: `self=${data.self}` })
211
- .then((res : APIResponse) => res)
211
+ .then((res : APIResponse) => res as UpdatedDKIMAuthorityResponse)
212
212
  .then((res : UpdatedDKIMAuthorityResponse) => res.body as UpdatedDKIMAuthority);
213
213
  }
214
214
 
package/lib/events.ts CHANGED
@@ -13,7 +13,7 @@ export default class EventClient {
13
13
  }
14
14
 
15
15
  _parsePageNumber(url: string) : string {
16
- return url.split('/').pop();
16
+ return url.split('/').pop() || '';
17
17
  }
18
18
 
19
19
  _parsePage(id: string, url: string) : EventsPage {
@@ -23,9 +23,9 @@ export default class EventClient {
23
23
  _parsePageLinks(response: EventsResponse) : ParsedPagesList {
24
24
  const pages = Object.entries(response.body.paging as PagesList);
25
25
  return pages.reduce(
26
- (acc: PagesListAccumulator, entrie: [url: string, id: string]) => {
27
- const id = entrie[0];
28
- const url = entrie[1];
26
+ (acc: PagesListAccumulator, pair: [url: string, id: string]) => {
27
+ const id = pair[0];
28
+ const url = pair[1];
29
29
  acc[id] = this._parsePage(id, url);
30
30
  return acc;
31
31
  }, {}
package/lib/index.ts CHANGED
@@ -2,7 +2,8 @@ import Client from './client';
2
2
  import { InputFormData } from './interfaces/IFormData';
3
3
  import Options from './interfaces/Options';
4
4
 
5
- class Mailgun {
5
+ export default class Mailgun {
6
+ static get default(): typeof Mailgun { return this; }
6
7
  private formData: InputFormData
7
8
 
8
9
  constructor(FormData: InputFormData) {
@@ -13,5 +14,3 @@ class Mailgun {
13
14
  return new Client(options, this.formData);
14
15
  }
15
16
  }
16
-
17
- export = Mailgun;
@@ -238,7 +238,6 @@ export interface DomainTagDevicesAggregation {
238
238
  device: DevicesTypes;
239
239
  }
240
240
 
241
-
242
241
  export interface IDomainTagsClient {
243
242
  list(domain: string): Promise<DomainTagsList>
244
243
  get(domain: string, tag: string): Promise<DomainTagsItem>
@@ -206,7 +206,7 @@ export interface ListDomainTemplateVersionsResult {
206
206
 
207
207
  export interface IDomainTemplatesClient {
208
208
  list(domain: string, query?: DomainTemplatesQuery): Promise<ListDomainTemplatesResult>
209
- get(domain: string, templateName: string, query: TemplateQuery): Promise<DomainTemplateItem>
209
+ get(domain: string, templateName: string, query?: TemplateQuery): Promise<DomainTemplateItem>
210
210
  create(domain: string, data: DomainTemplateData): Promise<DomainTemplateItem>
211
211
  update(
212
212
  domain: string,
@@ -1,9 +1,9 @@
1
1
  /* eslint-disable camelcase */
2
2
  export interface DomainsQuery {
3
- authority : string;
4
- state: 'active' | 'unverified' | 'disabled';
5
- limit: number;
6
- skip: number;
3
+ authority? : string;
4
+ state?: 'active' | 'unverified' | 'disabled';
5
+ limit?: number;
6
+ skip?: number;
7
7
  }
8
8
 
9
9
  export interface DomainInfo {
@@ -1,4 +1,4 @@
1
- import NodeFormData from 'form-data';
1
+ import * as NodeFormData from 'form-data';
2
2
 
3
3
  export interface IFormDataOptions {
4
4
  [key: string]: any;
@@ -14,10 +14,12 @@ export interface StatsOptions {
14
14
  stats: Stat[];
15
15
  }
16
16
 
17
+ export type StatsEvent = 'accepted' | 'delivered' | 'opened' | 'clicked' | 'unsubscribed' | 'stored' | 'rejected' | 'complained' | 'failed-temporary' | 'failed-permanent';
18
+
17
19
  export interface StatsQuery {
18
- event: string | string[];
19
- start: string | Date;
20
- end: string | Date;
21
- resolution: 'hour'| 'day' | 'month';
22
- duration: string;
20
+ event: StatsEvent | StatsEvent[];
21
+ start?: string | Date;
22
+ end?: string | Date;
23
+ resolution?: 'hour'| 'day' | 'month';
24
+ duration?: string;
23
25
  }
@@ -1,3 +1,7 @@
1
+ import {
2
+ Bounce, Complaint, Unsubscribe, WhiteList
3
+ } from '../suppressions';
4
+
1
5
  /* eslint-disable camelcase */
2
6
  export interface BounceData {
3
7
  address: string;
@@ -23,3 +27,97 @@ export interface WhiteListData {
23
27
  reason: string;
24
28
  createdAt: string | Date;
25
29
  }
30
+
31
+ export interface ParsedPage {
32
+ id: string;
33
+ page: string | null | undefined;
34
+ address: string | null | undefined;
35
+ url: string
36
+ }
37
+ export interface ParsedPagesList {
38
+ previous: ParsedPage;
39
+ first: ParsedPage;
40
+ last: ParsedPage;
41
+ next: ParsedPage;
42
+ }
43
+
44
+ export interface SuppressionList {
45
+ items: (Bounce | Complaint | Unsubscribe | WhiteList)[];
46
+ pages: ParsedPagesList;
47
+ }
48
+
49
+ export interface PagesList {
50
+ previous: string;
51
+ first: string;
52
+ last: string;
53
+ next: string;
54
+ }
55
+
56
+ export enum SuppressionModels {
57
+ BOUNCES = 'bounces',
58
+ COMPLAINTS = 'complaints',
59
+ UNSUBSCRIBES = 'unsubscribes',
60
+ WHITELISTS = 'whitelists'
61
+ }
62
+
63
+ export interface PagesListAccumulator {
64
+ [index: string]: ParsedPage;
65
+ }
66
+
67
+ export interface SuppressionListQuery {
68
+ limit?: number;
69
+ }
70
+
71
+ export interface SuppressionListResponse {
72
+ body: {
73
+ items: BounceData[] | ComplaintData[] | UnsubscribeData[] | WhiteListData[];
74
+ paging: PagesList;
75
+ }
76
+ status: number;
77
+ }
78
+
79
+ export interface SuppressionResponse {
80
+ body: BounceData | ComplaintData | UnsubscribeData | WhiteListData;
81
+ status: number;
82
+ }
83
+
84
+ export interface SuppressionDestroyResponse {
85
+ body: {
86
+ message: string;
87
+ value?: string;
88
+ address?: string;
89
+ }
90
+ status: number;
91
+ }
92
+
93
+ export interface SuppressionDestroyResult {
94
+ message: string;
95
+ value: string;
96
+ address: string;
97
+ status: number;
98
+ }
99
+
100
+ export interface SuppressionCreationData {
101
+ address: string;
102
+ code?: number;
103
+ error?: string;
104
+ domain?: string;
105
+ tag?: string;
106
+ created_at?: string ;
107
+ }
108
+
109
+ export interface SuppressionCreationResponse {
110
+ body:{
111
+ message:string;
112
+ type?: string;
113
+ value?: string;
114
+ }
115
+ status: number;
116
+ }
117
+
118
+ export interface SuppressionCreationResult {
119
+ message:string;
120
+ type: string;
121
+ value: string;
122
+ status: number;
123
+ }
@@ -28,3 +28,13 @@ export interface ValidationResponse {
28
28
  code: number;
29
29
  message: string;
30
30
  }
31
+
32
+ export enum WebhooksIds {
33
+ CLICKED = 'clicked',
34
+ COMPLAINED = 'complained',
35
+ DELIVERED = 'delivered',
36
+ OPENED = 'opened',
37
+ PERMANENT_FAIL = 'permanent_fail',
38
+ TEMPORARY_FAIL = 'temporary_fail',
39
+ UNSUBSCRIBED = 'unsubscribe',
40
+ }
package/lib/request.ts CHANGED
@@ -1,5 +1,5 @@
1
- import NodeFormData from 'form-data';
2
- import base64 from 'base-64';
1
+ import * as NodeFormData from 'form-data';
2
+ import * as base64 from 'base-64';
3
3
  import urljoin from 'url-join';
4
4
  import ky from 'ky-universal';
5
5
  import APIError from './error';
package/lib/stats.ts CHANGED
@@ -27,8 +27,8 @@ export default class StatsClient {
27
27
  this.request = request;
28
28
  }
29
29
 
30
- private prepareSearchParams(query: StatsQuery): Array<Array<string>> {
31
- let searchParams = [];
30
+ private prepareSearchParams(query: StatsQuery | undefined): Array<Array<string>> {
31
+ let searchParams = [] as Array<Array<string>>;
32
32
  if (typeof query === 'object' && Object.keys(query).length) {
33
33
  searchParams = Object.entries(query).reduce((arrayWithPairs, currentPair) => {
34
34
  const [key, value] = currentPair;
@@ -38,7 +38,7 @@ export default class StatsClient {
38
38
  }
39
39
  arrayWithPairs.push([key, value]);
40
40
  return arrayWithPairs;
41
- }, []);
41
+ }, [] as Array<Array<string>>);
42
42
  }
43
43
 
44
44
  return searchParams;
@@ -1,28 +1,47 @@
1
1
  /* eslint-disable camelcase */
2
- import url from 'url';
3
2
  import urljoin from 'url-join';
4
3
 
5
4
  import Request from './request';
6
5
  import {
7
6
  BounceData,
8
7
  ComplaintData,
8
+ PagesList,
9
+ PagesListAccumulator,
10
+ ParsedPage,
11
+ ParsedPagesList,
12
+ SuppressionCreationData,
13
+ SuppressionCreationResponse,
14
+ SuppressionCreationResult,
15
+ SuppressionDestroyResponse,
16
+ SuppressionDestroyResult,
17
+ SuppressionList,
18
+ SuppressionListQuery,
19
+ SuppressionListResponse,
20
+ SuppressionModels,
21
+ SuppressionResponse,
9
22
  UnsubscribeData,
10
- WhiteListData
23
+ WhiteListData,
11
24
  } from './interfaces/Supressions';
25
+ import APIError from './error';
26
+ import APIErrorOptions from './interfaces/APIErrorOptions';
12
27
 
13
28
  const createOptions = {
14
29
  headers: { 'Content-Type': 'application/json' }
15
30
  };
16
-
17
- class Bounce {
31
+ export class Suppression {
18
32
  type: string;
33
+ constructor(type: SuppressionModels) {
34
+ this.type = type;
35
+ }
36
+ }
37
+ export class Bounce extends Suppression {
19
38
  address: string;
20
39
  code: number;
21
40
  error: string;
22
41
  created_at: Date;
23
42
 
24
43
  constructor(data: BounceData) {
25
- this.type = 'bounces';
44
+ super(SuppressionModels.BOUNCES);
26
45
  this.address = data.address;
27
46
  this.code = +data.code;
28
47
  this.error = data.error;
@@ -30,126 +49,173 @@ class Bounce {
30
49
  }
31
50
  }
32
51
 
33
- class Complaint {
34
- type: string;
35
- address: any;
52
+ export class Complaint extends Suppression {
53
+ address: string | undefined;
36
54
  created_at: Date;
37
55
 
38
56
  constructor(data: ComplaintData) {
39
- this.type = 'complaints';
57
+ super(SuppressionModels.COMPLAINTS);
40
58
  this.address = data.address;
41
59
  this.created_at = new Date(data.created_at);
42
60
  }
43
61
  }
44
62
 
45
- class Unsubscribe {
46
- type: string;
63
+ export class Unsubscribe extends Suppression {
47
64
  address: string;
48
- tags: any;
65
+ tags: string[];
49
66
  created_at: Date;
50
67
 
51
68
  constructor(data: UnsubscribeData) {
52
- this.type = 'unsubscribes';
69
+ super(SuppressionModels.UNSUBSCRIBES);
53
70
  this.address = data.address;
54
71
  this.tags = data.tags;
55
72
  this.created_at = new Date(data.created_at);
56
73
  }
57
74
  }
58
75
 
59
- class WhiteList {
60
- type: string;
76
+ export class WhiteList extends Suppression {
61
77
  value: string;
62
78
  reason: string;
63
79
  createdAt: Date;
64
80
 
65
81
  constructor(data: WhiteListData) {
66
- this.type = 'whitelists';
82
+ super(SuppressionModels.WHITELISTS);
67
83
  this.value = data.value;
68
84
  this.reason = data.reason;
69
85
  this.createdAt = new Date(data.createdAt);
70
86
  }
71
87
  }
72
88
 
73
- type TModel = typeof Bounce | typeof Complaint | typeof Unsubscribe | typeof WhiteList;
74
-
75
89
  export default class SuppressionClient {
76
- request: any;
77
- models: {
78
- bounces: typeof Bounce;
79
- complaints: typeof Complaint;
80
- unsubscribes: typeof Unsubscribe;
81
- whitelists: typeof WhiteList;
82
- };
90
+ request: Request;
91
+ models: Map<string, any>;
83
92
 
84
93
  constructor(request: Request) {
85
94
  this.request = request;
86
- this.models = {
87
- bounces: Bounce,
88
- complaints: Complaint,
89
- unsubscribes: Unsubscribe,
90
- whitelists: WhiteList,
91
- };
95
+ this.models = new Map();
96
+ this.models.set('bounces', Bounce);
97
+ this.models.set('complaints', Complaint);
98
+ this.models.set('unsubscribes', Unsubscribe);
99
+ this.models.set('whitelists', WhiteList);
92
100
  }
93
101
 
94
- _parsePage(id: string, pageUrl: string) {
95
- const parsedUrl = url.parse(pageUrl, true);
96
- const { query } = parsedUrl;
97
-
102
+ _parsePage(id: string, pageUrl: string) : ParsedPage {
103
+ const parsedUrl = new URL(pageUrl);
104
+ const { searchParams } = parsedUrl;
98
105
  return {
99
106
  id,
100
- page: query.page,
101
- address: query.address,
107
+ page: searchParams.has('page') ? searchParams.get('page') : undefined,
108
+ address: searchParams.has('address') ? searchParams.get('address') : undefined,
102
109
  url: pageUrl
103
110
  };
104
111
  }
105
112
 
106
- _parsePageLinks(response: { body: { paging: any } }) {
107
- const pages = Object.entries(response.body.paging);
113
+ _parsePageLinks(response: SuppressionListResponse): ParsedPagesList {
114
+ const pages = Object.entries(response.body.paging as PagesList);
108
115
  return pages.reduce(
109
- (acc: any, [id, pageUrl]: [pageUrl: string, id: string]) => {
116
+ (acc: PagesListAccumulator, pair: [pageUrl: string, id: string]) => {
117
+ const id = pair[0];
118
+ const pageUrl = pair[1];
110
119
  acc[id] = this._parsePage(id, pageUrl);
111
120
  return acc;
112
121
  }, {}
113
- );
122
+ ) as unknown as ParsedPagesList;
114
123
  }
115
124
 
116
- _parseList(response: { body: { items: any, paging: any } }, Model: TModel) {
117
- const data = {} as any;
118
-
119
- data.items = response.body.items.map((d: any) => new Model(d));
125
+ _parseList(
126
+ response: SuppressionListResponse,
127
+ Model: {
128
+ new(data: BounceData | ComplaintData | UnsubscribeData | WhiteListData):
129
+ Bounce | Complaint | Unsubscribe | WhiteList
130
+ }
131
+ ): SuppressionList {
132
+ const data = {} as SuppressionList;
133
+ data.items = response.body.items.map((item) => new Model(item));
120
134
 
121
135
  data.pages = this._parsePageLinks(response);
122
136
 
123
137
  return data;
124
138
  }
125
139
 
126
- _parseItem(response: { body: any }, Model: TModel) {
127
- return new Model(response.body);
140
+ _parseItem<T extends Suppression>(
141
+ data : BounceData | ComplaintData | UnsubscribeData | WhiteListData,
142
+ Model: {
143
+ new(data: BounceData | ComplaintData | UnsubscribeData | WhiteListData):
144
+ T
145
+ }
146
+ ): T {
147
+ return new Model(data);
128
148
  }
129
149
 
130
- private createWhiteList(domain: string, data: any) {
150
+ private createWhiteList(
151
+ domain: string,
152
+ data: SuppressionCreationData | SuppressionCreationData[]
153
+ ): Promise<SuppressionCreationResult> {
154
+ if (Array.isArray(data)) {
155
+ throw new APIError({
156
+ status: 400,
157
+ statusText: 'Data property should be an object',
158
+ body: {
159
+ message: 'Whitelist\'s creation process does not support multiple creations. Data property should be an object'
160
+ }
161
+ } as APIErrorOptions);
162
+ }
131
163
  return this.request
132
- .postWithFD(urljoin('v3', domain, 'whitelists'), data, createOptions)
133
- .then((response: { body: any }) => response.body);
164
+ .postWithFD(urljoin('v3', domain, 'whitelists'), data)
165
+ .then(this.prepareResponse);
166
+ }
167
+
168
+ private checkType(type: string) {
169
+ if (!this.models.has(type)) {
170
+ throw new APIError({
171
+ status: 400,
172
+ statusText: 'Unknown type value',
173
+ body: { message: 'Type may be only one of [bounces, complaints, unsubscribes, whitelists]' }
174
+ } as APIErrorOptions);
175
+ }
176
+ }
177
+
178
+ private prepareResponse(response: SuppressionCreationResponse): SuppressionCreationResult {
179
+ return {
180
+ message: response.body.message,
181
+ type: response.body.type || '',
182
+ value: response.body.value || '',
183
+ status: response.status
184
+ };
134
185
  }
135
186
 
136
- list(domain: string, type: string, query: any) {
137
- const model = (this.models as any)[type];
187
+ list(
188
+ domain: string,
189
+ type: string,
190
+ query?: SuppressionListQuery
191
+ ): Promise<SuppressionList> {
192
+ this.checkType(type);
138
193
 
194
+ const model = this.models.get(type);
139
195
  return this.request
140
196
  .get(urljoin('v3', domain, type), query)
141
- .then((response: { body: { items: any, paging: any } }) => this._parseList(response, model));
197
+ .then((response: SuppressionListResponse) => this._parseList(response, model));
142
198
  }
143
199
 
144
- get(domain: string, type: string, address: string) {
145
- const model = (this.models as any)[type];
200
+ get(
201
+ domain: string,
202
+ type: string,
203
+ address: string
204
+ ): Promise<Bounce | Complaint | Unsubscribe | WhiteList> {
205
+ this.checkType(type);
146
206
 
207
+ const model = this.models.get(type);
147
208
  return this.request
148
209
  .get(urljoin('v3', domain, type, encodeURIComponent(address)))
149
- .then((response: { body: any }) => this._parseItem(response, model));
210
+ .then((response: SuppressionResponse) => this._parseItem<typeof model>(response.body, model));
150
211
  }
151
212
 
152
- create(domain: string, type: string, data: any) {
213
+ create(
214
+ domain: string,
215
+ type: string,
216
+ data: SuppressionCreationData | SuppressionCreationData[]
217
+ ): Promise<SuppressionCreationResult> {
218
+ this.checkType(type);
153
219
  // supports adding multiple suppressions by default
154
220
  let postData;
155
221
  if (type === 'whitelists') {
@@ -164,13 +230,23 @@ export default class SuppressionClient {
164
230
 
165
231
  return this.request
166
232
  .post(urljoin('v3', domain, type), JSON.stringify(postData), createOptions)
167
- .then((response: { body: any }) => response.body);
233
+ .then(this.prepareResponse);
168
234
  }
169
235
 
170
- destroy(domain: string, type: string, address: string) {
236
+ destroy(
237
+ domain: string,
238
+ type: string,
239
+ address: string
240
+ ): Promise<SuppressionDestroyResult> {
241
+ this.checkType(type);
171
242
  return this.request
172
243
  .delete(urljoin('v3', domain, type, encodeURIComponent(address)))
173
- .then((response: { body: any }) => response.body);
244
+ .then((response: SuppressionDestroyResponse) => ({
245
+ message: response.body.message,
246
+ value: response.body.value || '',
247
+ address: response.body.address || '',
248
+ status: response.status
249
+ }));
174
250
  }
175
251
  }
176
252
 
package/lib/webhooks.ts CHANGED
@@ -4,15 +4,16 @@ import {
4
4
  ValidationResponse,
5
5
  WebhookList,
6
6
  WebhookResponse,
7
+ WebhooksIds,
7
8
  WebhooksQuery
8
9
  } from './interfaces/Webhooks';
9
10
  import Request from './request';
10
11
 
11
12
  class Webhook {
12
13
  id: string;
13
- url: string;
14
+ url: string | undefined;
14
15
 
15
- constructor(id: string, url: string) {
16
+ constructor(id: string, url: string | undefined) {
16
17
  this.id = id;
17
18
  this.url = url;
18
19
  }
@@ -34,7 +35,9 @@ export default class WebhookClient {
34
35
  const webhookResponse = response?.body?.webhook;
35
36
  let url = webhookResponse?.url;
36
37
  if (!url) {
37
- url = webhookResponse?.urls && webhookResponse.urls.length ? webhookResponse.urls[0] : null;
38
+ url = webhookResponse?.urls && webhookResponse.urls.length
39
+ ? webhookResponse.urls[0]
40
+ : undefined;
38
41
  }
39
42
  return new Webhook(id, url);
40
43
  };
@@ -50,7 +53,7 @@ export default class WebhookClient {
50
53
  .then(this._parseWebhookList);
51
54
  }
52
55
 
53
- get(domain: string, id: string): Promise<Webhook> {
56
+ get(domain: string, id: WebhooksIds): Promise<Webhook> {
54
57
  return this.request.get(urljoin('/v3/domains', domain, 'webhooks', id))
55
58
  .then(this._parseWebhookWithID(id));
56
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailgun.js",
3
- "version": "5.0.0",
3
+ "version": "5.0.3",
4
4
  "main": "dist/mailgun.node.js",
5
5
  "browser": "dist/mailgun.web.js",
6
6
  "types": "dist/mailgun.js.d.ts",
@@ -38,8 +38,7 @@
38
38
  "bluebird": "^3.7.2",
39
39
  "ky": "^0.25.1",
40
40
  "ky-universal": "^0.8.2",
41
- "url": "^0.11.0",
42
- "url-join": "0.0.1",
41
+ "url-join": "^4.0.1",
43
42
  "web-streams-polyfill": "^3.0.1",
44
43
  "webpack-merge": "^5.4.0"
45
44
  },
@@ -51,7 +50,7 @@
51
50
  "@types/base-64": "^1.0.0",
52
51
  "@types/chai": "^4.2.14",
53
52
  "@types/mocha": "^8.2.0",
54
- "@types/url-join": "^4.0.0",
53
+ "@types/url-join": "^4.0.1",
55
54
  "@typescript-eslint/eslint-plugin": "^4.26.0",
56
55
  "@typescript-eslint/parser": "^4.26.0",
57
56
  "babel-loader": "^8.1.0",