notifications-node-client 8.2.1 → 8.3.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/.dockerignore ADDED
@@ -0,0 +1 @@
1
+ **/.git/*
package/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 8.3.0 - 2026-02-19
2
+
3
+ * Adds Typescript hints (run `npm build:types` to generate type files in `types/`)
4
+ * Bump axios from 1.7.2 to 1.13.5
5
+
1
6
  ## 8.2.1 - 2024-07-03
2
7
 
3
8
  * Fix a bug where an internal API client function isn't being exposed in the library.
package/Dockerfile CHANGED
@@ -6,9 +6,12 @@ RUN \
6
6
  echo "Install base packages" \
7
7
  && apt-get update \
8
8
  && apt-get install -y --no-install-recommends \
9
+ awscli \
9
10
  make \
10
11
  gnupg \
12
+ jq \
11
13
  && echo "Clean up" \
12
14
  && rm -rf /var/lib/apt/lists/* /tmp/*
13
15
 
14
16
  WORKDIR /var/project
17
+ COPY . .
package/Makefile CHANGED
@@ -14,6 +14,7 @@ build: bootstrap ## Build project (dummy task for CI)
14
14
 
15
15
  .PHONY: test
16
16
  test: ## Run tests
17
+ npm run check:types
17
18
  npm test
18
19
 
19
20
  .PHONY: integration-test
@@ -4,13 +4,12 @@ var defaultRestClient = require('axios').default,
4
4
  version = require('../package.json').version;
5
5
 
6
6
  /**
7
- * @param urlBase
8
- * @param serviceId
9
- * @param apiKeyId
10
- *
7
+ * @param {string} apiKeyOrUrl - API key (1 arg), or base URL (2-3 args)
8
+ * @param {string} [serviceIdOrApiKey] - API key (2 args), or service ID (3 args)
9
+ * @param {string} [apiKeyId] - API key (3 args)
11
10
  * @constructor
12
11
  */
13
- function ApiClient() {
12
+ function ApiClient(apiKeyOrUrl, serviceIdOrApiKey, apiKeyId) {
14
13
 
15
14
  this.proxy = null;
16
15
  this.restClient = defaultRestClient;
@@ -56,67 +55,61 @@ function createToken(requestMethod, requestPath, apiKeyId, serviceId) {
56
55
  return createGovukNotifyToken(requestMethod, requestPath, apiKeyId, serviceId);
57
56
  }
58
57
 
59
- Object.assign(ApiClient.prototype, {
60
-
61
- /**
62
- *
63
- * @param {string} path
64
- *
65
- * @returns {Promise}
66
- */
67
- get: function(path, additionalOptions) {
68
- var options = {
69
- method: 'get',
70
- url: this.urlBase + path,
71
- headers: {
72
- 'Authorization': 'Bearer ' + createToken('GET', path, this.apiKeyId, this.serviceId),
73
- 'User-Agent': 'NOTIFY-API-NODE-CLIENT/' + version
74
- }
75
- };
76
- Object.assign(options, additionalOptions)
77
- if(this.proxy !== null) options.proxy = this.proxy;
78
-
79
- return this.restClient(options);
80
- },
81
-
82
- /**
83
- *
84
- * @param {string} path
85
- * @param {object} data
86
- *
87
- * @returns {Promise}
88
- */
89
- post: function(path, data){
90
- var options = {
91
- method: 'post',
92
- url: this.urlBase + path,
93
- data: data,
94
- headers: {
95
- 'Authorization': 'Bearer ' + createToken('GET', path, this.apiKeyId, this.serviceId),
96
- 'User-Agent': 'NOTIFY-API-NODE-CLIENT/' + version
97
- }
98
- };
99
-
100
- if(this.proxy !== null) options.proxy = this.proxy;
101
-
102
- return this.restClient(options);
103
- },
104
-
105
- /**
106
- *
107
- * @param {object} an axios proxy config
108
- */
109
- setProxy: function(proxyConfig){
110
- this.proxy = proxyConfig
111
- },
112
-
113
- /**
114
- *
115
- * @param {object} an axios instance
116
- */
117
- setClient: function(restClient){
118
- this.restClient = restClient;
119
- }
120
- });
58
+ /**
59
+ * @param {string} path
60
+ * @param {import('axios').AxiosRequestConfig} [additionalOptions]
61
+ * @returns {Promise<import('axios').AxiosResponse>}
62
+ */
63
+ ApiClient.prototype.get = function(path, additionalOptions) {
64
+ var options = {
65
+ method: 'get',
66
+ url: this.urlBase + path,
67
+ headers: {
68
+ 'Authorization': 'Bearer ' + createToken('GET', path, this.apiKeyId, this.serviceId),
69
+ 'User-Agent': 'NOTIFY-API-NODE-CLIENT/' + version
70
+ }
71
+ };
72
+ Object.assign(options, additionalOptions)
73
+ if(this.proxy !== null) options.proxy = this.proxy;
74
+
75
+ return this.restClient(options);
76
+ };
77
+
78
+ /**
79
+ * @param {string} path
80
+ * @param {object} data
81
+ * @returns {Promise<import('axios').AxiosResponse>}
82
+ */
83
+ ApiClient.prototype.post = function(path, data){
84
+ var options = {
85
+ method: 'post',
86
+ url: this.urlBase + path,
87
+ data: data,
88
+ headers: {
89
+ 'Authorization': 'Bearer ' + createToken('GET', path, this.apiKeyId, this.serviceId),
90
+ 'User-Agent': 'NOTIFY-API-NODE-CLIENT/' + version
91
+ }
92
+ };
93
+
94
+ if(this.proxy !== null) options.proxy = this.proxy;
95
+
96
+ return this.restClient(options);
97
+ };
98
+
99
+ /**
100
+ * @param {import('axios').AxiosProxyConfig} proxyConfig
101
+ * @returns {void}
102
+ */
103
+ ApiClient.prototype.setProxy = function(proxyConfig){
104
+ this.proxy = proxyConfig
105
+ };
106
+
107
+ /**
108
+ * @param {import('axios').AxiosInstance} restClient
109
+ * @returns {void}
110
+ */
111
+ ApiClient.prototype.setClient = function(restClient){
112
+ this.restClient = restClient;
113
+ };
121
114
 
122
115
  module.exports = ApiClient;
@@ -1,14 +1,72 @@
1
1
  var ApiClient = require('./api_client');
2
2
 
3
3
  /**
4
- *
5
- * @param baseUrl
6
- * @param serviceId
7
- * @param apiKeyId
8
- *
4
+ * @typedef {"sms" | "letter" | "email"} NotificationType
5
+ */
6
+
7
+ /**
8
+ * @typedef {"first" | "second" | "economy" | "europe" | "rest-of-world"} PostageType
9
+ */
10
+
11
+ /**
12
+ * @typedef {Object} TemplateRef
13
+ * @property {string} id
14
+ * @property {number} version
15
+ * @property {string} uri
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} NotificationResponse
20
+ * @property {string} id
21
+ * @property {string} [reference]
22
+ * @property {NotificationType} type
23
+ * @property {string} status
24
+ * @property {{id: string, name: string, version: number}} template
25
+ * @property {string} body
26
+ * @property {string} created_at
27
+ * @property {string} [created_by_name]
28
+ * @property {string} [sent_at]
29
+ * @property {string} [completed_at]
30
+ * @property {string} [scheduled_for]
31
+ * @property {string} [one_click_unsubscribe]
32
+ * @property {boolean} is_cost_data_ready
33
+ * @property {number} [cost_in_pounds]
34
+ * @property {{billable_sheets_of_paper?: number, postage?: string} | {billable_sms_fragments?: number, international_rate_multiplier?: number, sms_rate?: number}} [cost_details]
35
+ * @property {string} [email_address]
36
+ * @property {string} [phone_number]
37
+ * @property {string} [subject]
38
+ * @property {string} [line_1]
39
+ * @property {string} [line_2]
40
+ * @property {string} [line_3]
41
+ * @property {string} [line_4]
42
+ * @property {string} [line_5]
43
+ * @property {string} [line_6]
44
+ * @property {string} [line_7]
45
+ * @property {PostageType} [postage]
46
+ */
47
+
48
+ /**
49
+ * @typedef {Object} TemplateData
50
+ * @property {string} id
51
+ * @property {string} name
52
+ * @property {NotificationType} type
53
+ * @property {string} created_at
54
+ * @property {string | null} updated_at
55
+ * @property {string} created_by
56
+ * @property {number} version
57
+ * @property {string} body
58
+ * @property {string} [subject]
59
+ * @property {string} [letter_contact_block]
60
+ * @property {PostageType} [postage]
61
+ */
62
+
63
+ /**
64
+ * @param {string} apiKeyOrUrl - API key (1 arg), or base URL (2-3 args)
65
+ * @param {string} [serviceIdOrApiKey] - API key (2 args), or service ID (3 args)
66
+ * @param {string} [apiKeyId] - API key (3 args)
9
67
  * @constructor
10
68
  */
11
- function NotifyClient() {
69
+ function NotifyClient(apiKeyOrUrl, serviceIdOrApiKey, apiKeyId) {
12
70
  this.apiClient = new (Function.prototype.bind.apply(
13
71
  ApiClient,
14
72
  [null].concat(Array.prototype.slice.call(arguments))
@@ -126,265 +184,231 @@ function _check_and_encode_file(file, size_limit) {
126
184
  return file.toString('base64')
127
185
  }
128
186
 
129
- Object.assign(NotifyClient.prototype, {
130
- /**
131
- * Usage:
132
- *
133
- * notifyClient = new NotifyClient(urlBase, serviceId, apiKeyId);
134
- *
135
- * notifyClient.sendEmail(templateId, email, personalisation)
136
- * .then(function (response) {
137
- * //do stuff with response
138
- * })
139
- * .catch(function (error) {
140
- * //deal with errors here
141
- * });
142
- *
143
- *
144
- * @param {String} templateId
145
- * @param {String} emailAddress
146
- * @param {Object} options
147
- *
148
- * @returns {Promise}
149
- */
150
- sendEmail: function (templateId, emailAddress, options) {
151
- options = options || {};
152
- var err = checkOptionsKeys(['personalisation', 'reference', 'emailReplyToId', 'oneClickUnsubscribeURL'], options)
153
- if (err) {
154
- return Promise.reject(err);
155
- }
156
- var personalisation = options.personalisation || undefined,
157
- reference = options.reference || undefined,
158
- emailReplyToId = options.emailReplyToId || undefined,
159
- oneClickUnsubscribeURL = options.oneClickUnsubscribeURL || undefined;
160
-
161
- return this.apiClient.post('/v2/notifications/email',
162
- createNotificationPayload('email', templateId, emailAddress, personalisation, reference, emailReplyToId, oneClickUnsubscribeURL));
163
- },
164
-
165
- /**
166
- *
167
- * @param {String} templateId
168
- * @param {String} phoneNumber
169
- * @param {Object} options
170
- *
171
- * @returns {Promise}
172
- */
173
- sendSms: function (templateId, phoneNumber, options) {
174
- options = options || {};
175
- var err = checkOptionsKeys(['personalisation', 'reference', 'smsSenderId'], options)
176
- if (err) {
177
- return Promise.reject(err);
178
- }
179
-
180
- var personalisation = options.personalisation || undefined;
181
- var reference = options.reference || undefined;
182
- var smsSenderId = options.smsSenderId || undefined;
183
-
184
- return this.apiClient.post('/v2/notifications/sms',
185
- createNotificationPayload('sms', templateId, phoneNumber, personalisation, reference, smsSenderId));
186
- },
187
-
188
- /**
189
- *
190
- * @param {String} templateId
191
- * @param {Object} options
192
- *
193
- * @returns {Promise}
194
- */
195
- sendLetter: function (templateId, options) {
196
- options = options || {};
197
- var err = checkOptionsKeys(['personalisation', 'reference'], options)
198
- if (err) {
199
- return Promise.reject(err);
200
- }
201
- var personalisation = options.personalisation || undefined;
202
- var reference = options.reference || undefined;
203
-
204
- return this.apiClient.post('/v2/notifications/letter',
205
- createNotificationPayload('letter', templateId, undefined, personalisation, reference));
206
- },
207
-
208
- sendPrecompiledLetter: function(reference, pdf_file, postage) {
209
- var postage = postage || undefined
210
- var content = _check_and_encode_file(pdf_file, 5)
211
- var notification = {
212
- "reference": reference,
213
- "content": content
214
- }
215
- if (postage != undefined) {
216
- notification["postage"] = postage
217
- }
218
- return this.apiClient.post('/v2/notifications/letter', notification);
219
- },
220
-
221
- /**
222
- *
223
- * @param {String} notificationId
224
- *
225
- * @returns {Promise}
226
- */
227
- getNotificationById: function(notificationId) {
228
- return this.apiClient.get('/v2/notifications/' + notificationId);
229
- },
230
-
231
- /**
232
- *
233
- * @param {String} templateType
234
- * @param {String} status
235
- * @param {String} reference
236
- * @param {String} olderThanId
237
- *
238
- * @returns {Promise}
239
- *
240
- */
241
- getNotifications: function(templateType, status, reference, olderThanId) {
242
- return this.apiClient.get('/v2/notifications' + buildGetAllNotificationsQuery(templateType, status, reference, olderThanId));
243
- },
244
-
245
- /**
246
- *
247
- * @param {String} notificationId
248
- *
249
- * @returns {Promise}
250
- */
251
- getPdfForLetterNotification: function(notificationId) {
252
- const url = '/v2/notifications/' + notificationId + '/pdf'
253
-
254
- // Unlike other requests, we expect a successful response as an arraybuffer and an error as JSON
255
- // Axios does not support flexible response types so we will need to handle the error case ourselves below
256
- return this.apiClient.get(url, { responseType: 'arraybuffer' })
257
- .then(function(response) {
258
- var pdf = Buffer.from(response.data, "base64")
259
- return pdf
260
- })
261
- .catch(function(error) {
262
- // If we receive an error, as the response is an arraybuffer rather than our usual JSON
263
- // we need to convert it to JSON to be read by the user
264
- string_of_error_body = new TextDecoder().decode(error.response.data);
265
-
266
- // Then we replace the error data with the JSON error rather than the arraybuffer of the error
267
- error.response.data = JSON.parse(string_of_error_body);
268
-
269
- // and rethrow to let the user handle the error
270
- throw error
271
- });
272
- },
273
-
274
- /**
275
- *
276
- * @param {String} templateId
277
- *
278
- * @returns {Promise}
279
- */
280
- getTemplateById: function(templateId) {
281
- return this.apiClient.get('/v2/template/' + templateId);
282
- },
283
-
284
- /**
285
- *
286
- * @param {String} templateId
287
- * @param {Integer} version
288
- *
289
- * @returns {Promise}
290
- */
291
- getTemplateByIdAndVersion: function(templateId, version) {
292
- return this.apiClient.get('/v2/template/' + templateId + '/version/' + version);
293
- },
294
-
295
- /**
296
- *
297
- * @param {String} type
298
- *
299
- * @returns {Promise}
300
- */
301
- getAllTemplates: function(templateType) {
302
- let templateQuery = ''
303
-
304
- if (templateType) {
305
- templateQuery = '?type=' + templateType;
306
- }
307
-
308
- return this.apiClient.get('/v2/templates' + templateQuery);
309
- },
310
-
311
- /**
312
- *
313
- * @param {String} templateId
314
- * @param {Dictionary} personalisation
315
- *
316
- * @returns {Promise}
317
- */
318
- previewTemplateById: function(templateId, personalisation) {
319
-
320
- let payload = {}
321
-
322
- if (personalisation) {
323
- payload.personalisation = personalisation;
324
- }
325
-
326
- return this.apiClient.post('/v2/template/' + templateId + '/preview', payload);
327
- },
328
-
329
- /**
330
- *
331
- * @param {String} olderThan
332
- *
333
- * @returns {Promise}
334
- */
335
- getReceivedTexts: function(olderThan){
336
- let queryString;
337
-
338
- if (olderThan) {
339
- queryString = '?older_than=' + olderThan;
340
- } else {
341
- queryString = '';
342
- }
343
-
344
- return this.apiClient.get('/v2/received-text-messages' + queryString);
345
- },
346
-
347
- /**
348
- *
349
- * @param {object} an axios proxy config
350
- */
351
- setProxy: function(proxyConfig) {
352
- this.apiClient.setProxy(proxyConfig);
353
- },
354
-
355
- /**
356
- *
357
- * @param {object} an axios instance
358
- */
359
- setClient: function(client) {
360
- this.apiClient.setClient(client);
361
- },
362
-
363
- /**
364
- *
365
- * @param {Buffer} fileData
366
- * @param {object} options
367
- *
368
- * @returns {Dictionary}
369
- */
370
- prepareUpload: function(fileData, options) {
371
- let data = {
372
- file: _check_and_encode_file(fileData, 2),
373
- filename: null,
374
- confirm_email_before_download: null,
375
- retention_period: null,
376
- }
377
-
378
- if (options !== undefined) {
379
- data.filename = options.filename || null;
380
- data.confirm_email_before_download = options.confirmEmailBeforeDownload !== undefined ? options.confirmEmailBeforeDownload : null;
381
- data.retention_period = options.retentionPeriod || null;
382
- }
383
-
384
- return data;
385
- },
386
-
387
- });
187
+ /**
188
+ * @param {string} templateId
189
+ * @param {string} emailAddress
190
+ * @param {{personalisation?: Object, reference?: string, emailReplyToId?: string, oneClickUnsubscribeURL?: string}} [options]
191
+ * @returns {Promise<import('axios').AxiosResponse<{id: string, reference?: string, content: {body: string, subject: string, from_email: string, one_click_unsubscribe_url?: string}, uri: string, template: TemplateRef}>>}
192
+ */
193
+ NotifyClient.prototype.sendEmail = function (templateId, emailAddress, options) {
194
+ options = options || {};
195
+ var err = checkOptionsKeys(['personalisation', 'reference', 'emailReplyToId', 'oneClickUnsubscribeURL'], options)
196
+ if (err) {
197
+ return Promise.reject(err);
198
+ }
199
+ var personalisation = options.personalisation || undefined,
200
+ reference = options.reference || undefined,
201
+ emailReplyToId = options.emailReplyToId || undefined,
202
+ oneClickUnsubscribeURL = options.oneClickUnsubscribeURL || undefined;
203
+
204
+ return this.apiClient.post('/v2/notifications/email',
205
+ createNotificationPayload('email', templateId, emailAddress, personalisation, reference, emailReplyToId, oneClickUnsubscribeURL));
206
+ };
207
+
208
+ /**
209
+ * @param {string} templateId
210
+ * @param {string} phoneNumber
211
+ * @param {{personalisation?: Object, reference?: string, smsSenderId?: string}} [options]
212
+ * @returns {Promise<import('axios').AxiosResponse<{id: string, reference?: string, content: {body: string, from_number: string}, uri: string, template: TemplateRef}>>}
213
+ */
214
+ NotifyClient.prototype.sendSms = function (templateId, phoneNumber, options) {
215
+ options = options || {};
216
+ var err = checkOptionsKeys(['personalisation', 'reference', 'smsSenderId'], options)
217
+ if (err) {
218
+ return Promise.reject(err);
219
+ }
220
+
221
+ var personalisation = options.personalisation || undefined;
222
+ var reference = options.reference || undefined;
223
+ var smsSenderId = options.smsSenderId || undefined;
224
+
225
+ return this.apiClient.post('/v2/notifications/sms',
226
+ createNotificationPayload('sms', templateId, phoneNumber, personalisation, reference, smsSenderId));
227
+ };
228
+
229
+ /**
230
+ * @param {string} templateId
231
+ * @param {{personalisation?: Object, reference?: string}} [options]
232
+ * @returns {Promise<import('axios').AxiosResponse<{id: string, reference?: string, content: {body: string, subject: string}, uri: string, template: TemplateRef, scheduled_for: string | null}>>}
233
+ */
234
+ NotifyClient.prototype.sendLetter = function (templateId, options) {
235
+ options = options || {};
236
+ var err = checkOptionsKeys(['personalisation', 'reference'], options)
237
+ if (err) {
238
+ return Promise.reject(err);
239
+ }
240
+ var personalisation = options.personalisation || undefined;
241
+ var reference = options.reference || undefined;
242
+
243
+ return this.apiClient.post('/v2/notifications/letter',
244
+ createNotificationPayload('letter', templateId, undefined, personalisation, reference));
245
+ };
246
+
247
+ /**
248
+ * @param {string} reference
249
+ * @param {Buffer | string} pdf_file
250
+ * @param {"first" | "second" | "economy" | "europe" | "rest-of-world"} [postage]
251
+ * @returns {Promise<import('axios').AxiosResponse<{id: string, reference: string, postage: PostageType}>>}
252
+ */
253
+ NotifyClient.prototype.sendPrecompiledLetter = function(reference, pdf_file, postage) {
254
+ var postage = postage || undefined
255
+ var content = _check_and_encode_file(pdf_file, 5)
256
+ var notification = {
257
+ "reference": reference,
258
+ "content": content
259
+ }
260
+ if (postage != undefined) {
261
+ notification["postage"] = postage
262
+ }
263
+ return this.apiClient.post('/v2/notifications/letter', notification);
264
+ };
265
+
266
+ /**
267
+ * @param {string} notificationId
268
+ * @returns {Promise<import('axios').AxiosResponse<NotificationResponse>>}
269
+ */
270
+ NotifyClient.prototype.getNotificationById = function(notificationId) {
271
+ return this.apiClient.get('/v2/notifications/' + notificationId);
272
+ };
273
+
274
+ /**
275
+ * @param {string} [templateType]
276
+ * @param {string} [status]
277
+ * @param {string} [reference]
278
+ * @param {string} [olderThanId]
279
+ * @returns {Promise<import('axios').AxiosResponse<{notifications: NotificationResponse[], links: {current: string, next: string}}>>}
280
+ */
281
+ NotifyClient.prototype.getNotifications = function(templateType, status, reference, olderThanId) {
282
+ return this.apiClient.get('/v2/notifications' + buildGetAllNotificationsQuery(templateType, status, reference, olderThanId));
283
+ };
284
+
285
+ /**
286
+ * @param {string} notificationId
287
+ * @returns {Promise<Buffer>}
288
+ */
289
+ NotifyClient.prototype.getPdfForLetterNotification = function(notificationId) {
290
+ const url = '/v2/notifications/' + notificationId + '/pdf'
291
+
292
+ // Unlike other requests, we expect a successful response as an arraybuffer and an error as JSON
293
+ // Axios does not support flexible response types so we will need to handle the error case ourselves below
294
+ return this.apiClient.get(url, { responseType: 'arraybuffer' })
295
+ .then(function(response) {
296
+ var pdf = Buffer.from(response.data, "base64")
297
+ return pdf
298
+ })
299
+ .catch(function(error) {
300
+ // If we receive an error, as the response is an arraybuffer rather than our usual JSON
301
+ // we need to convert it to JSON to be read by the user
302
+ string_of_error_body = new TextDecoder().decode(error.response.data);
303
+
304
+ // Then we replace the error data with the JSON error rather than the arraybuffer of the error
305
+ error.response.data = JSON.parse(string_of_error_body);
306
+
307
+ // and rethrow to let the user handle the error
308
+ throw error
309
+ });
310
+ };
311
+
312
+ /**
313
+ * @param {string} templateId
314
+ * @returns {Promise<import('axios').AxiosResponse<TemplateData>>}
315
+ */
316
+ NotifyClient.prototype.getTemplateById = function(templateId) {
317
+ return this.apiClient.get('/v2/template/' + templateId);
318
+ };
319
+
320
+ /**
321
+ * @param {string} templateId
322
+ * @param {number} version
323
+ * @returns {Promise<import('axios').AxiosResponse<TemplateData>>}
324
+ */
325
+ NotifyClient.prototype.getTemplateByIdAndVersion = function(templateId, version) {
326
+ return this.apiClient.get('/v2/template/' + templateId + '/version/' + version);
327
+ };
328
+
329
+ /**
330
+ * @param {NotificationType} [templateType]
331
+ * @returns {Promise<import('axios').AxiosResponse<{templates: TemplateData[]}>>}
332
+ */
333
+ NotifyClient.prototype.getAllTemplates = function(templateType) {
334
+ let templateQuery = ''
335
+
336
+ if (templateType) {
337
+ templateQuery = '?type=' + templateType;
338
+ }
339
+
340
+ return this.apiClient.get('/v2/templates' + templateQuery);
341
+ };
342
+
343
+ /**
344
+ * @param {string} templateId
345
+ * @param {Object} [personalisation]
346
+ * @returns {Promise<import('axios').AxiosResponse<{id: string, type: NotificationType, version: number, body: string, html?: string, subject?: string, postage?: PostageType}>>}
347
+ */
348
+ NotifyClient.prototype.previewTemplateById = function(templateId, personalisation) {
349
+
350
+ let payload = {}
351
+
352
+ if (personalisation) {
353
+ payload.personalisation = personalisation;
354
+ }
355
+
356
+ return this.apiClient.post('/v2/template/' + templateId + '/preview', payload);
357
+ };
358
+
359
+ /**
360
+ * @param {string} [olderThan]
361
+ * @returns {Promise<import('axios').AxiosResponse<{received_text_messages: Array<{id: string, user_number: string, notify_number: string, created_at: string, service_id: string, content: string}>, links: {current: string, next: string}}>>}
362
+ */
363
+ NotifyClient.prototype.getReceivedTexts = function(olderThan){
364
+ let queryString;
365
+
366
+ if (olderThan) {
367
+ queryString = '?older_than=' + olderThan;
368
+ } else {
369
+ queryString = '';
370
+ }
371
+
372
+ return this.apiClient.get('/v2/received-text-messages' + queryString);
373
+ };
374
+
375
+ /**
376
+ * @param {import('axios').AxiosProxyConfig} proxyConfig
377
+ * @returns {void}
378
+ */
379
+ NotifyClient.prototype.setProxy = function(proxyConfig) {
380
+ this.apiClient.setProxy(proxyConfig);
381
+ };
382
+
383
+ /**
384
+ * @param {import('axios').AxiosInstance} client
385
+ * @returns {void}
386
+ */
387
+ NotifyClient.prototype.setClient = function(client) {
388
+ this.apiClient.setClient(client);
389
+ };
390
+
391
+ /**
392
+ * @param {Buffer | string} fileData
393
+ * @param {{filename?: string, confirmEmailBeforeDownload?: boolean, retentionPeriod?: string}} [options]
394
+ * @returns {{file: string, filename: string | null, confirm_email_before_download: boolean | null, retention_period: string | null}}
395
+ */
396
+ NotifyClient.prototype.prepareUpload = function(fileData, options) {
397
+ let data = {
398
+ file: _check_and_encode_file(fileData, 2),
399
+ filename: null,
400
+ confirm_email_before_download: null,
401
+ retention_period: null,
402
+ }
403
+
404
+ if (options !== undefined) {
405
+ data.filename = options.filename || null;
406
+ data.confirm_email_before_download = options.confirmEmailBeforeDownload !== undefined ? options.confirmEmailBeforeDownload : null;
407
+ data.retention_period = options.retentionPeriod || null;
408
+ }
409
+
410
+ return data;
411
+ };
388
412
 
389
413
  module.exports = {
390
414
  NotifyClient: NotifyClient
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notifications-node-client",
3
- "version": "8.2.1",
3
+ "version": "8.3.0",
4
4
  "homepage": "https://docs.notifications.service.gov.uk/node.html",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,10 +8,13 @@
8
8
  },
9
9
  "description": "GOV.UK Notify Node.js client ",
10
10
  "main": "index.js",
11
+ "types": "types/index.d.ts",
11
12
  "scripts": {
12
13
  "test": "mocha \"spec/**/*.js\" && npm run test:markdown:standard",
13
14
  "test:markdown:standard": "standard-markdown DOCUMENTATION.md",
14
- "integration": "mocha spec/integration/test.js"
15
+ "integration": "mocha spec/integration/test.js",
16
+ "build:types": "tsc",
17
+ "check:types": "tsc --noEmit"
15
18
  },
16
19
  "engines": {
17
20
  "npm": ">=6.14.13",
@@ -24,6 +27,7 @@
24
27
  "jsonwebtoken": "^9.0.2"
25
28
  },
26
29
  "devDependencies": {
30
+ "@types/node": "^25.3.0",
27
31
  "chai": "4.3.4",
28
32
  "chai-as-promised": "7.1.1",
29
33
  "chai-bytes": "0.1.2",
@@ -34,6 +38,7 @@
34
38
  "nock": "9.2.6",
35
39
  "optimist": "0.6.1",
36
40
  "sinon": "^18.0.0",
37
- "standard-markdown": "7.1.0"
41
+ "standard-markdown": "7.1.0",
42
+ "typescript": "^5.9.0"
38
43
  }
39
44
  }
@@ -27,7 +27,7 @@ function make_random_id() {
27
27
 
28
28
  describer('notification api with a live service', function () {
29
29
  // default is 2000 (ms) - api is sometimes slower than this :(
30
- this.timeout(40000)
30
+ this.timeout(120000)
31
31
 
32
32
  let notifyClient;
33
33
  let teamNotifyClient;
@@ -247,7 +247,7 @@ describer('notification api with a live service', function () {
247
247
  }
248
248
  if (err.response.data && (err.response.data.errors[0].error === "PDFNotReadyError")) {
249
249
  count += 1
250
- if (count < 7) {
250
+ if (count < 24) {
251
251
  setTimeout(tryClient, 5000)
252
252
  } else {
253
253
  done(new Error('Too many PDFNotReadyError errors'));
@@ -0,0 +1,153 @@
1
+ // This file is compiled with tsc --noEmit to verify the generated types work correctly.
2
+ // It is NOT run as a test - it only needs to type-check.
3
+
4
+ import { NotifyClient } from '..';
5
+
6
+ // Constructor overloads
7
+ const client1 = new NotifyClient('apiKeyId');
8
+ const client2 = new NotifyClient('https://example.com', 'apiKeyId');
9
+ const client3 = new NotifyClient('https://example.com', 'serviceId', 'apiKeyId');
10
+
11
+ // sendEmail
12
+ async function testSendEmail() {
13
+ const response = await client1.sendEmail('template-id', 'test@example.com', {
14
+ personalisation: { name: 'Test' },
15
+ reference: 'ref-123',
16
+ emailReplyToId: 'reply-to-id',
17
+ oneClickUnsubscribeURL: 'https://example.com/unsub',
18
+ });
19
+ const id: string = response.data.id;
20
+ const subject: string = response.data.content.subject;
21
+ const fromEmail: string = response.data.content.from_email;
22
+ const unsubUrl: string | undefined = response.data.content.one_click_unsubscribe_url;
23
+ const templateId: string = response.data.template.id;
24
+ const templateVersion: number = response.data.template.version;
25
+ const templateUri: string = response.data.template.uri;
26
+ }
27
+
28
+ // sendSms
29
+ async function testSendSms() {
30
+ const response = await client1.sendSms('template-id', '07123456789', {
31
+ personalisation: { code: '1234' },
32
+ reference: 'ref-123',
33
+ smsSenderId: 'sender-id',
34
+ });
35
+ const id: string = response.data.id;
36
+ const body: string = response.data.content.body;
37
+ const fromNumber: string = response.data.content.from_number;
38
+ }
39
+
40
+ // sendLetter
41
+ async function testSendLetter() {
42
+ const response = await client1.sendLetter('template-id', {
43
+ personalisation: { address_line_1: 'Mr Test', address_line_2: '1 Test St', postcode: 'SW1A 1AA' },
44
+ reference: 'ref-123',
45
+ });
46
+ const id: string = response.data.id;
47
+ const scheduledFor: string | null = response.data.scheduled_for;
48
+ }
49
+
50
+ // sendPrecompiledLetter
51
+ async function testSendPrecompiledLetter() {
52
+ const response = await client1.sendPrecompiledLetter('ref', Buffer.from('pdf'), 'first');
53
+ const id: string = response.data.id;
54
+ const postage: string = response.data.postage;
55
+ }
56
+
57
+ // getNotificationById
58
+ async function testGetNotificationById() {
59
+ const response = await client1.getNotificationById('notification-id');
60
+ const id: string = response.data.id;
61
+ const type: "sms" | "letter" | "email" = response.data.type;
62
+ const status: string = response.data.status;
63
+ const body: string = response.data.body;
64
+ const createdAt: string = response.data.created_at;
65
+ const isCostDataReady: boolean = response.data.is_cost_data_ready;
66
+ const emailAddress: string | undefined = response.data.email_address;
67
+ const phoneNumber: string | undefined = response.data.phone_number;
68
+ const subject: string | undefined = response.data.subject;
69
+ const createdByName: string | undefined = response.data.created_by_name;
70
+ const sentAt: string | undefined = response.data.sent_at;
71
+ const completedAt: string | undefined = response.data.completed_at;
72
+ const scheduledFor: string | undefined = response.data.scheduled_for;
73
+ const oneClickUnsubscribe: string | undefined = response.data.one_click_unsubscribe;
74
+ const costInPounds: number | undefined = response.data.cost_in_pounds;
75
+ const line1: string | undefined = response.data.line_1;
76
+ }
77
+
78
+ // getNotifications
79
+ async function testGetNotifications() {
80
+ const response = await client1.getNotifications('sms', 'delivered', 'ref', 'older-than-id');
81
+ const notifications = response.data.notifications;
82
+ const firstNotification = notifications[0];
83
+ const type: "sms" | "letter" | "email" = firstNotification.type;
84
+ const links = response.data.links;
85
+ const current: string = links.current;
86
+ const next: string = links.next;
87
+ }
88
+
89
+ // getPdfForLetterNotification
90
+ async function testGetPdf() {
91
+ const pdf: Buffer = await client1.getPdfForLetterNotification('notification-id');
92
+ }
93
+
94
+ // getTemplateById
95
+ async function testGetTemplateById() {
96
+ const response = await client1.getTemplateById('template-id');
97
+ const id: string = response.data.id;
98
+ const name: string = response.data.name;
99
+ const type: "sms" | "letter" | "email" = response.data.type;
100
+ const createdAt: string = response.data.created_at;
101
+ const version: number = response.data.version;
102
+ const body: string = response.data.body;
103
+ const letterContactBlock: string | undefined = response.data.letter_contact_block;
104
+ const postage: string | undefined = response.data.postage;
105
+ }
106
+
107
+ // getTemplateByIdAndVersion
108
+ async function testGetTemplateByIdAndVersion() {
109
+ const response = await client1.getTemplateByIdAndVersion('template-id', 3);
110
+ const id: string = response.data.id;
111
+ }
112
+
113
+ // getAllTemplates
114
+ async function testGetAllTemplates() {
115
+ const response = await client1.getAllTemplates('email');
116
+ const templates = response.data.templates;
117
+ const first = templates[0];
118
+ const type: "sms" | "letter" | "email" = first.type;
119
+ }
120
+
121
+ // previewTemplateById
122
+ async function testPreviewTemplateById() {
123
+ const response = await client1.previewTemplateById('template-id', { name: 'Test' });
124
+ const id: string = response.data.id;
125
+ const type: "sms" | "letter" | "email" = response.data.type;
126
+ const body: string = response.data.body;
127
+ const html: string | undefined = response.data.html;
128
+ const postage: "first" | "second" | "economy" | "europe" | "rest-of-world" | undefined = response.data.postage;
129
+ }
130
+
131
+ // getReceivedTexts
132
+ async function testGetReceivedTexts() {
133
+ const response = await client1.getReceivedTexts('older-than-id');
134
+ const messages = response.data.received_text_messages;
135
+ const first = messages[0];
136
+ const userNumber: string = first.user_number;
137
+ const serviceId: string = first.service_id;
138
+ }
139
+
140
+ // prepareUpload
141
+ function testPrepareUpload() {
142
+ const result = client1.prepareUpload(Buffer.from('data'), {
143
+ filename: 'test.csv',
144
+ confirmEmailBeforeDownload: true,
145
+ retentionPeriod: '52 weeks',
146
+ });
147
+ const file: string = result.file;
148
+ }
149
+
150
+ // setProxy
151
+ client1.setProxy({ host: 'proxy.example.com', port: 8080 });
152
+
153
+ // setClient - accepts axios instance
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "checkJs": false,
5
+ "declaration": true,
6
+ "emitDeclarationOnly": true,
7
+ "declarationDir": "types",
8
+ "strict": false,
9
+ "esModuleInterop": true,
10
+ "moduleResolution": "node",
11
+ "resolveJsonModule": true,
12
+ "skipLibCheck": true,
13
+ "target": "es2015",
14
+ "module": "commonjs"
15
+ },
16
+ "include": ["client/notification.js", "index.js"],
17
+ "exclude": ["node_modules", "spec"]
18
+ }