mailgun.js 5.0.2 → 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 v5.0.1 */
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/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;
@@ -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;
@@ -24,35 +28,10 @@ export interface WhiteListData {
24
28
  createdAt: string | Date;
25
29
  }
26
30
 
27
- export interface IBounce {
28
- type: string;
29
- address: string;
30
- code: number;
31
- error: string;
32
- created_at: Date;
33
- }
34
- export interface IComplaint {
35
- type: string;
36
- address: any;
37
- created_at: Date;
38
- }
39
- export interface IUnsubscribe {
40
- type: string;
41
- address: string;
42
- tags: any;
43
- created_at: Date;
44
- }
45
- export interface IWhiteList {
46
- type: string;
47
- value: string;
48
- reason: string;
49
- createdAt: Date;
50
- }
51
-
52
31
  export interface ParsedPage {
53
32
  id: string;
54
- page: string | undefined;
55
- address: string | undefined;
33
+ page: string | null | undefined;
34
+ address: string | null | undefined;
56
35
  url: string
57
36
  }
58
37
  export interface ParsedPagesList {
@@ -63,7 +42,7 @@ export interface ParsedPagesList {
63
42
  }
64
43
 
65
44
  export interface SuppressionList {
66
- items: IBounce[] | IComplaint[] | IUnsubscribe[] | IWhiteList[];
45
+ items: (Bounce | Complaint | Unsubscribe | WhiteList)[];
67
46
  pages: ParsedPagesList;
68
47
  }
69
48
 
@@ -84,3 +63,61 @@ export enum SuppressionModels {
84
63
  export interface PagesListAccumulator {
85
64
  [index: string]: ParsedPage;
86
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
+ }
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';
@@ -1,38 +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,
9
- IBounce,
10
- IComplaint,
11
- IUnsubscribe,
12
- IWhiteList,
13
8
  PagesList,
14
9
  PagesListAccumulator,
15
10
  ParsedPage,
16
11
  ParsedPagesList,
12
+ SuppressionCreationData,
13
+ SuppressionCreationResponse,
14
+ SuppressionCreationResult,
15
+ SuppressionDestroyResponse,
16
+ SuppressionDestroyResult,
17
17
  SuppressionList,
18
+ SuppressionListQuery,
19
+ SuppressionListResponse,
18
20
  SuppressionModels,
21
+ SuppressionResponse,
19
22
  UnsubscribeData,
20
- WhiteListData
23
+ WhiteListData,
21
24
  } from './interfaces/Supressions';
25
+ import APIError from './error';
26
+ import APIErrorOptions from './interfaces/APIErrorOptions';
22
27
 
23
28
  const createOptions = {
24
29
  headers: { 'Content-Type': 'application/json' }
25
30
  };
26
-
27
- class Bounce implements IBounce {
31
+ export class Suppression {
28
32
  type: string;
33
+ constructor(type: SuppressionModels) {
34
+ this.type = type;
35
+ }
36
+ }
37
+ export class Bounce extends Suppression {
29
38
  address: string;
30
39
  code: number;
31
40
  error: string;
32
41
  created_at: Date;
33
42
 
34
43
  constructor(data: BounceData) {
35
- this.type = 'bounces';
44
+ super(SuppressionModels.BOUNCES);
36
45
  this.address = data.address;
37
46
  this.code = +data.code;
38
47
  this.error = data.error;
@@ -40,80 +49,68 @@ class Bounce implements IBounce {
40
49
  }
41
50
  }
42
51
 
43
- class Complaint implements IComplaint {
44
- type: string;
45
- address: any;
52
+ export class Complaint extends Suppression {
53
+ address: string | undefined;
46
54
  created_at: Date;
47
55
 
48
56
  constructor(data: ComplaintData) {
49
- this.type = 'complaints';
57
+ super(SuppressionModels.COMPLAINTS);
50
58
  this.address = data.address;
51
59
  this.created_at = new Date(data.created_at);
52
60
  }
53
61
  }
54
62
 
55
- class Unsubscribe implements IUnsubscribe {
56
- type: string;
63
+ export class Unsubscribe extends Suppression {
57
64
  address: string;
58
- tags: any;
65
+ tags: string[];
59
66
  created_at: Date;
60
67
 
61
68
  constructor(data: UnsubscribeData) {
62
- this.type = 'unsubscribes';
69
+ super(SuppressionModels.UNSUBSCRIBES);
63
70
  this.address = data.address;
64
71
  this.tags = data.tags;
65
72
  this.created_at = new Date(data.created_at);
66
73
  }
67
74
  }
68
75
 
69
- class WhiteList implements IWhiteList {
70
- type: string;
76
+ export class WhiteList extends Suppression {
71
77
  value: string;
72
78
  reason: string;
73
79
  createdAt: Date;
74
80
 
75
81
  constructor(data: WhiteListData) {
76
- this.type = 'whitelists';
82
+ super(SuppressionModels.WHITELISTS);
77
83
  this.value = data.value;
78
84
  this.reason = data.reason;
79
85
  this.createdAt = new Date(data.createdAt);
80
86
  }
81
87
  }
82
88
 
83
- type TModel = typeof Bounce | typeof Complaint | typeof Unsubscribe | typeof WhiteList;
84
-
85
89
  export default class SuppressionClient {
86
- request: any;
87
- models: {
88
- bounces: typeof Bounce;
89
- complaints: typeof Complaint;
90
- unsubscribes: typeof Unsubscribe;
91
- whitelists: typeof WhiteList;
92
- };
90
+ request: Request;
91
+ models: Map<string, any>;
93
92
 
94
93
  constructor(request: Request) {
95
94
  this.request = request;
96
- this.models = {
97
- bounces: Bounce,
98
- complaints: Complaint,
99
- unsubscribes: Unsubscribe,
100
- whitelists: WhiteList,
101
- };
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);
102
100
  }
103
101
 
104
102
  _parsePage(id: string, pageUrl: string) : ParsedPage {
105
- const parsedUrl = url.parse(pageUrl, true);
106
- const { query } = parsedUrl;
107
-
103
+ const parsedUrl = new URL(pageUrl);
104
+ const { searchParams } = parsedUrl;
108
105
  return {
109
106
  id,
110
- page: query.page as string,
111
- address: query.address as string,
107
+ page: searchParams.has('page') ? searchParams.get('page') : undefined,
108
+ address: searchParams.has('address') ? searchParams.get('address') : undefined,
112
109
  url: pageUrl
113
110
  };
114
111
  }
115
112
 
116
- _parsePageLinks(response: { body: { paging: any } }): ParsedPagesList {
113
+ _parsePageLinks(response: SuppressionListResponse): ParsedPagesList {
117
114
  const pages = Object.entries(response.body.paging as PagesList);
118
115
  return pages.reduce(
119
116
  (acc: PagesListAccumulator, pair: [pageUrl: string, id: string]) => {
@@ -126,51 +123,99 @@ export default class SuppressionClient {
126
123
  }
127
124
 
128
125
  _parseList(
129
- response: { body: { items: any, paging: any } }, Model: TModel
126
+ response: SuppressionListResponse,
127
+ Model: {
128
+ new(data: BounceData | ComplaintData | UnsubscribeData | WhiteListData):
129
+ Bounce | Complaint | Unsubscribe | WhiteList
130
+ }
130
131
  ): SuppressionList {
131
132
  const data = {} as SuppressionList;
132
-
133
- data.items = response.body.items.map((d: any) => new Model(d));
133
+ data.items = response.body.items.map((item) => new Model(item));
134
134
 
135
135
  data.pages = this._parsePageLinks(response);
136
136
 
137
137
  return data;
138
138
  }
139
139
 
140
- _parseItem(
141
- response: { body: any },
142
- Model: TModel
143
- ): IBounce | IComplaint | IUnsubscribe | IWhiteList {
144
- 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);
145
148
  }
146
149
 
147
- 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
+ }
148
163
  return this.request
149
- .postWithFD(urljoin('v3', domain, 'whitelists'), data, createOptions)
150
- .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
+ };
151
185
  }
152
186
 
153
- list(domain: string, type: SuppressionModels, query: any) : Promise<SuppressionList> {
154
- const model = (this.models)[type];
187
+ list(
188
+ domain: string,
189
+ type: string,
190
+ query?: SuppressionListQuery
191
+ ): Promise<SuppressionList> {
192
+ this.checkType(type);
155
193
 
194
+ const model = this.models.get(type);
156
195
  return this.request
157
196
  .get(urljoin('v3', domain, type), query)
158
- .then((response: { body: { items: any, paging: any } }) => this._parseList(response, model));
197
+ .then((response: SuppressionListResponse) => this._parseList(response, model));
159
198
  }
160
199
 
161
200
  get(
162
201
  domain: string,
163
- type: SuppressionModels,
202
+ type: string,
164
203
  address: string
165
- ): Promise<IBounce | IComplaint | IUnsubscribe | IWhiteList> {
166
- const model = (this.models)[type];
204
+ ): Promise<Bounce | Complaint | Unsubscribe | WhiteList> {
205
+ this.checkType(type);
167
206
 
207
+ const model = this.models.get(type);
168
208
  return this.request
169
209
  .get(urljoin('v3', domain, type, encodeURIComponent(address)))
170
- .then((response: { body: any }) => this._parseItem(response, model));
210
+ .then((response: SuppressionResponse) => this._parseItem<typeof model>(response.body, model));
171
211
  }
172
212
 
173
- 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);
174
219
  // supports adding multiple suppressions by default
175
220
  let postData;
176
221
  if (type === 'whitelists') {
@@ -185,13 +230,23 @@ export default class SuppressionClient {
185
230
 
186
231
  return this.request
187
232
  .post(urljoin('v3', domain, type), JSON.stringify(postData), createOptions)
188
- .then((response: { body: any }) => response.body);
233
+ .then(this.prepareResponse);
189
234
  }
190
235
 
191
- 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);
192
242
  return this.request
193
243
  .delete(urljoin('v3', domain, type, encodeURIComponent(address)))
194
- .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
+ }));
195
250
  }
196
251
  }
197
252
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailgun.js",
3
- "version": "5.0.2",
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",
@@ -6,6 +6,7 @@ import Request from '../lib/request';
6
6
  import SuppressionClient from '../lib/suppressions';
7
7
  import RequestOptions from '../lib/interfaces/RequestOptions';
8
8
  import { InputFormData } from '../lib/interfaces/IFormData';
9
+ import { WhiteListData } from '../lib/interfaces/Supressions';
9
10
 
10
11
  chai.should();
11
12
 
@@ -123,6 +124,38 @@ describe('SuppressionsClient', function () {
123
124
  });
124
125
  });
