pushpad 1.2.0 → 1.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: 6ca523c6dd48111c5a369889092c80690401158c3f0a14142eb3aee236848062
4
- data.tar.gz: 637c8461863686d6c3c1f9f666a7df74b2053aed5017318f360054f908b83fbe
3
+ metadata.gz: 5a4c81351920b86c10b9926e94eb2aca07a9da9b87ddb3f9d4725459a0993e9c
4
+ data.tar.gz: 657f63d05bcabe69c8a197199ab516452d893450ad221b33a07b28162eb7d9be
5
5
  SHA512:
6
- metadata.gz: 7aaed7d9218f698803bf1adda9ba1459365b49eaba897903b1cab109aeb9bb825ab40b25119a52cc620d515dd7cce36ad8509d34baacb7b74d58028c26327207
7
- data.tar.gz: 0e89ea2619f3c6f39c4a073c95d56faa451e672b86ad3d898e681b53c7ecef59af7e2b7b5fd22b3be957ab02cb7fcc98b6c7e4a84b5a328e853f6c5abc8905ac
6
+ metadata.gz: 6a1e76b5ebfc31b43af6ff17d394c3f4bc88b41f359ab42359e018e5a213bda46c93ca830b3ec663b6b04d915244eda457463305ded412eb56fc448b50cac332
7
+ data.tar.gz: cebbc1b3610953a2064549998acc12348e9bd33c69ef97d20bbfb5f6bdcc4d7c94357cc0296dd4584ec68cb4ac8b3cb883cfa0cd774bff842dc326c1c760abd6
data/README.md CHANGED
@@ -260,6 +260,96 @@ 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
+
317
+ ## Managing projects
318
+
319
+ Projects are usually created manually from the Pushpad dashboard. However you can also create projects from code if you need advanced automation or if you manage [many different domains](https://pushpad.xyz/docs/multiple_domains).
320
+
321
+ ```ruby
322
+ attributes = {
323
+ # required attributes
324
+ sender_id: 123,
325
+ name: "My project",
326
+ website: "https://example.com",
327
+
328
+ # optional configurations
329
+ icon_url: "https://example.com/icon.png",
330
+ badge_url: "https://example.com/badge.png",
331
+ notifications_ttl: 604800,
332
+ notifications_require_interaction: false,
333
+ notifications_silent: false
334
+ }
335
+
336
+ project = Pushpad::Project.create(attributes)
337
+ ```
338
+
339
+ You can also find, update and delete projects:
340
+
341
+ ```ruby
342
+ Pushpad::Project.find_all.each do |p|
343
+ puts "Project #{p.id}: #{p.name}"
344
+ end
345
+
346
+ project = Pushpad::Project.find 123
347
+
348
+ project.update(name: 'The New Project Name')
349
+
350
+ project.delete
351
+ ```
352
+
263
353
  ## License
264
354
 
265
355
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,95 @@
1
+ module Pushpad
2
+ class Project
3
+ class CreateError < RuntimeError
4
+ end
5
+
6
+ class FindError < RuntimeError
7
+ end
8
+
9
+ class UpdateError < RuntimeError
10
+ end
11
+
12
+ class DeleteError < RuntimeError
13
+ end
14
+
15
+ ATTRIBUTES = :id, :sender_id, :name, :website, :icon_url, :badge_url, :notifications_ttl, :notifications_require_interaction, :notifications_silent, :created_at
16
+
17
+ attr_reader *ATTRIBUTES
18
+
19
+ def initialize(options)
20
+ @id = options[:id]
21
+ @sender_id = options[:sender_id]
22
+ @name = options[:name]
23
+ @website = options[:website]
24
+ @icon_url = options[:icon_url]
25
+ @badge_url = options[:badge_url]
26
+ @notifications_ttl = options[:notifications_ttl]
27
+ @notifications_require_interaction = options[:notifications_require_interaction]
28
+ @notifications_silent = options[:notifications_silent]
29
+ @created_at = options[:created_at] && Time.parse(options[:created_at])
30
+ end
31
+
32
+ def self.create(attributes)
33
+ endpoint = "https://pushpad.xyz/api/v1/projects"
34
+ response = Request.post(endpoint, attributes.to_json)
35
+
36
+ unless response.code == "201"
37
+ raise CreateError, "Response #{response.code} #{response.message}: #{response.body}"
38
+ end
39
+
40
+ new(JSON.parse(response.body, symbolize_names: true))
41
+ end
42
+
43
+ def self.find(id)
44
+ response = Request.get("https://pushpad.xyz/api/v1/projects/#{id}")
45
+
46
+ unless response.code == "200"
47
+ raise FindError, "Response #{response.code} #{response.message}: #{response.body}"
48
+ end
49
+
50
+ new(JSON.parse(response.body, symbolize_names: true))
51
+ end
52
+
53
+ def self.find_all
54
+ response = Request.get("https://pushpad.xyz/api/v1/projects")
55
+
56
+ unless response.code == "200"
57
+ raise FindError, "Response #{response.code} #{response.message}: #{response.body}"
58
+ end
59
+
60
+ JSON.parse(response.body, symbolize_names: true).map do |attributes|
61
+ new(attributes)
62
+ end
63
+ end
64
+
65
+ def update(attributes)
66
+ raise "You must set id" unless id
67
+
68
+ endpoint = "https://pushpad.xyz/api/v1/projects/#{id}"
69
+ response = Request.patch(endpoint, attributes.to_json)
70
+
71
+ unless response.code == "200"
72
+ raise UpdateError, "Response #{response.code} #{response.message}: #{response.body}"
73
+ end
74
+
75
+ updated = self.class.new(JSON.parse(response.body, symbolize_names: true))
76
+
77
+ ATTRIBUTES.each do |attr|
78
+ self.instance_variable_set("@#{attr}", updated.instance_variable_get("@#{attr}"))
79
+ end
80
+
81
+ self
82
+ end
83
+
84
+ def delete
85
+ raise "You must set id" unless id
86
+
87
+ response = Request.delete("https://pushpad.xyz/api/v1/projects/#{id}")
88
+
89
+ unless response.code == "202"
90
+ raise DeleteError, "Response #{response.code} #{response.message}: #{response.body}"
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -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/lib/pushpad.rb CHANGED
@@ -3,6 +3,7 @@ require 'openssl'
3
3
  require "pushpad/request"
4
4
  require "pushpad/notification"
5
5
  require "pushpad/subscription"
6
+ require "pushpad/project"
6
7
 
7
8
  module Pushpad
8
9
  @@auth_token = nil
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.4.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."
@@ -0,0 +1,211 @@
1
+ require "spec_helper"
2
+
3
+ module Pushpad
4
+ describe Project do
5
+
6
+ def stub_projects_post(attributes = {})
7
+ stub_request(:post, "https://pushpad.xyz/api/v1/projects").
8
+ with(body: hash_including(attributes)).
9
+ to_return(status: 201, body: attributes.to_json)
10
+ end
11
+
12
+ def stub_failing_projects_post
13
+ stub_request(:post, "https://pushpad.xyz/api/v1/projects").
14
+ to_return(status: 422)
15
+ end
16
+
17
+ def stub_project_get(attributes)
18
+ stub_request(:get, "https://pushpad.xyz/api/v1/projects/#{attributes[:id]}").
19
+ to_return(status: 200, body: attributes.to_json)
20
+ end
21
+
22
+ def stub_failing_project_get(attributes)
23
+ stub_request(:get, "https://pushpad.xyz/api/v1/projects/#{attributes[:id]}").
24
+ to_return(status: 404)
25
+ end
26
+
27
+ def stub_projects_get(list)
28
+ stub_request(:get, "https://pushpad.xyz/api/v1/projects").
29
+ to_return(status: 200, body: list.to_json)
30
+ end
31
+
32
+ def stub_failing_projects_get
33
+ stub_request(:get, "https://pushpad.xyz/api/v1/projects").
34
+ to_return(status: 401)
35
+ end
36
+
37
+ def stub_project_patch(id, attributes)
38
+ stub_request(:patch, "https://pushpad.xyz/api/v1/projects/#{id}").
39
+ with(body: hash_including(attributes)).
40
+ to_return(status: 200, body: attributes.to_json)
41
+ end
42
+
43
+ def stub_failing_project_patch(id)
44
+ stub_request(:patch, "https://pushpad.xyz/api/v1/projects/#{id}").
45
+ to_return(status: 422)
46
+ end
47
+
48
+ def stub_project_delete(id)
49
+ stub_request(:delete, "https://pushpad.xyz/api/v1/projects/#{id}").
50
+ to_return(status: 202)
51
+ end
52
+
53
+ def stub_failing_project_delete(id)
54
+ stub_request(:delete, "https://pushpad.xyz/api/v1/projects/#{id}").
55
+ to_return(status: 403)
56
+ end
57
+
58
+ describe ".create" do
59
+ it "creates a new project with the given attributes and returns it" do
60
+ attributes = {
61
+ sender_id: 123,
62
+ name: "My project",
63
+ website: "https://example.com"
64
+ }
65
+ stub = stub_projects_post(attributes)
66
+
67
+ project = Project.create(attributes)
68
+ expect(project).to have_attributes(attributes)
69
+
70
+ expect(stub).to have_been_requested
71
+ end
72
+
73
+ it "fails with CreateError if response status code is not 201" do
74
+ attributes = { name: "My project" }
75
+ stub_failing_projects_post
76
+
77
+ expect {
78
+ Project.create(attributes)
79
+ }.to raise_error(Project::CreateError)
80
+ end
81
+ end
82
+
83
+ describe ".find" do
84
+ it "returns project with attributes from json response" do
85
+ attributes = {
86
+ id: 361,
87
+ sender_id: 123,
88
+ name: "Example Project",
89
+ website: "https://example.com",
90
+ icon_url: "https://example.com/icon.png",
91
+ badge_url: "https://example.com/badge.png",
92
+ notifications_ttl: 604800,
93
+ notifications_require_interaction: false,
94
+ notifications_silent: false,
95
+ created_at: "2016-07-06T10:58:39.143Z"
96
+ }
97
+ stub_project_get(attributes)
98
+
99
+ project = Project.find(361)
100
+
101
+ attributes.delete(:created_at)
102
+ expect(project).to have_attributes(attributes)
103
+ expect(project.created_at.utc.to_s).to eq(Time.utc(2016, 7, 6, 10, 58, 39.143).to_s)
104
+ end
105
+
106
+ it "fails with FindError if response status code is not 200" do
107
+ attributes = { id: 362 }
108
+ stub_failing_project_get(attributes)
109
+
110
+ expect {
111
+ Project.find(362)
112
+ }.to raise_error(Project::FindError)
113
+ end
114
+ end
115
+
116
+ describe ".find_all" do
117
+ it "returns projects with attributes from json response" do
118
+ attributes = {
119
+ id: 361,
120
+ sender_id: 123,
121
+ name: "Example Project",
122
+ website: "https://example.com",
123
+ icon_url: "https://example.com/icon.png",
124
+ badge_url: "https://example.com/badge.png",
125
+ notifications_ttl: 604800,
126
+ notifications_require_interaction: false,
127
+ notifications_silent: false,
128
+ created_at: "2016-07-06T10:58:39.143Z"
129
+ }
130
+ stub_projects_get([attributes])
131
+
132
+ projects = Project.find_all
133
+
134
+ attributes.delete(:created_at)
135
+ expect(projects[0]).to have_attributes(attributes)
136
+ expect(projects[0].created_at.utc.to_s).to eq(Time.utc(2016, 7, 6, 10, 58, 39.143).to_s)
137
+ end
138
+
139
+ it "fails with FindError if response status code is not 200" do
140
+ stub_failing_projects_get
141
+
142
+ expect {
143
+ Project.find_all
144
+ }.to raise_error(Project::FindError)
145
+ end
146
+
147
+ it "works properly when there are no results" do
148
+ stub_projects_get([])
149
+
150
+ projects = Project.find_all
151
+
152
+ expect(projects).to eq([])
153
+ end
154
+ end
155
+
156
+ describe "#update" do
157
+ it "updates a project with the given attributes and returns it" do
158
+ attributes = {
159
+ name: "The New Project Name"
160
+ }
161
+ stub = stub_project_patch(5, attributes)
162
+
163
+ project = Project.new(id: 5)
164
+ project.update attributes
165
+ expect(project).to have_attributes(attributes)
166
+
167
+ expect(stub).to have_been_requested
168
+ end
169
+
170
+ it "fails with UpdateError if response status code is not 200" do
171
+ attributes = { name: "" }
172
+ stub_failing_project_patch(5)
173
+
174
+ project = Project.new(id: 5)
175
+
176
+ expect {
177
+ project.update attributes
178
+ }.to raise_error(Project::UpdateError)
179
+ end
180
+
181
+ it "fails with helpful error message when id is missing" do
182
+ expect {
183
+ Project.new(id: nil).update({})
184
+ }.to raise_error(/must set id/)
185
+ end
186
+ end
187
+
188
+ describe "#delete" do
189
+ it "deletes a project" do
190
+ stub = stub_project_delete(5)
191
+
192
+ project = Project.new(id: 5)
193
+ res = project.delete
194
+ expect(res).to be_nil
195
+
196
+ expect(stub).to have_been_requested
197
+ end
198
+
199
+ it "fails with DeleteError if response status code is not 202" do
200
+ stub_failing_project_delete(5)
201
+
202
+ project = Project.new(id: 5)
203
+
204
+ expect {
205
+ project.delete
206
+ }.to raise_error(Project::DeleteError)
207
+ end
208
+ end
209
+
210
+ end
211
+ end
@@ -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.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pushpad
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-18 00:00:00.000000000 Z
11
+ date: 2024-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- description:
41
+ description:
42
42
  email:
43
43
  - support@pushpad.xyz
44
44
  executables: []
@@ -54,10 +54,12 @@ files:
54
54
  - Rakefile
55
55
  - lib/pushpad.rb
56
56
  - lib/pushpad/notification.rb
57
+ - lib/pushpad/project.rb
57
58
  - lib/pushpad/request.rb
58
59
  - lib/pushpad/subscription.rb
59
60
  - pushpad.gemspec
60
61
  - spec/pushpad/notification_spec.rb
62
+ - spec/pushpad/project_spec.rb
61
63
  - spec/pushpad/request_spec.rb
62
64
  - spec/pushpad/subscription_spec.rb
63
65
  - spec/pushpad_spec.rb
@@ -66,7 +68,7 @@ homepage: https://pushpad.xyz
66
68
  licenses:
67
69
  - MIT
68
70
  metadata: {}
69
- post_install_message:
71
+ post_install_message:
70
72
  rdoc_options: []
71
73
  require_paths:
72
74
  - lib
@@ -81,13 +83,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
83
  - !ruby/object:Gem::Version
82
84
  version: '0'
83
85
  requirements: []
84
- rubygems_version: 3.0.3.1
85
- signing_key:
86
+ rubygems_version: 3.5.16
87
+ signing_key:
86
88
  specification_version: 4
87
89
  summary: Web push notifications for Chrome, Firefox, Opera, Edge and Safari using
88
90
  Pushpad.
89
91
  test_files:
90
92
  - spec/pushpad/notification_spec.rb
93
+ - spec/pushpad/project_spec.rb
91
94
  - spec/pushpad/request_spec.rb
92
95
  - spec/pushpad/subscription_spec.rb
93
96
  - spec/pushpad_spec.rb