pushpad 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ca523c6dd48111c5a369889092c80690401158c3f0a14142eb3aee236848062
4
- data.tar.gz: 637c8461863686d6c3c1f9f666a7df74b2053aed5017318f360054f908b83fbe
3
+ metadata.gz: 328e31a9039907c359c8a9eeb0e560a71027163f2fbc05d4f899ef94dd21fad2
4
+ data.tar.gz: 334cc8c408c1b8c0c7a2bb9dcd982919c4f223cd36076869691d251ce932fdfb
5
5
  SHA512:
6
- metadata.gz: 7aaed7d9218f698803bf1adda9ba1459365b49eaba897903b1cab109aeb9bb825ab40b25119a52cc620d515dd7cce36ad8509d34baacb7b74d58028c26327207
7
- data.tar.gz: 0e89ea2619f3c6f39c4a073c95d56faa451e672b86ad3d898e681b53c7ecef59af7e2b7b5fd22b3be957ab02cb7fcc98b6c7e4a84b5a328e853f6c5abc8905ac
6
+ metadata.gz: 1126ed9f343b6e9b57dfae141dd8a7c46c2234c459f10d906b3051e8932699851d986f53d8721ae32b3fa8bdb654011e594334d3c699dfaf74f80ee2bf072f6c
7
+ data.tar.gz: 9e0cdb926a5b27b306c74dfd24c556447001087eb6d62fb00d24d462bcbcf160920e39abf932c7bfac3bcb51f281367a074bd7121ce70e2bb628a4d24b7041fd
data/README.md CHANGED
@@ -260,6 +260,60 @@ to get the full list in multiple requests.
260
260
  subscriptions = Pushpad::Subscription.find_all(project_id: 5, page: 2)