125
126
 
127
+ it('fetches WhiteLists', function () {
128
+ response.items = [{
129
+ type: 'whitelists',
130
+ value: 'brad@example.com',
131
+ reason: 'first reason',
132
+ createdAt: '2021-11-30T10:38:56.000Z'
133
+ }, {
134
+ type: 'whitelists',
135
+ value: 'roman@example.com',
136
+ reason: 'second reason',
137
+ createdAt: '2021-11-30T10:38:56.000Z'
138
+ }];
139
+
140
+ api.get('/v3/domain.com/whitelists').reply(200, response);
141
+
142
+ return client.list('domain.com', 'whitelists')
143
+ .then(function (complaints: { items: WhiteListData[] }) {
144
+ let c;
145
+ c = complaints.items[0];
146
+ c.type.should.eql('whitelists');
147
+ c.value.should.eql('brad@example.com');
148
+ c.reason.should.eql('first reason');
149
+ c.createdAt.should.eql(new Date('2021-11-30T10:38:56.000Z'));
150
+
151
+ c = complaints.items[1];
152
+ c.type.should.eql('whitelists');
153
+ c.value.should.eql('roman@example.com');
154
+ c.reason.should.eql('second reason');
155
+ c.createdAt.should.eql(new Date('2021-11-30T10:38:56.000Z'));
156
+ });
157
+ });
158
+
126
159
  it('parses page links', function () {
127
160
  api.get('/v3/domain.com/bounces').reply(200, response);
128
161
 
@@ -186,21 +219,72 @@ describe('SuppressionsClient', function () {
186
219
  });
187
220
  });
