notifications-ruby-client 6.2.0 → 6.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3bed01407fc2e19df7f65f9be76877f250bda03e9c7db644bfa86b7d83342f67
4
- data.tar.gz: cf02533dcabab2d337aac521e338845d587417d25d238d5bc549feccc99b79e2
3
+ metadata.gz: 0f795202efa7a08dc79833a2c1ff7aec35a7deadf495c8380e290bff1c11189f
4
+ data.tar.gz: 0e938bb4e2a4ed8d516766131192ef8a7acae0de5dee6d25a69cdfbfe1673456
5
5
  SHA512:
6
- metadata.gz: a1f891ddcaf3120c3ab90f01b990178e879c0e785fe27734b017bdaa990a04fc430ed25e5d0b27ed3e6eed5b42a9022fa6dac34e3d7d2ecaae1f2112a6051838
7
- data.tar.gz: ea20265635c620d5ee7873e6cce5dd8983db0a3489b4aecb9f09122a4c49d07a3a90f1a1eca1e29bb4bf5df0634e856c67c631c9fc0e416439ceb5976e769876
6
+ metadata.gz: b910c3bf6d1e115763bc4e88db202dc33a4d633439e26b5599aab48b5a5d1260cf34ebc4b4f494a911b5d039dc4b4cff2620589ee82e7d73ddfda0b9c35c7429
7
+ data.tar.gz: 28aef4fdb14d28c0100262fa583eaab52e9f7d949490405f13a1c5e351afa8326956f262fbe30a7a9029400f3be16e1638673dea057fff6e4746be2510eb3c4b
data/.dockerignore CHANGED
@@ -1 +1,2 @@
1
+ **/.git/*
1
2
  environment*.sh
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 6.4.0
2
+
3
+ * Add support for the optional `sanitise_content_for` parameter when sending email (list of personalisation placeholder keys to sanitise for Markdown). Documented on `Notifications::Client::Speaker#post`.
4
+ * Add `sanitised_content` to the send-email response object (`Notifications::Client::ResponseNotification`). The API returns a JSON object (Ruby `Hash`): keys are personalisation placeholder names, and each value is another object with `unsanitised` and `sanitised` for placeholders that were rewritten; the object is empty when nothing changed. If the key is absent from the JSON, the client exposes `nil`.
5
+
6
+ ## 6.3.0
7
+ * Update dependencies
8
+ * Permit the `jwt` gem to be upgraded to version 3
9
+ * Remove the `bundler` gem dependency
10
+ * Allow the `factory_bot` gem to be upgraded whilst keeping the restriction in place for Ruby versions below 3
11
+
1
12
  ## 6.2.0
2
13
  * Added fields related to cost data in response:
3
14
  * `is_cost_data_ready`: This field is true if cost data is ready, and false if it isn't (Boolean).
data/Dockerfile CHANGED
@@ -4,11 +4,13 @@ RUN \
4
4
  echo "Install Debian packages" \
5
5
  && apt-get update \
6
6
  && apt-get install -y --no-install-recommends \
7
+ awscli \
7
8
  gcc \
8
9
  make \
9
10
  curl \
10
11
  git \
11
- gnupg
12
+ gnupg \
13
+ jq
12
14
 
13
15
  WORKDIR /var/project
14
16
 
data/bin/test_client.rb CHANGED
@@ -24,7 +24,6 @@ def main
24
24
  test_get_received_texts
25
25
  test_get_pdf_for_letter(client, letter_notification.id)
26
26
  p 'ruby client integration tests pass'
27
- exit 0
28
27
  end
29
28
 
30
29
  def test_get_email_template_by_id(client, id)
@@ -50,27 +49,23 @@ end
50
49
  def test_get_all_templates(client)
51
50
  response = client.get_all_templates
52
51
  unless response.is_a?(Notifications::Client::TemplateCollection)
53
- p 'failed test_get_all_templates response is not a Notifications::Client::TemplateCollection'
54
- exit 1
52
+ raise 'failed test_get_all_templates response is not a Notifications::Client::TemplateCollection'
55
53
  end
56
54
  unless response.collection.length >= 3
57
- p 'failed test_get_all_templates, expected at least 3 templates returned.'
58
- exit 1
55
+ raise 'failed test_get_all_templates, expected at least 3 templates returned.'
59
56
  end
60
- test_template_response(response.collection[0], 'letter', 'test_get_all_templates')
61
- test_template_response(response.collection[1], 'email', 'test_get_all_templates')
62
- test_template_response(response.collection[2], 'sms', 'test_get_all_templates')
57
+ test_template_response(response.collection.find { |template| template.id == ENV['LETTER_TEMPLATE_ID'] }, 'letter', 'test_get_all_templates')
58
+ test_template_response(response.collection.find { |template| template.id == ENV['EMAIL_TEMPLATE_ID'] }, 'email', 'test_get_all_templates')
59
+ test_template_response(response.collection.find { |template| template.id == ENV['SMS_TEMPLATE_ID'] }, 'sms', 'test_get_all_templates')
63
60
  end
64
61
 
65
62
  def test_get_all_templates_filter_by_type(client)
66
63
  response = client.get_all_templates('type' => 'sms')
67
64
  unless response.is_a?(Notifications::Client::TemplateCollection)
68
- p 'failed test_get_all_templates response is not a Notifications::Client::TemplateCollection'
69
- exit 1
65
+ raise 'failed test_get_all_templates response is not a Notifications::Client::TemplateCollection'
70
66
  end
71
67
  unless response.collection.length >= 1
72
- p 'failed test_get_all_templates, expected at least 2 templates returned.'
73
- exit 1
68
+ raise 'failed test_get_all_templates, expected at least 1 template to be returned.'
74
69
  end
75
70
  test_template_response(response.collection[0], 'sms', 'test_get_all_templates')
76
71
  end
@@ -82,12 +77,10 @@ end
82
77
 
83
78
  def test_template_response(response, template_type, test_method)
84
79
  unless response.is_a?(Notifications::Client::Template)
85
- p 'failed test_get_template_by_id response is not a Notifications::Client::Template'
86
- exit 1
80
+ raise 'failed test_get_template_by_id response is not a Notifications::Client::Template'
87
81
  end
88
82
  unless response.id.is_a?(String)
89
- p 'failed template id is not a String'
90
- exit 1
83
+ raise 'failed template id is not a String'
91
84
  end
92
85
 
93
86
  field_should_not_be_nil(
@@ -104,12 +97,10 @@ end
104
97
 
105
98
  def test_template_preview(response)
106
99
  unless response.is_a?(Notifications::Client::TemplatePreview)
107
- p 'failed test_generate_template_preview response is not a Notifications::Client::TemplatePreview'
108
- exit 1
100
+ raise 'failed test_generate_template_preview response is not a Notifications::Client::TemplatePreview'
109
101
  end
110
102
  unless response.id.is_a?(String)
111
- p 'failed template id is not a String'
112
- exit 1
103
+ raise 'failed template id is not a String'
113
104
  end
114
105
  field_should_not_be_nil(expected_fields_in_template_preview, response, 'generate_template_preview')
115
106
  end
@@ -121,7 +112,8 @@ def test_send_email_endpoint(client)
121
112
  personalisation: { "name" => "some name" },
122
113
  reference: "some reference",
123
114
  email_reply_to_id: ENV['EMAIL_REPLY_TO_ID'],
124
- one_click_unsubscribe_url: "https://www.clovercouncil.gov.uk/unsubscribe?email_address=faye@example.com"
115
+ one_click_unsubscribe_url: "https://www.clovercouncil.gov.uk/unsubscribe?email_address=faye@example.com",
116
+ sanitise_content_for: ["name"]
125
117
  )
126
118
  test_notification_response_data_type(email_resp, 'email')
127
119
  email_resp
@@ -176,19 +168,16 @@ end
176
168
 
177
169
  def test_notification_response_data_type(notification, message_type)
178
170
  unless notification.is_a?(Notifications::Client::ResponseNotification) || (notification.is_a?(Notifications::Client::ResponsePrecompiledLetter) && message_type == "precompiled_letter")
179
- p 'failed ' + message_type + ' response is not a Notifications::Client::ResponseNotification'
180
- exit 1
171
+ raise 'failed ' + message_type + ' response is not a Notifications::Client::ResponseNotification'
181
172
  end
182
173
  unless notification.id.is_a?(String)
183
- p 'failed ' + message_type + 'id is not a String'
184
- exit 1
174
+ raise 'failed ' + message_type + 'id is not a String'
185
175
  end
186
176
 
187
177
  if message_type == 'precompiled_letter'
188
178
  field_should_not_be_nil(expected_fields_in_precompiled_letter_response, notification, 'send_precompiled_letter')
189
179
  if notification.postage != "first"
190
- p "Postage should be set to 'first' for precompiled letter sending test. Right now it is set to #{notification.postage}"
191
- exit 1
180
+ raise "Postage should be set to 'first' for precompiled letter sending test. Right now it is set to #{notification.postage}"
192
181
  end
193
182
  return
194
183
  end
@@ -198,6 +187,10 @@ def test_notification_response_data_type(notification, message_type)
198
187
 
199
188
  if message_type == 'email'
200
189
  hash_key_should_not_be_nil(expected_fields_in_email_content, notification.send('content'), 'send_' + message_type + '.content')
190
+ unless sanitised_content_matches_email_send_api_response?(notification.sanitised_content)
191
+ raise 'contract test failed: sanitised_content should be a JSON object whose keys are placeholder names ' \
192
+ 'and whose values are objects with unsanitised and sanitised (see notifications-api post v2/email response)'
193
+ end
201
194
  elsif message_type == 'sms'
202
195
  hash_key_should_not_be_nil(expected_fields_in_sms_content, notification.send('content'), 'send_' + message_type + '.content')
203
196
  elsif message_type == 'letter'
@@ -206,68 +199,93 @@ def test_notification_response_data_type(notification, message_type)
206
199
  end
207
200
 
208
201
  def test_get_notification_by_id_endpoint(client, id, message_type)
209
- get_notification_response = client.get_notification(id)
202
+ get_notification_response = nil
203
+ 24.times do
204
+ get_notification_response = client.get_notification(id)
205
+ break if get_notification_response&.is_cost_data_ready
206
+ sleep 5
207
+ end
208
+
209
+ raise "cost data didn't become ready in time" unless get_notification_response&.is_cost_data_ready
210
210
 
211
211
  unless get_notification_response.is_a?(Notifications::Client::Notification)
212
- p 'get notification is not a Notifications::Client::Notification for id ' + id
213
- exit 1
212
+ raise 'get notification is not a Notifications::Client::Notification for id ' + id
214
213
  end
215
214
 
216
215
  if message_type == 'email'
217
216
  field_should_not_be_nil(expected_fields_in_email_notification, get_notification_response, 'Notifications::Client::Notification for type email')
218
217
  field_should_be_nil(expected_fields_in_email_notification_that_are_nil, get_notification_response, 'Notifications::Client::Notification for type email')
219
218
  hash_key_should_not_be_nil(expected_fields_in_template, get_notification_response.send('template'), 'Notifications::Client::Notification.template for type email')
219
+ hash_should_be_empty(get_notification_response.send('cost_details'), 'Notifications::Client::Notification.cost_details for type sms')
220
+
220
221
  elsif message_type == 'sms'
221
222
  field_should_not_be_nil(expected_fields_in_sms_notification, get_notification_response, 'Notifications::Client::Notification for type sms')
222
223
  field_should_be_nil(expected_fields_in_sms_notification_that_are_nil, get_notification_response, 'Notifications::Client::Notification for type sms')
223
224
  hash_key_should_not_be_nil(expected_fields_in_template, get_notification_response.send('template'), 'Notifications::Client::Notification.template for type sms')
225
+ hash_key_should_not_be_nil(expected_cost_details_fields, get_notification_response.send('cost_details'), 'Notifications::Client::Notification.cost_details for type sms')
226
+
224
227
  elsif message_type == 'letter'
225
228
  field_should_not_be_nil(expected_fields_in_letter_notification, get_notification_response, 'Notifications::Client::Notification for type letter')
226
229
  field_should_be_nil(expected_fields_in_letter_notification_that_are_nil, get_notification_response, 'Notifications::Client::Notification for type letter')
227
230
  hash_key_should_not_be_nil(expected_fields_in_template, get_notification_response.send('template'), 'Notifications::Client::Notification.template for type letter')
231
+ hash_key_should_not_be_nil(expected_cost_details_fields, get_notification_response.send('cost_details'), 'Notifications::Client::Notification.cost_details for type sms')
232
+
228
233
  elsif message_type == 'precompiled_letter'
229
234
  field_should_not_be_nil(expected_fields_in_precompiled_letter_notification, get_notification_response, 'Notifications::Client::Notification for type precompiled letter')
230
235
  field_should_be_nil(expected_fields_in_precompiled_letter_notification_that_are_nil, get_notification_response, 'Notifications::Client::Notification for type precompiled letter')
231
236
  hash_key_should_not_be_nil(expected_fields_in_template, get_notification_response.send('template'), 'Notifications::Client::Notification.template for type precompiled letter')
237
+ hash_key_should_not_be_nil(expected_cost_details_fields, get_notification_response.send('cost_details'), 'Notifications::Client::Notification.cost_details for type sms')
238
+
232
239
  end
233
240
  end
234
241
 
235
242
  def test_get_pdf_for_letter(client, id)
236
243
  response = nil
237
244
 
238
- # try 15 times with 3 secs sleep between each attempt, to get the PDF
239
- 15.times do
245
+ 24.times do
240
246
  begin
241
247
  response = client.get_pdf_for_letter(id)
242
- rescue Notifications::Client::BadRequestError
243
- sleep(3)
244
- end
245
-
246
- if !response.nil?
247
248
  break
249
+ rescue Notifications::Client::BadRequestError
250
+ sleep(5)
248
251
  end
249
252
  end
250
253
 
251
- unless !response.nil? && response.start_with?("%PDF-")
252
- p "get_pdf_for_letter response for " + id + " is not a PDF: " + response.to_s
253
- exit 1
254
+ raise "pdf didn't become ready in time" if response.nil?
255
+ raise "get_pdf_for_letter response for #{id} is not a PDF: #{response}" unless response.start_with?('%PDF-')
256
+ end
257
+
258
+ # POST /v2/notifications/email merges sanitised_content from the API: a JSON object mapping each changed
259
+ # placeholder to { "unsanitised" => ..., "sanitised" => ... }. It is {} when no placeholders differ.
260
+ def sanitised_content_matches_email_send_api_response?(sanitised_content)
261
+ return false unless sanitised_content.is_a?(Hash)
262
+
263
+ sanitised_content.all? do |placeholder_key, entry|
264
+ placeholder_key.is_a?(String) &&
265
+ entry.is_a?(Hash) &&
266
+ entry.key?('unsanitised') &&
267
+ entry.key?('sanitised')
254
268
  end
255
269
  end
256
270
 
257
271
  def hash_key_should_not_be_nil(fields, obj, method_name)
258
272
  fields.each do |field|
259
273
  if obj.has_value?(:"#{field}")
260
- p 'contract test failed because ' + field + ' should not be nil for ' + method_name + ' response'
261
- exit 1
274
+ raise 'contract test failed because ' + field + ' should not be nil for ' + method_name + ' response'
262
275
  end
263
276
  end
264
277
  end
265
278
 
279
+ def hash_should_be_empty(hash, method_name)
280
+ if !hash.empty?
281
+ raise "contract test failed because #{hash} should be empty for #{method_name} response"
282
+ end
283
+ end
284
+
266
285
  def field_should_not_be_nil(fields, obj, method_name)
267
286
  fields.each do |field|
268
287
  if obj.send(:"#{field}") == nil
269
- p 'contract test failed because ' + field + ' should not be nil for ' + method_name + ' response'
270
- exit 1
288
+ raise 'contract test failed because ' + field + ' should not be nil for ' + method_name + ' response'
271
289
  end
272
290
  end
273
291
  end
@@ -275,8 +293,7 @@ end
275
293
  def field_should_be_nil(fields, obj, method_name)
276
294
  fields.each do |field|
277
295
  if obj.send(:"#{field}") != nil
278
- p 'contract test failed because ' + field + ' should be nil for ' + method_name + ' response'
279
- exit 1
296
+ raise 'contract test failed because ' + field + ' should be nil for ' + method_name + ' response'
280
297
  end
281
298
  end
282
299
  end
@@ -348,7 +365,11 @@ def expected_fields_in_email_notification
348
365
  body
349
366
  subject
350
367
  created_at
351
- one_click_unsubscribe_url)
368
+ one_click_unsubscribe_url
369
+ is_cost_data_ready
370
+ cost_in_pounds
371
+ cost_details
372
+ )
352
373
  end
353
374
 
354
375
  def expected_fields_in_email_notification_that_are_nil
@@ -372,7 +393,10 @@ def expected_fields_in_sms_notification
372
393
  status
373
394
  template
374
395
  body
375
- created_at)
396
+ created_at
397
+ is_cost_data_ready
398
+ cost_in_pounds
399
+ cost_details)
376
400
  end
377
401
 
378
402
  def expected_fields_in_sms_notification_that_are_nil
@@ -404,6 +428,9 @@ def expected_fields_in_letter_notification
404
428
  postcode
405
429
  created_at
406
430
  postage
431
+ is_cost_data_ready
432
+ cost_in_pounds
433
+ cost_details
407
434
  )
408
435
  end
409
436
 
@@ -438,7 +465,6 @@ end
438
465
 
439
466
  def expected_fields_in_precompiled_letter_notification_that_are_nil
440
467
  %w(
441
- completed_at
442
468
  created_by_name
443
469
  email_address
444
470
  line_2
@@ -459,6 +485,14 @@ def expected_fields_in_template
459
485
  uri)
460
486
  end
461
487
 
488
+ def expected_cost_details_fields
489
+ %w(billable_sms_fragments
490
+ international_rate_multiplier
491
+ sms_rate
492
+ billable_sheets_of_paper
493
+ postage)
494
+ end
495
+
462
496
  def expected_fields_in_received_text_response
463
497
  %w(id
464
498
  created_at
@@ -471,8 +505,7 @@ end
471
505
  def test_get_all_notifications(client)
472
506
  notifications = client.get_notifications
473
507
  unless notifications.is_a?(Notifications::Client::NotificationsCollection)
474
- p 'get all notifications is not Notifications::Client::NotificationsCollection'
475
- exit 1
508
+ raise 'get all notifications is not Notifications::Client::NotificationsCollection'
476
509
  end
477
510
  field_should_not_be_nil(expected_fields_for_get_all_notifications, notifications, 'get_notifications')
478
511
  end
@@ -481,26 +514,20 @@ def test_get_received_texts
481
514
  client = Notifications::Client.new(ENV['INBOUND_SMS_QUERY_KEY'], ENV['NOTIFY_API_URL'])
482
515
  response = client.get_received_texts
483
516
  unless response.is_a?(Notifications::Client::ReceivedTextCollection)
484
- p 'failed test_get_received_texts response is not a Notifications::Client::ReceivedTextCollection'
485
- exit 1
517
+ raise 'failed test_get_received_texts response is not a Notifications::Client::ReceivedTextCollection'
486
518
  end
487
519
  unless response.collection.length >= 0
488
- p 'failed test_get_received_texts, expected at least 1 received text returned.'
489
- exit 1
520
+ raise 'failed test_get_received_texts, expected at least 1 received text returned.'
490
521
  end
491
522
  test_received_text_response(response.collection[0], 'test_received_text_response')
492
- test_received_text_response(response.collection[1], 'test_received_text_response')
493
- test_received_text_response(response.collection[2], 'test_received_text_response')
494
523
  end
495
524
 
496
525
  def test_received_text_response(response, test_method)
497
526
  unless response.is_a?(Notifications::Client::ReceivedText)
498
- p 'failed test_get_received_texts response is not a Notifications::Client::ReceivedText'
499
- exit 1
527
+ raise 'failed test_get_received_texts response is not a Notifications::Client::ReceivedText'
500
528
  end
501
529
  unless response.id.is_a?(String)
502
- p 'failed received text id is not a String'
503
- exit 1
530
+ raise 'failed received text id is not a String'
504
531
  end
505
532
  field_should_not_be_nil(expected_fields_in_received_text_response, response, test_method)
506
533
  end
@@ -7,6 +7,7 @@ module Notifications
7
7
  content
8
8
  template
9
9
  uri
10
+ sanitised_content
10
11
  ).freeze
11
12
 
12
13
  attr_reader(*FIELDS)
@@ -49,6 +49,8 @@ module Notifications
49
49
  # id of the sender to be used for an sms notification
50
50
  # @option form_data [String] :one_click_unsubscribe_url
51
51
  # link that end user can click to unsubscribe from the distribution list. We will pass this link in the email headers.
52
+ # @option form_data [Array<String>] :sanitise_content_for
53
+ # email only: optional list of personalisation placeholder keys to sanitise for Markdown before the template is rendered.
52
54
  # @see #perform_request!
53
55
  def post(kind, form_data)
54
56
  request = Net::HTTP::Post.new(
@@ -9,6 +9,6 @@
9
9
 
10
10
  module Notifications
11
11
  class Client
12
- VERSION = "6.2.0".freeze
12
+ VERSION = "6.4.0".freeze
13
13
  end
14
14
  end
@@ -30,7 +30,8 @@ module Notifications
30
30
  end
31
31
 
32
32
  ##
33
- # @see Notifications::Client::Speaker#post
33
+ # @see Notifications::Client::Speaker#post
34
+ # Optional email-only keys are documented on {Notifications::Client::Speaker#post}.
34
35
  # @return [ResponseNotification]
35
36
  def send_email(args)
36
37
  ResponseNotification.new(
@@ -20,11 +20,14 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_runtime_dependency "jwt", ">= 1.5", "< 3"
23
+ spec.add_runtime_dependency "jwt", ">= 1.5", "< 4"
24
24
 
25
- spec.add_development_dependency "bundler", "~> 1.7"
26
25
  spec.add_development_dependency "rake", "~> 13.0"
27
26
  spec.add_development_dependency "rspec", "~> 3.7"
28
27
  spec.add_development_dependency "webmock", "~> 3.4"
29
- spec.add_development_dependency "factory_bot", "~> 6.1", "<6.4.5"
28
+ if RUBY_VERSION < '3.0.0'
29
+ spec.add_development_dependency "factory_bot", "~> 6.1", "< 6.4.5"
30
+ else
31
+ spec.add_development_dependency "factory_bot", "~> 6.1"
32
+ end
30
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: notifications-ruby-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.2.0
4
+ version: 6.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Government Digital Service
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-31 00:00:00.000000000 Z
11
+ date: 2026-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '1.5'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '3'
22
+ version: '4'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,21 +29,7 @@ dependencies:
29
29
  version: '1.5'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '3'
33
- - !ruby/object:Gem::Dependency
34
- name: bundler
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '1.7'
40
- type: :development
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '1.7'
32
+ version: '4'
47
33
  - !ruby/object:Gem::Dependency
48
34
  name: rake
49
35
  requirement: !ruby/object:Gem::Requirement