mailgun.js 4.0.0 → 4.1.2
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/CHANGELOG.md +28 -0
- package/dist/lib/interfaces/StatsOptions.d.ts +17 -10
- package/dist/lib/interfaces/Supressions.d.ts +6 -0
- package/dist/lib/request.d.ts +5 -1
- package/dist/lib/stats.d.ts +5 -4
- package/dist/lib/suppressions.d.ts +12 -3
- package/dist/mailgun.node.js +2 -2
- package/dist/mailgun.node.js.LICENSE.txt +1 -1
- package/dist/mailgun.web.js +2 -2
- package/dist/mailgun.web.js.LICENSE.txt +1 -1
- package/lib/interfaces/StatsOptions.ts +18 -10
- package/lib/interfaces/Supressions.ts +7 -0
- package/lib/ip-pools.ts +2 -2
- package/lib/request.ts +77 -33
- package/lib/stats.ts +27 -8
- package/lib/suppressions.ts +36 -5
- package/package.json +3 -2
- package/test/stats.test.ts +1 -1
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
interface
|
|
1
|
+
export interface Stat {
|
|
2
|
+
time: string | Date,
|
|
3
|
+
delivered: {
|
|
4
|
+
smtp: number,
|
|
5
|
+
http: number,
|
|
6
|
+
total: number
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface StatsOptions {
|
|
2
11
|
start: string | Date;
|
|
3
12
|
end: string | Date;
|
|
4
13
|
resolution: string;
|
|
5
|
-
stats:
|
|
6
|
-
time: string | Date,
|
|
7
|
-
delivered: {
|
|
8
|
-
smtp: number,
|
|
9
|
-
http: number,
|
|
10
|
-
total: number
|
|
11
|
-
}
|
|
12
|
-
}[];
|
|
14
|
+
stats: Stat[];
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export
|
|
17
|
+
export interface StatsQuery {
|
|
18
|
+
event: string | string[];
|
|
19
|
+
start: string | Date;
|
|
20
|
+
end: string | Date;
|
|
21
|
+
resolution: 'hour'| 'day' | 'month';
|
|
22
|
+
duration: string;
|
|
23
|
+
}
|
package/lib/ip-pools.ts
CHANGED
|
@@ -16,12 +16,12 @@ export default class IpPoolsClient {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
create(data: { name: string, description?: string, ips?: string[] }) {
|
|
19
|
-
return this.request.
|
|
19
|
+
return this.request.postWithFD('/v1/ip_pools', data)
|
|
20
20
|
.then((response: { body: { message: string, pool_id: string } }) => response?.body);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
update(poolId: string, data: IpPoolUpdateData) : Promise<any> {
|
|
24
|
-
return this.request.
|
|
24
|
+
return this.request.patchWithFD(`/v1/ip_pools/${poolId}`, data)
|
|
25
25
|
.then((response: { body: any }) => response?.body);
|
|
26
26
|
}
|
|
27
27
|
|
package/lib/request.ts
CHANGED
|
@@ -50,7 +50,7 @@ class Request {
|
|
|
50
50
|
private url: string;
|
|
51
51
|
private timeout: number;
|
|
52
52
|
private headers: any;
|
|
53
|
-
private
|
|
53
|
+
private FormDataConstructor: InputFormData;
|
|
54
54
|
|
|
55
55
|
constructor(options: RequestOptions, formData: InputFormData) {
|
|
56
56
|
this.username = options.username;
|
|
@@ -58,7 +58,7 @@ class Request {
|
|
|
58
58
|
this.url = options.url as string;
|
|
59
59
|
this.timeout = options.timeout;
|
|
60
60
|
this.headers = options.headers || {};
|
|
61
|
-
this.
|
|
61
|
+
this.FormDataConstructor = formData;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
async request(method: string, url: string, inputOptions?: any): Promise<APIResponse> {
|
|
@@ -165,50 +165,94 @@ class Request {
|
|
|
165
165
|
return this.command('put', url, formData, params);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
patchWithFD(url: string, data: any): Promise<APIResponse> {
|
|
169
|
+
if (!data) {
|
|
170
|
+
throw new Error('Please provide data object');
|
|
171
|
+
}
|
|
172
|
+
const params: any = {
|
|
173
|
+
headers: { 'Content-Type': null }
|
|
174
|
+
};
|
|
175
|
+
const formData = this.createFormData(data);
|
|
176
|
+
return this.command('patch', url, formData, params);
|
|
177
|
+
}
|
|
178
|
+
|
|
168
179
|
createFormData(data: any): NodeFormData | FormData {
|
|
180
|
+
const formData: NodeFormData | FormData = Object.keys(data)
|
|
181
|
+
.filter(function (key) { return data[key]; })
|
|
182
|
+
.reduce((formDataAcc: NodeFormData | FormData, key) => {
|
|
183
|
+
const fileKeys = ['attachment', 'inline', 'file'];
|
|
184
|
+
if (fileKeys.includes(key)) {
|
|
185
|
+
this.addFilesToFD(key, data[key], formDataAcc);
|
|
186
|
+
return formDataAcc;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (key === 'message') { // mime message
|
|
190
|
+
this.addMimeDataToFD(key, data[key], formDataAcc);
|
|
191
|
+
return formDataAcc;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.addCommonPropertyToFD(key, data[key], formDataAcc);
|
|
195
|
+
return formDataAcc;
|
|
196
|
+
}, new this.FormDataConstructor());
|
|
197
|
+
return formData;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private addMimeDataToFD(
|
|
201
|
+
key: string,
|
|
202
|
+
data: Buffer | Blob,
|
|
203
|
+
formDataInstance: NodeFormData | FormData
|
|
204
|
+
): void {
|
|
205
|
+
if (isNodeFormData(formDataInstance)) {
|
|
206
|
+
if (Buffer.isBuffer(data)) {
|
|
207
|
+
formDataInstance.append(key, data, { filename: 'MimeMessage' });
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
formDataInstance.append(key, data as Blob, 'MimeMessage');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private addFilesToFD(
|
|
215
|
+
propertyName: string,
|
|
216
|
+
value: any,
|
|
217
|
+
formDataInstance: NodeFormData | FormData
|
|
218
|
+
): void {
|
|
169
219
|
const appendFileToFD = (
|
|
170
220
|
key: string,
|
|
171
221
|
obj: any,
|
|
172
|
-
|
|
222
|
+
formData: NodeFormData | FormData
|
|
173
223
|
): void => {
|
|
174
224
|
const isStreamData = isStream(obj);
|
|
175
225
|
const objData = isStreamData ? obj : obj.data;
|
|
226
|
+
// getAttachmentOptions should be called with obj parameter to prevent loosing filename
|
|
176
227
|
const options = getAttachmentOptions(obj);
|
|
177
|
-
if (isNodeFormData(
|
|
178
|
-
|
|
228
|
+
if (isNodeFormData(formData)) {
|
|
229
|
+
formData.append(key, objData, options);
|
|
179
230
|
return;
|
|
180
231
|
}
|
|
181
|
-
|
|
232
|
+
formData.append(key, objData, options.filename);
|
|
182
233
|
};
|
|
183
234
|
|
|
184
|
-
|
|
185
|
-
.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
appendFileToFD(key, item, formDataAcc);
|
|
193
|
-
});
|
|
194
|
-
} else {
|
|
195
|
-
appendFileToFD(key, obj, formDataAcc);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return formDataAcc;
|
|
199
|
-
}
|
|
235
|
+
if (Array.isArray(value)) {
|
|
236
|
+
value.forEach(function (item) {
|
|
237
|
+
appendFileToFD(propertyName, item, formDataInstance);
|
|
238
|
+
});
|
|
239
|
+
} else {
|
|
240
|
+
appendFileToFD(propertyName, value, formDataInstance);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
200
243
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
244
|
+
private addCommonPropertyToFD(
|
|
245
|
+
key: string,
|
|
246
|
+
value: any,
|
|
247
|
+
formDataAcc: NodeFormData | FormData
|
|
248
|
+
): void {
|
|
249
|
+
if (Array.isArray(value)) {
|
|
250
|
+
value.forEach(function (item: any) {
|
|
251
|
+
formDataAcc.append(key, item);
|
|
252
|
+
});
|
|
253
|
+
} else if (value != null) {
|
|
254
|
+
formDataAcc.append(key, value);
|
|
255
|
+
}
|
|
212
256
|
}
|
|
213
257
|
|
|
214
258
|
put(url: string, data: any, options?: any): Promise<APIResponse> {
|
package/lib/stats.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import urljoin from 'url-join';
|
|
2
2
|
import Request from './request';
|
|
3
|
-
import StatsOptions from './interfaces/StatsOptions';
|
|
3
|
+
import { StatsQuery, StatsOptions, Stat } from './interfaces/StatsOptions';
|
|
4
4
|
|
|
5
5
|
class Stats {
|
|
6
6
|
start: Date;
|
|
7
7
|
end: Date;
|
|
8
8
|
resolution: string;
|
|
9
|
-
stats:
|
|
9
|
+
stats: Stat[];
|
|
10
10
|
|
|
11
11
|
constructor(data: StatsOptions) {
|
|
12
12
|
this.start = new Date(data.start);
|
|
13
13
|
this.end = new Date(data.end);
|
|
14
14
|
this.resolution = data.resolution;
|
|
15
|
-
this.stats = data.stats.map(function (stat:
|
|
15
|
+
this.stats = data.stats.map(function (stat: Stat) {
|
|
16
16
|
const res = { ...stat };
|
|
17
17
|
res.time = new Date(stat.time);
|
|
18
18
|
return res;
|
|
@@ -27,17 +27,36 @@ export default class StatsClient {
|
|
|
27
27
|
this.request = request;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
private prepareSearchParams(query: StatsQuery): Array<Array<string>> {
|
|
31
|
+
let searchParams = [];
|
|
32
|
+
if (typeof query === 'object' && Object.keys(query).length) {
|
|
33
|
+
searchParams = Object.entries(query).reduce((arrayWithPairs, currentPair) => {
|
|
34
|
+
const [key, value] = currentPair;
|
|
35
|
+
if (Array.isArray(value) && value.length) {
|
|
36
|
+
const repeatedProperty = value.map((item) => [key, item]);
|
|
37
|
+
return [...arrayWithPairs, ...repeatedProperty];
|
|
38
|
+
}
|
|
39
|
+
arrayWithPairs.push([key, value]);
|
|
40
|
+
return arrayWithPairs;
|
|
41
|
+
}, []);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return searchParams;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_parseStats(response: { body: StatsOptions }): Stats {
|
|
31
48
|
return new Stats(response.body);
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
getDomain(domain: string, query
|
|
35
|
-
|
|
51
|
+
getDomain(domain: string, query?: StatsQuery): Promise<Stats> {
|
|
52
|
+
const searchParams = this.prepareSearchParams(query);
|
|
53
|
+
return this.request.get(urljoin('/v3', domain, 'stats/total'), searchParams)
|
|
36
54
|
.then(this._parseStats);
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
getAccount(query
|
|
40
|
-
|
|
57
|
+
getAccount(query?: StatsQuery): Promise<Stats> {
|
|
58
|
+
const searchParams = this.prepareSearchParams(query);
|
|
59
|
+
return this.request.get('/v3/stats/total', searchParams)
|
|
41
60
|
.then(this._parseStats);
|
|
42
61
|
}
|
|
43
62
|
}
|
package/lib/suppressions.ts
CHANGED
|
@@ -3,7 +3,12 @@ import url from 'url';
|
|
|
3
3
|
import urljoin from 'url-join';
|
|
4
4
|
|
|
5
5
|
import Request from './request';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
BounceData,
|
|
8
|
+
ComplaintData,
|
|
9
|
+
UnsubscribeData,
|
|
10
|
+
WhiteListData
|
|
11
|
+
} from './interfaces/Supressions';
|
|
7
12
|
|
|
8
13
|
const createOptions = {
|
|
9
14
|
headers: { 'Content-Type': 'application/json' }
|
|
@@ -51,7 +56,21 @@ class Unsubscribe {
|
|
|
51
56
|
}
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
class WhiteList {
|
|
60
|
+
type: string;
|
|
61
|
+
value: string;
|
|
62
|
+
reason: string;
|
|
63
|
+
createdAt: Date;
|
|
64
|
+
|
|
65
|
+
constructor(data: WhiteListData) {
|
|
66
|
+
this.type = 'whitelists';
|
|
67
|
+
this.value = data.value;
|
|
68
|
+
this.reason = data.reason;
|
|
69
|
+
this.createdAt = new Date(data.createdAt);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type TModel = typeof Bounce | typeof Complaint | typeof Unsubscribe | typeof WhiteList;
|
|
55
74
|
|
|
56
75
|
export default class SuppressionClient {
|
|
57
76
|
request: any;
|
|
@@ -59,6 +78,7 @@ export default class SuppressionClient {
|
|
|
59
78
|
bounces: typeof Bounce;
|
|
60
79
|
complaints: typeof Complaint;
|
|
61
80
|
unsubscribes: typeof Unsubscribe;
|
|
81
|
+
whitelists: typeof WhiteList;
|
|
62
82
|
};
|
|
63
83
|
|
|
64
84
|
constructor(request: Request) {
|
|
@@ -66,7 +86,8 @@ export default class SuppressionClient {
|
|
|
66
86
|
this.models = {
|
|
67
87
|
bounces: Bounce,
|
|
68
88
|
complaints: Complaint,
|
|
69
|
-
unsubscribes: Unsubscribe
|
|
89
|
+
unsubscribes: Unsubscribe,
|
|
90
|
+
whitelists: WhiteList,
|
|
70
91
|
};
|
|
71
92
|
}
|
|
72
93
|
|
|
@@ -106,6 +127,12 @@ export default class SuppressionClient {
|
|
|
106
127
|
return new Model(response.body);
|
|
107
128
|
}
|
|
108
129
|
|
|
130
|
+
private createWhiteList(domain: string, data: any) {
|
|
131
|
+
return this.request
|
|
132
|
+
.postWithFD(urljoin('v3', domain, 'whitelists'), data, createOptions)
|
|
133
|
+
.then((response: { body: any }) => response.body);
|
|
134
|
+
}
|
|
135
|
+
|
|
109
136
|
list(domain: string, type: string, query: any) {
|
|
110
137
|
const model = (this.models as any)[type];
|
|
111
138
|
|
|
@@ -125,14 +152,18 @@ export default class SuppressionClient {
|
|
|
125
152
|
create(domain: string, type: string, data: any) {
|
|
126
153
|
// supports adding multiple suppressions by default
|
|
127
154
|
let postData;
|
|
155
|
+
if (type === 'whitelists') {
|
|
156
|
+
return this.createWhiteList(domain, data);
|
|
157
|
+
}
|
|
158
|
+
|
|
128
159
|
if (!Array.isArray(data)) {
|
|
129
160
|
postData = [data];
|
|
130
161
|
} else {
|
|
131
|
-
postData =
|
|
162
|
+
postData = [...data];
|
|
132
163
|
}
|
|
133
164
|
|
|
134
165
|
return this.request
|
|
135
|
-
.post(urljoin('v3', domain, type), postData, createOptions)
|
|
166
|
+
.post(urljoin('v3', domain, type), JSON.stringify(postData), createOptions)
|
|
136
167
|
.then((response: { body: any }) => response.body);
|
|
137
168
|
}
|
|
138
169
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailgun.js",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"main": "dist/mailgun.node.js",
|
|
5
5
|
"browser": "dist/mailgun.web.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -105,7 +105,8 @@
|
|
|
105
105
|
"commitUrlFormat": "https://github.com/mailgun/mailgun.js/commits/{{hash}}",
|
|
106
106
|
"compareUrlFormat": "https://github.com/mailgun/mailgun.js/compare/{{previousTag}}...{{currentTag}}",
|
|
107
107
|
"scripts": {
|
|
108
|
-
"prerelease": "npm test && webpack --config ./webpack/webpack.release.config.js --progress --color && git add -A dist"
|
|
108
|
+
"prerelease": "npm test && webpack --config ./webpack/webpack.release.config.js --progress --color && git add -A dist",
|
|
109
|
+
"posttag": "git push && git push --tags && rm -rf build"
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
112
|
}
|
package/test/stats.test.ts
CHANGED
|
@@ -4,7 +4,7 @@ import nock from 'nock';
|
|
|
4
4
|
import Request from '../lib/request';
|
|
5
5
|
import StatsClient from '../lib/stats';
|
|
6
6
|
import RequestOptions from '../lib/interfaces/RequestOptions';
|
|
7
|
-
import StatsOptions from '../lib/interfaces/StatsOptions';
|
|
7
|
+
import { StatsOptions } from '../lib/interfaces/StatsOptions';
|
|
8
8
|
import { InputFormData } from '../lib/interfaces/IFormData';
|
|
9
9
|
|
|
10
10
|
describe('StatsClient', function () {
|