188
221
  });
189
-
190
222
  describe('create', function () {
191
- it('creates suppression', function () {
192
- api.post('/v3/domain.com/bounces').reply(200, {
193
- message: 'Bounced address has been inserted',
194
- address: 'myaddress'
223
+ describe('create bounce', function () {
224
+ it('creates suppression', function () {
225
+ api.post('/v3/domain.com/bounces').reply(200, {
226
+ message: '1 addresses have been added to the bounces table'
227
+ });
228
+
229
+ return client.create('domain.com', 'bounces', {
230
+ address: 'myaddress',
231
+ code: 550
232
+ }).then(function (data: { message: string }) {
233
+ data.message.should.eql('1 addresses have been added to the bounces table');
234
+ });
195
235
  });
236
+ });
196
237
 
197
- return client.create('domain.com', 'bounces', {
198
- address: 'myaddress',
199
- code: 550
200
- }).then(function (data: { address: string }) {
201
- data.address.should.eql('myaddress');
238
+ describe('create multiple bounces', function () {
239
+ it('creates bounces', async function () {
240
+ const message = '2 addresses have been added to the bounces table';
241
+ api.post('/v3/domain.com/bounces').reply(200, {
242
+ message
243
+ });
244
+ return client.create('domain.com', 'bounces',
245
+ [{ address: 'myaddress' }, { address: 'myaddress1' }])
246
+ .then(function (data: { message: string }) {
247
+ data.message.should.eql(message);
248
+ });
249
+ });
250
+ });
251
+ describe('create whitelists', function () {
252
+ it('creates whitelist', async function () {
253
+ const message = '1 addresses have been added to the whitelists table';
254
+ api.post('/v3/domain.com/whitelists').reply(200, {
255
+ message
256
+ });
257
+ return client.create('domain.com', 'whitelists',
258
+ { address: 'myaddress' })
259
+ .then(function (data: { message: string }) {
260
+ data.message.should.eql(message);
261
+ });
202
262
  });
203
263
  });
264
+
265
+ describe('create multiple whitelists', function () {
266
+ it('throws in case multiple whitelists provided', async function () {
267
+ try {
268
+ await client.create('domain.com', 'whitelists',
269
+ [{ address: 'myaddress' }, { address: 'myaddress1' }]);
270
+ } catch (error: any) {
271
+ error.message.should.eql('Data property should be an object');
272
+ error.details.should.eql("Whitelist's creation process does not support multiple creations. Data property should be an object");
273
+ error.status.should.eql(400);
274
+ }
275
+ });
276
+ });
277
+
278
+ it('throws in case type is unknown', async function () {
279
+ try {
280
+ await client.create('domain.com', 'wrong type',
281
+ { address: 'myaddress' });
282
+ } catch (error: any) {
283
+ error.message.should.eql('Unknown type value');
284
+ error.details.should.eql('Type may be only one of [bounces, complaints, unsubscribes, whitelists]');
285
+ error.status.should.eql(400);
286
+ }
287
+ });
204
288
  });
205
289
 
206
290
  describe('destroy', function () {
@@ -16,7 +16,6 @@ const commonConfig = {
16
16
  path: path.join(SRC_DIR, outputDir),
17
17
  filename: 'mailgun.js',
18
18
  library: 'mailgun',
19
- libraryTarget: 'umd',
20
19
  globalObject: 'this',
21
20
  },
22
21
  module: {