261
261
  ```
262
262
 
263
+ You can also retrieve the data of a specific subscription if you already know its id:
264
+
265
+ ```ruby
266
+ Pushpad::Subscription.find 123
267
+ Pushpad::Subscription.find 123, project_id: 456
268
+ ```
269
+
270
+ ## Updating push subscription data
271
+
272
+ Usually you add data, like user IDs and tags, to the push subscriptions using the [JavaScript SDK](https://pushpad.xyz/docs/javascript_sdk_reference) in the frontend.
273
+
274
+ However you can also update the subscription data from your server:
275
+
276
+ ```ruby
277
+ Pushpad::Subscription.find_all(uids: ['user1']).each do |subscription|
278
+ # update the user ID associated to the push subscription
279
+ subscription.update uid: 'myuser1'
280
+
281
+ # update the tags associated to the push subscription
282
+ tags = subscription.tags
283
+ tags << 'another_tag'
284
+ subscription.update tags: tags
285
+ end
286
+ ```
287
+
288
+ ## Importing push subscriptions
289
+
290
+ If you need to [import](https://pushpad.xyz/docs/import) some existing push subscriptions (from another service to Pushpad, or from your backups) or if you simply need to create some test data, you can use this method:
291
+
292
+ ```ruby
293
+ attributes = {
294
+ endpoint: "https://example.com/push/f7Q1Eyf7EyfAb1",
295
+ p256dh: "BCQVDTlYWdl05lal3lG5SKr3VxTrEWpZErbkxWrzknHrIKFwihDoZpc_2sH6Sh08h-CacUYI-H8gW4jH-uMYZQ4=",
296
+ auth: "cdKMlhgVeSPzCXZ3V7FtgQ==",
297
+ uid: "exampleUid",
298
+ tags: ["exampleTag1", "exampleTag2"]
299
+ }
300
+
301
+ subscription = Pushpad::Subscription.create(attributes, project_id: 5)
302
+ ```
303
+
304
+ Please note that this is not the standard way to collect subscriptions on Pushpad: usually you subscribe the users to the notifications using the [JavaScript SDK](https://pushpad.xyz/docs/javascript_sdk_reference) in the frontend.
305
+
306
+ ## Deleting push subscriptions
307
+
308
+ Usually you unsubscribe a user from push notifications using the [JavaScript SDK](https://pushpad.xyz/docs/javascript_sdk_reference) in the frontend (recommended).
309
+
310
+ However you can also delete the subscriptions using this library. Be careful, the subscriptions are permanently deleted!
311
+
312
+ ```ruby
313
+ subscription = Pushpad::Subscription.find 123
314
+ subscription.delete
315
+ ```
316
+
263
317
  ## License
264
318
 
265
319
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -19,6 +19,12 @@ module Pushpad
19
19
  end
20
20
  end
21
21
 
22
+ def patch(endpoint, body, options = {})
23
+ perform(Net::HTTP::Patch, endpoint, options) do |request|
24
+ request.body = body
25
+ end
26
+ end
27
+
22
28
  def delete(endpoint, options = {})
23
29
  perform(Net::HTTP::Delete, endpoint, options)
24
30
  end
@@ -1,11 +1,20 @@
1
1
  module Pushpad
2
2
  class Subscription
3
+ class CreateError < RuntimeError
4
+ end
5
+
6
+ class UpdateError < RuntimeError
7
+ end
8
+
3
9
  class CountError < RuntimeError
4
10
  end
5
11
 
6
12
  class FindError < RuntimeError
7
13
  end
8
14
 
15
+ class DeleteError < RuntimeError
16
+ end
17
+
9
18
  attr_reader :id, :endpoint, :p256dh, :auth, :uid, :tags, :last_click_at, :created_at
10
19
 
11
20
  def initialize(options)
@@ -18,6 +27,20 @@ module Pushpad
18
27
  @last_click_at = options[:last_click_at] && Time.parse(options[:last_click_at])
19
28
  @created_at = options[:created_at] && Time.parse(options[:created_at])
20
29
  end
30
+
31
+ def self.create(attributes, options = {})
32
+ project_id = options[:project_id] || Pushpad.project_id
33
+ raise "You must set project_id" unless project_id
34
+
35
+ endpoint = "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions"
36
+ response = Request.post(endpoint, attributes.to_json)
37
+
38
+ unless response.code == "201"
39
+ raise CreateError, "Response #{response.code} #{response.message}: #{response.body}"
40
+ end
41
+
42
+ new(JSON.parse(response.body, symbolize_names: true))
43
+ end
21
44
 
22
45
  def self.count(options = {})
23
46
  project_id = options[:project_id] || Pushpad.project_id
@@ -33,6 +56,19 @@ module Pushpad
33
56
  response["X-Total-Count"].to_i
34
57
  end
35
58
 
59
+ def self.find(id, options = {})
60
+ project_id = options[:project_id] || Pushpad.project_id
61
+ raise "You must set project_id" unless project_id
62
+
63
+ response = Request.get("https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions/#{id}")
64
+
65
+ unless response.code == "200"
66
+ raise FindError, "Response #{response.code} #{response.message}: #{response.body}"
67
+ end
68
+
69
+ new(JSON.parse(response.body, symbolize_names: true))
70
+ end
71
+
36
72
  def self.find_all(options = {})
37
73
  project_id = options[:project_id] || Pushpad.project_id
38
74
  raise "You must set project_id" unless project_id
@@ -51,6 +87,39 @@ module Pushpad
51
87
  new(attributes)
52
88
  end
53
89
  end
90
+
91
+ def update(attributes, options = {})
92
+ project_id = options[:project_id] || Pushpad.project_id
93
+ raise "You must set project_id" unless project_id
94
+
95
+ raise "You must set id" unless id
96
+
97
+ endpoint = "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions/#{id}"
98
+ response = Request.patch(endpoint, attributes.to_json)
99
+
100
+ unless response.code == "200"
101
+ raise UpdateError, "Response #{response.code} #{response.message}: #{response.body}"
102
+ end
103
+
104
+ attributes = JSON.parse(response.body, symbolize_names: true)
105
+ @uid = attributes[:uid]
106
+ @tags = attributes[:tags]
107
+
108
+ self
109
+ end
110
+
111
+ def delete(options = {})
112
+ project_id = options[:project_id] || Pushpad.project_id
113
+ raise "You must set project_id" unless project_id
114
+
115
+ raise "You must set id" unless id
116
+
117
+ response = Request.delete("https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions/#{id}")
118
+
119
+ unless response.code == "204"
120
+ raise DeleteError, "Response #{response.code} #{response.message}: #{response.body}"
121
+ end
122
+ end
54
123
 
55
124
  private
56
125
 
data/pushpad.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "pushpad"
3
- spec.version = '1.2.0'
3
+ spec.version = '1.3.0'
4
4
  spec.authors = ["Pushpad"]
5
5
  spec.email = ["support@pushpad.xyz"]
6
6
  spec.summary = "Web push notifications for Chrome, Firefox, Opera, Edge and Safari using Pushpad."
@@ -2,6 +2,16 @@ require "spec_helper"
2
2
 
3
3
  module Pushpad
4
4
  describe Subscription do
5
+ def stub_subscription_get(options)
6
+ stub_request(:get, "https://pushpad.xyz/api/v1/projects/#{options[:project_id]}/subscriptions/#{options[:id]}").
7
+ to_return(status: 200, body: options[:attributes].to_json)
8
+ end
9
+
10
+ def stub_failing_subscription_get(options)
11
+ stub_request(:get, "https://pushpad.xyz/api/v1/projects/#{options[:project_id]}/subscriptions/#{options[:id]}").
12
+ to_return(status: 404)
13
+ end
14
+
5
15
  def stub_subscriptions_head(options)
6
16
  stub_request(:head, "https://pushpad.xyz/api/v1/projects/#{options[:project_id]}/subscriptions").
7
17
  with(query: hash_including(options.fetch(:query, {}))).
@@ -24,6 +34,63 @@ module Pushpad
24
34
  stub_request(:get, "https://pushpad.xyz/api/v1/projects/#{options[:project_id]}/subscriptions").
25
35
  to_return(status: 403)
26
36
  end
37
+
38
+ def stub_subscriptions_post(project_id, attributes = {})
39
+ stub_request(:post, "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions").
40
+ with(body: hash_including(attributes)).
41
+ to_return(status: 201, body: attributes.to_json)
42
+ end
43
+
44
+ def stub_failing_subscriptions_post(project_id)
45
+ stub_request(:post, "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions").
46
+ to_return(status: 403)
47
+ end
48
+
49
+ def stub_subscription_patch(project_id, id, attributes = {})
50
+ stub_request(:patch, "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions/#{id}").
51
+ with(body: hash_including(attributes)).
52
+ to_return(status: 200, body: attributes.to_json)
53
+ end
54
+
55
+ def stub_failing_subscription_patch(project_id, id)
56
+ stub_request(:patch, "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions/#{id}").
57
+ to_return(status: 422)
58
+ end
59
+
60
+ def stub_subscription_delete(project_id, id)
61
+ stub_request(:delete, "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions/#{id}").
62
+ to_return(status: 204)
63
+ end
64
+
65
+ def stub_failing_subscription_delete(project_id, id)
66
+ stub_request(:delete, "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions/#{id}").
67
+ to_return(status: 403)
68
+ end
69
+
70
+ describe ".create" do
71
+ it "creates a new subscription with the given attributes and returns it" do
72
+ attributes = {
73
+ endpoint: "https://example.com/push/f7Q1Eyf7EyfAb1",
74
+ p256dh: "BCQVDTlYWdl05lal3lG5SKr3VxTrEWpZErbkxWrzknHrIKFwihDoZpc_2sH6Sh08h-CacUYI-H8gW4jH-uMYZQ4=",
75
+ auth: "cdKMlhgVeSPzCXZ3V7FtgQ==",
76
+ uid: "exampleUid",
77
+ tags: ["exampleTag1", "exampleTag2"]
78
+ }
79
+ stub_subscriptions_post(5, attributes)
80
+
81
+ subscription = Subscription.create(attributes, project_id: 5)
82
+ expect(subscription).to have_attributes(attributes)
83
+ end
84
+
85
+ it "fails with CreateError if response status code is not 201" do
86
+ attributes = { endpoint: "https://example.com/push/123" }
87
+ stub_failing_subscriptions_post(5)
88
+
89
+ expect {
90
+ Subscription.create(attributes, project_id: 5)
91
+ }.to raise_error(Subscription::CreateError)
92
+ end
93
+ end
27
94
 
28
95
  describe ".count" do
29
96
  it "returns value from X-Total-Count header" do
@@ -93,6 +160,46 @@ module Pushpad
93
160
  end
94
161
  end
95
162
 
163
+ describe ".find" do
164
+ it "returns subscription with attributes from json response" do
165
+ attributes = {
166
+ id: 5,
167
+ endpoint: "https://example.com/push/f7Q1Eyf7EyfAb1",
168
+ p256dh: "BCQVDTlYWdl05lal3lG5SKr3VxTrEWpZErbkxWrzknHrIKFwihDoZpc_2sH6Sh08h-CacUYI-H8gW4jH-uMYZQ4=",
169
+ auth: "cdKMlhgVeSPzCXZ3V7FtgQ==",
170
+ uid: "exampleUid",
171
+ tags: ["exampleTag1", "exampleTag2"],
172
+ last_click_at: "2023-11-03T10:30:00.000Z",
173
+ created_at: "2016-09-06T10:47:05.494Z"
174
+ }
175
+ stub_subscription_get(id: 5, project_id: 10, attributes: attributes)
176
+
177
+ subscription = Subscription.find(5, project_id: 10)
178
+
179
+ attributes.delete(:last_click_at)
180
+ attributes.delete(:created_at)
181
+ expect(subscription).to have_attributes(attributes)
182
+ expect(subscription.last_click_at.utc.to_s).to eq(Time.utc(2023, 11, 3, 10, 30, 0.0).to_s)
183
+ expect(subscription.created_at.utc.to_s).to eq(Time.utc(2016, 9, 6, 10, 47, 5.494).to_s)
184
+ end
185
+
186
+ it "fails with FindError if response status code is not 200" do
187
+ stub_failing_subscription_get(id: 5, project_id: 10)
188
+
189
+ expect {
190
+ Subscription.find(5, project_id: 10)
191
+ }.to raise_error(Subscription::FindError)
192
+ end
193
+
194
+ it "fails with helpful error message when project_id is missing" do
195
+ Pushpad.project_id = nil
196
+
197
+ expect {
198
+ Subscription.find 5
199
+ }.to raise_error(/must set project_id/)
200
+ end
201
+ end
202
+
96
203
  describe ".find_all" do
97
204
  it "returns subscriptions of project with attributes from json response" do
98
205
  attributes = {
@@ -197,5 +304,69 @@ module Pushpad
197
304
  end
198
305
  end
199
306
 
307
+ describe "#update" do
308
+ it "updates a subscription with the given attributes and returns it" do
309
+ attributes = {
310
+ uid: "exampleUid",
311
+ tags: ["exampleTag1", "exampleTag2"]
312
+ }
313
+ stub_subscription_patch(5, 123, attributes)
314
+
315
+ subscription = Subscription.new(id: 123)
316
+ subscription.update attributes, project_id: 5
317
+ expect(subscription).to have_attributes(attributes)
318
+ end
319
+
320
+ it "fails with UpdateError if response status code is not 200" do
321
+ attributes = { uid: "exampleUid" }
322
+ stub_failing_subscription_patch(5, 123)
323
+
324
+ subscription = Subscription.new(id: 123)
325
+
326
+ expect {
327
+ subscription.update attributes, project_id: 5
328
+ }.to raise_error(Subscription::UpdateError)
329
+ end
330
+
331
+ it "fails with helpful error message when project_id is missing" do
332
+ Pushpad.project_id = nil
333
+
334
+ expect {
335
+ Subscription.new(id: 123).update({})
336
+ }.to raise_error(/must set project_id/)
337
+ end
338
+
339
+ it "fails with helpful error message when id is missing" do
340
+ Pushpad.project_id = 5
341
+
342
+ expect {
343
+ Subscription.new(id: nil).update({})
344
+ }.to raise_error(/must set id/)
345
+ end
346
+ end
347
+
348
+ describe "#delete" do
349
+ it "deletes a subscription" do
350
+ Pushpad.project_id = 5
351
+ stub_subscription_delete(5, 123)
352
+
353
+ subscription = Subscription.new(id: 123)
354
+
355
+ res = subscription.delete
356
+ expect(res).to be_nil
357
+ end
358
+
359
+ it "fails with DeleteError if response status code is not 204" do
360
+ Pushpad.project_id = 5
361
+ stub_failing_subscription_delete(5, 123)
362
+
363
+ subscription = Subscription.new(id: 123)
364
+
365
+ expect {
366
+ subscription.delete
367
+ }.to raise_error(Subscription::DeleteError)
368
+ end
369
+ end
370
+
200
371
  end
201
372
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pushpad
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pushpad
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-18 00:00:00.000000000 Z
11
+ date: 2024-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec