fcm 1.0.0 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Gemfile +1 -0
  4. data/README.md +70 -16
  5. data/fcm.gemspec +3 -3
  6. data/lib/fcm.rb +115 -58
  7. data/spec/fcm_spec.rb +131 -108
  8. metadata +9 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dea7a47eae801d77518e6a53fee8740b99e0ba0ab62eb35d3bc56545ac44ceab
4
- data.tar.gz: 8cac898bef85b9243f03573da25334298a206c6ed2492b3d18a20eb7d614e556
3
+ metadata.gz: a3378dc24fca1ced3cbda70c31a427261044eaa4b9d0580884e73981a04b1ce3
4
+ data.tar.gz: 6801d93f67ace2faeda17ad8ccf99b363cc6526a99573d8e5938eca7563e78c5
5
5
  SHA512:
6
- metadata.gz: ac7d34e4ebaf6c18dc60e915f7d915575b1e77401458163bd9eb3147277fc15ba42685939cf9d05b22ee585f9025f2dd3b4c40d4116f99778a8c8dbbd3b44b2f
7
- data.tar.gz: 385fd1829ae861daac05bac042c52f141a23f6bed289311723d9f5c07ffbbe2130e1c18da55ad9b59fc8ee1c991d1e0472d8732a9d6392939327f605e4ee25a6
6
+ metadata.gz: 657dd3db45b913993cbd876a918ed69affaefe076ca649722c0c6c61937978b20337f8677398f735e7ff40ea983193d58c420811a2ed90778bbe8efc00f4f033
7
+ data.tar.gz: 662b4fc1b8b04962f957518ebaf86d68ca92d905a989e6baed51d4705af55b83f30388030da66237fec93b7fe2d66c0fdd3b14132915e75d0dcccccf312eb58d
data/.gitignore CHANGED
@@ -48,4 +48,4 @@ specifications
48
48
  Gemfile.lock
49
49
  .rvmrc
50
50
  spec/reports
51
- *.gem
51
+ *.gem
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ gem 'rake'
5
5
  gem 'rspec'
6
6
  gem 'webmock'
7
7
  gem 'ci_reporter_rspec'
8
+ gem 'googleauth'
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Firebase Cloud Messaging (FCM) for Android and iOS
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/fcm.svg)](http://badge.fury.io/rb/fcm) [![Build Status](https://secure.travis-ci.org/spacialdb/fcm.png?branch=master)](http://travis-ci.org/spacialdb/fcm)
3
+ [![Gem Version](https://badge.fury.io/rb/fcm.svg)](http://badge.fury.io/rb/fcm) [![Build Status](https://secure.travis-ci.org/decision-labs/fcm.svg?branch=master)](http://travis-ci.org/decision-labs/fcm)
4
4
 
5
5
  The FCM gem lets your ruby backend send notifications to Android and iOS devices via [
6
6
  Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/).
@@ -19,13 +19,56 @@ gem 'fcm'
19
19
 
20
20
  For Android you will need a device running 2.3 (or newer) that also have the Google Play Store app installed, or an emulator running Android 2.3 with Google APIs. iOS devices are also supported.
21
21
 
22
-
23
- A version of supported Ruby, currently:
22
+ A version of supported Ruby, currently:
24
23
  `ruby >= 2.4`
25
24
 
26
-
27
25
  ## Usage
28
26
 
27
+ ## HTTP v1 API
28
+
29
+ To migrate to HTTP v1 see: https://firebase.google.com/docs/cloud-messaging/migrate-v1
30
+
31
+ ```ruby
32
+ fcm = FCM.new(
33
+ API_TOKEN,
34
+ GOOGLE_APPLICATION_CREDENTIALS_PATH,
35
+ FIREBASE_PROJECT_ID
36
+ )
37
+ message = {
38
+ 'topic': "89023", # OR token if you want to send to a specific device
39
+ # 'token': "000iddqd",
40
+ 'data': {
41
+ payload: {
42
+ data: {
43
+ id: 1
44
+ }
45
+ }.to_json
46
+ },
47
+ 'notification': {
48
+ title: notification.title_th,
49
+ body: notification.body_th,
50
+ },
51
+ 'android': {},
52
+ 'apns': {
53
+ payload: {
54
+ aps: {
55
+ sound: "default",
56
+ category: "#{Time.zone.now.to_i}"
57
+ }
58
+ }
59
+ },
60
+ 'fcm_options': {
61
+ analytics_label: 'Label'
62
+ }
63
+ }
64
+
65
+ fcm.send_v1(message)
66
+ ```
67
+
68
+ ## HTTP Legacy Version
69
+
70
+ To migrate to HTTP v1 see: https://firebase.google.com/docs/cloud-messaging/migrate-v1
71
+
29
72
  For your server to send a message to one or more devices, you must first initialise a new `FCM` class with your Firebase Cloud Messaging server key, and then call the `send` method on this and give it 1 or more (up to 1000) registration tokens as an array of strings. You can also optionally send further [HTTP message parameters](https://firebase.google.com/docs/cloud-messaging/http-server-ref) like `data` or `time_to_live` etc. as a hash via the second optional argument to `send`.
30
73
 
31
74
  Example sending notifications:
@@ -34,14 +77,10 @@ Example sending notifications:
34
77
  require 'fcm'
35
78
 
36
79
  fcm = FCM.new("my_server_key")
37
- # you can set option parameters in here
38
- # - all options are pass to HTTParty method arguments
39
- # - ref: https://github.com/jnunemaker/httparty/blob/master/lib/httparty.rb#L29-L60
40
- # fcm = FCM.new("my_server_key", timeout: 3)
41
80
 
42
81
  registration_ids= ["12", "13"] # an array of one or more client registration tokens
43
82
 
44
- # See https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages for all available options.
83
+ # See https://firebase.google.com/docs/cloud-messaging/http-server-ref for all available options.
45
84
  options = { "notification": {
46
85
  "title": "Portugal vs. Denmark",
47
86
  "body": "5 to 1"
@@ -101,14 +140,14 @@ FCM [topic messaging](https://firebase.google.com/docs/cloud-messaging/topic-mes
101
140
 
102
141
  ```ruby
103
142
  response = fcm.send_with_notification_key("/topics/yourTopic",
104
- data: {message: "This is a FCM Topic Message!"})
143
+ notification: {body: "This is a FCM Topic Message!"})
105
144
  ```
106
145
 
107
146
  Or you can use the helper:
108
147
 
109
148
  ```ruby
110
149
  response = fcm.send_to_topic("yourTopic",
111
- data: {message: "This is a FCM Topic Message!"})
150
+ notification: {body: "This is a FCM Topic Message!"})
112
151
  ```
113
152
 
114
153
  ### Sending to Multiple Topics
@@ -137,8 +176,8 @@ The `send_to_topic_condition` method within this library allows you to specicy a
137
176
  ```ruby
138
177
  response = fcm.send_to_topic_condition(
139
178
  "'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)",
140
- data: {
141
- message: "This is an FCM Topic Message sent to a condition!"
179
+ notification: {
180
+ body: "This is an FCM Topic Message sent to a condition!"
142
181
  }
143
182
  )
144
183
  ```
@@ -171,6 +210,14 @@ The guide to set up an iOS app to get notifications is here: [Setting up a FCM C
171
210
 
172
211
  ## ChangeLog
173
212
 
213
+ ### 1.0.3
214
+
215
+ - Fix overly strict faraday depenecy
216
+
217
+ ### 1.0.2
218
+
219
+ - Bug fix: retrieve notification key" params: https://github.com/spacialdb/fcm/commit/b328a75c11d779a06d0ceda83527e26aa0495774
220
+
174
221
  ### 1.0.0
175
222
 
176
223
  - Bumped supported ruby to `>= 2.4`
@@ -197,8 +244,15 @@ The guide to set up an iOS app to get notifications is here: [Setting up a FCM C
197
244
 
198
245
  - [Contributors](https://github.com/spacialdb/fcm/contributors)
199
246
 
200
- ## Donations
247
+ ## Cutting a release
201
248
 
202
- We accept tips through [Gratipay](https://gratipay.com/spacialdb/).
249
+ Update version in `fcm.gemspec` with `VERSION` and update `README.md` `## ChangeLog` section.
203
250
 
204
- [![Gratipay](https://img.shields.io/gratipay/spacialdb.svg)](https://www.gittip.com/spacialdb/)
251
+ ```bash
252
+ # set the version
253
+ # VERSION="1.0.4"
254
+ gem build fcm.gemspec
255
+ git tag -a v${VERSION} -m "Releasing version v${VERSION}"
256
+ git push origin --tags
257
+ gem push fcm-${VERSION}.gem
258
+ ```
data/fcm.gemspec CHANGED
@@ -3,10 +3,10 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "fcm"
6
- s.version = "1.0.0"
6
+ s.version = "1.0.5"
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Kashif Rasul", "Shoaib Burq"]
9
- s.email = ["kashif@spacialdb.com", "shoaib@spacialdb.com"]
9
+ s.email = ["kashif@decision-labs.com", "shoaib@decision-labs.com"]
10
10
  s.homepage = "https://github.com/spacialdb/fcm"
11
11
  s.summary = %q{Reliably deliver messages and notifications via FCM}
12
12
  s.description = %q{fcm provides ruby bindings to Firebase Cloud Messaging (FCM) a cross-platform messaging solution that lets you reliably deliver messages and notifications at no cost to Android, iOS or Web browsers.}
@@ -19,5 +19,5 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_runtime_dependency('faraday','1.0.0')
22
+ s.add_runtime_dependency('faraday', '~> 1')
23
23
  end
data/lib/fcm.rb CHANGED
@@ -1,24 +1,71 @@
1
- require 'faraday'
2
- require 'cgi'
3
- require 'json'
1
+ require "faraday"
2
+ require "cgi"
3
+ require "json"
4
+ require "googleauth"
4
5
 
5
6
  class FCM
6
- BASE_URI = 'https://fcm.googleapis.com'
7
+ BASE_URI = "https://fcm.googleapis.com"
8
+ BASE_URI_V1 = "https://fcm.googleapis.com/v1/projects/"
7
9
  DEFAULT_TIMEOUT = 30
8
10
  FORMAT = :json
9
11
 
10
12
  # constants
11
- GROUP_NOTIFICATION_BASE_URI = 'https://android.googleapis.com'
12
- INSTANCE_ID_API = 'https://iid.googleapis.com'
13
+ GROUP_NOTIFICATION_BASE_URI = "https://android.googleapis.com"
14
+ INSTANCE_ID_API = "https://iid.googleapis.com"
13
15
  TOPIC_REGEX = /[a-zA-Z0-9\-_.~%]+/
14
16
 
15
- attr_accessor :timeout, :api_key
17
+ attr_accessor :timeout, :api_key, :json_key_path, :project_base_uri
16
18
 
17
- def initialize(api_key, client_options = {})
19
+ def initialize(api_key, json_key_path = "", project_name = "", client_options = {})
18
20
  @api_key = api_key
19
21
  @client_options = client_options
22
+ @json_key_path = json_key_path
23
+ @project_base_uri = BASE_URI_V1 + project_name.to_s
20
24
  end
21
25
 
26
+ # See https://firebase.google.com/docs/cloud-messaging/send-message
27
+ # {
28
+ # "token": "4sdsx",
29
+ # "notification": {
30
+ # "title": "Breaking News",
31
+ # "body": "New news story available."
32
+ # },
33
+ # "data": {
34
+ # "story_id": "story_12345"
35
+ # },
36
+ # "android": {
37
+ # "notification": {
38
+ # "click_action": "TOP_STORY_ACTIVITY",
39
+ # "body": "Check out the Top Story"
40
+ # }
41
+ # },
42
+ # "apns": {
43
+ # "payload": {
44
+ # "aps": {
45
+ # "category" : "NEW_MESSAGE_CATEGORY"
46
+ # }
47
+ # }
48
+ # }
49
+ # }
50
+ # fcm = FCM.new(api_key, json_key_path, project_name)
51
+ # fcm.send(
52
+ # { "token": "4sdsx",, "to" : "notification": {}.. }
53
+ # )
54
+ def send_notification_v1(message)
55
+ return if @project_base_uri.empty?
56
+
57
+ post_body = { 'message': message }
58
+
59
+ response = Faraday.post("#{@project_base_uri}/messages:send") do |req|
60
+ req.headers["Content-Type"] = "application/json"
61
+ req.headers["Authorization"] = "Bearer #{jwt_token}"
62
+ req.body = post_body.to_json
63
+ end
64
+ build_response(response)
65
+ end
66
+
67
+ alias send_v1 send_notification_v1
68
+
22
69
  # See https://developers.google.com/cloud-messaging/http for more details.
23
70
  # { "notification": {
24
71
  # "title": "Portugal vs. Denmark",
@@ -35,72 +82,72 @@ class FCM
35
82
  post_body = build_post_body(registration_ids, options)
36
83
 
37
84
  for_uri(BASE_URI) do |connection|
38
- response = connection.post('/fcm/send', post_body.to_json)
85
+ response = connection.post("/fcm/send", post_body.to_json)
39
86
  build_response(response, registration_ids)
40
87
  end
41
88
  end
89
+
42
90
  alias send send_notification
43
91
 
44
92
  def create_notification_key(key_name, project_id, registration_ids = [])
45
- post_body = build_post_body(registration_ids, operation: 'create',
46
- notification_key_name: key_name)
93
+ post_body = build_post_body(registration_ids, operation: "create",
94
+ notification_key_name: key_name)
47
95
 
48
96
  extra_headers = {
49
- 'project_id' => project_id
97
+ "project_id" => project_id,
50
98
  }
51
99
 
52
100
  for_uri(GROUP_NOTIFICATION_BASE_URI, extra_headers) do |connection|
53
- response = connection.post('/gcm/notification', post_body.to_json)
101
+ response = connection.post("/gcm/notification", post_body.to_json)
54
102
  build_response(response)
55
103
  end
56
104
  end
105
+
57
106
  alias create create_notification_key
58
107
 
59
108
  def add_registration_ids(key_name, project_id, notification_key, registration_ids)
60
- post_body = build_post_body(registration_ids, operation: 'add',
61
- notification_key_name: key_name,
62
- notification_key: notification_key)
109
+ post_body = build_post_body(registration_ids, operation: "add",
110
+ notification_key_name: key_name,
111
+ notification_key: notification_key)
63
112
 
64
113
  extra_headers = {
65
- 'project_id' => project_id
114
+ "project_id" => project_id,
66
115
  }
67
116
 
68
117
  for_uri(GROUP_NOTIFICATION_BASE_URI, extra_headers) do |connection|
69
- response = connection.post('/gcm/notification', post_body.to_json)
118
+ response = connection.post("/gcm/notification", post_body.to_json)
70
119
  build_response(response)
71
120
  end
72
121
  end
122
+
73
123
  alias add add_registration_ids
74
124
 
75
125
  def remove_registration_ids(key_name, project_id, notification_key, registration_ids)
76
- post_body = build_post_body(registration_ids, operation: 'remove',
77
- notification_key_name: key_name,
78
- notification_key: notification_key)
126
+ post_body = build_post_body(registration_ids, operation: "remove",
127
+ notification_key_name: key_name,
128
+ notification_key: notification_key)
79
129
 
80
130
  extra_headers = {
81
- 'project_id' => project_id
131
+ "project_id" => project_id,
82
132
  }
83
133
 
84
134
  for_uri(GROUP_NOTIFICATION_BASE_URI, extra_headers) do |connection|
85
- response = connection.post('/gcm/notification', post_body.to_json)
135
+ response = connection.post("/gcm/notification", post_body.to_json)
86
136
  build_response(response)
87
137
  end
88
138
  end
139
+
89
140
  alias remove remove_registration_ids
90
141
 
91
142
  def recover_notification_key(key_name, project_id)
92
- params = {
93
- query: {
94
- notification_key_name: key_name
95
- }
96
- }
97
-
143
+ params = { notification_key_name: key_name }
144
+
98
145
  extra_headers = {
99
- 'project_id' => project_id
146
+ "project_id" => project_id,
100
147
  }
101
148
 
102
149
  for_uri(GROUP_NOTIFICATION_BASE_URI, extra_headers) do |connection|
103
- response = connection.get('/gcm/notification', params)
150
+ response = connection.get("/gcm/notification", params)
104
151
  build_response(response)
105
152
  end
106
153
  end
@@ -118,11 +165,11 @@ class FCM
118
165
  end
119
166
 
120
167
  def batch_topic_subscription(topic, registration_ids)
121
- manage_topics_relationship(topic, registration_ids, 'Add')
168
+ manage_topics_relationship(topic, registration_ids, "Add")
122
169
  end
123
170
 
124
171
  def batch_topic_unsubscription(topic, registration_ids)
125
- manage_topics_relationship(topic, registration_ids, 'Remove')
172
+ manage_topics_relationship(topic, registration_ids, "Remove")
126
173
  end
127
174
 
128
175
  def manage_topics_relationship(topic, registration_ids, action)
@@ -136,35 +183,35 @@ class FCM
136
183
 
137
184
  def send_to_topic(topic, options = {})
138
185
  if topic.gsub(TOPIC_REGEX, "").length == 0
139
- send_with_notification_key('/topics/' + topic, options)
186
+ send_with_notification_key("/topics/" + topic, options)
140
187
  end
141
188
  end
142
189
 
143
- def get_instance_id_info iid_token, options={}
190
+ def get_instance_id_info(iid_token, options = {})
144
191
  params = {
145
- query: options
192
+ query: options,
146
193
  }
147
-
194
+
148
195
  for_uri(INSTANCE_ID_API) do |connection|
149
- response = connection.get('/iid/info/'+iid_token, params)
196
+ response = connection.get("/iid/info/" + iid_token, params)
150
197
  build_response(response)
151
198
  end
152
199
  end
153
200
 
154
- def subscribe_instance_id_to_topic iid_token, topic_name
201
+ def subscribe_instance_id_to_topic(iid_token, topic_name)
155
202
  batch_subscribe_instance_ids_to_topic([iid_token], topic_name)
156
203
  end
157
204
 
158
- def unsubscribe_instance_id_from_topic iid_token, topic_name
205
+ def unsubscribe_instance_id_from_topic(iid_token, topic_name)
159
206
  batch_unsubscribe_instance_ids_from_topic([iid_token], topic_name)
160
207
  end
161
208
 
162
- def batch_subscribe_instance_ids_to_topic instance_ids, topic_name
163
- manage_topics_relationship(topic_name, instance_ids, 'Add')
209
+ def batch_subscribe_instance_ids_to_topic(instance_ids, topic_name)
210
+ manage_topics_relationship(topic_name, instance_ids, "Add")
164
211
  end
165
212
 
166
- def batch_unsubscribe_instance_ids_from_topic instance_ids, topic_name
167
- manage_topics_relationship(topic_name, instance_ids, 'Remove')
213
+ def batch_unsubscribe_instance_ids_from_topic(instance_ids, topic_name)
214
+ manage_topics_relationship(topic_name, instance_ids, "Remove")
168
215
  end
169
216
 
170
217
  def send_to_topic_condition(condition, options = {})
@@ -178,7 +225,7 @@ class FCM
178
225
 
179
226
  def for_uri(uri, extra_headers = {})
180
227
  connection = ::Faraday.new(:url => uri) do |faraday|
181
- faraday.adapter Faraday.default_adapter
228
+ faraday.adapter Faraday.default_adapter
182
229
  faraday.headers["Content-Type"] = "application/json"
183
230
  faraday.headers["Authorization"] = "key=#{api_key}"
184
231
  extra_headers.each do |key, value|
@@ -198,18 +245,18 @@ class FCM
198
245
  response_hash = { body: body, headers: response.headers, status_code: response.status }
199
246
  case response.status
200
247
  when 200
201
- response_hash[:response] = 'success'
248
+ response_hash[:response] = "success"
202
249
  body = JSON.parse(body) unless body.empty?
203
250
  response_hash[:canonical_ids] = build_canonical_ids(body, registration_ids) unless registration_ids.empty?
204
251
  response_hash[:not_registered_ids] = build_not_registered_ids(body, registration_ids) unless registration_ids.empty?
205
252
  when 400
206
- response_hash[:response] = 'Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields.'
253
+ response_hash[:response] = "Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields."
207
254
  when 401
208
- response_hash[:response] = 'There was an error authenticating the sender account.'
255
+ response_hash[:response] = "There was an error authenticating the sender account."
209
256
  when 503
210
- response_hash[:response] = 'Server is temporarily unavailable.'
257
+ response_hash[:response] = "Server is temporarily unavailable."
211
258
  when 500..599
212
- response_hash[:response] = 'There was an internal error in the FCM server while trying to process the request.'
259
+ response_hash[:response] = "There was an internal error in the FCM server while trying to process the request."
213
260
  end
214
261
  response_hash
215
262
  end
@@ -217,9 +264,9 @@ class FCM
217
264
  def build_canonical_ids(body, registration_ids)
218
265
  canonical_ids = []
219
266
  unless body.empty?
220
- if body['canonical_ids'] > 0
221
- body['results'].each_with_index do |result, index|
222
- canonical_ids << { old: registration_ids[index], new: result['registration_id'] } if has_canonical_id?(result)
267
+ if body["canonical_ids"] > 0
268
+ body["results"].each_with_index do |result, index|
269
+ canonical_ids << { old: registration_ids[index], new: result["registration_id"] } if has_canonical_id?(result)
223
270
  end
224
271
  end
225
272
  end
@@ -229,8 +276,8 @@ class FCM
229
276
  def build_not_registered_ids(body, registration_id)
230
277
  not_registered_ids = []
231
278
  unless body.empty?
232
- if body['failure'] > 0
233
- body['results'].each_with_index do |result, index|
279
+ if body["failure"] > 0
280
+ body["results"].each_with_index do |result, index|
234
281
  not_registered_ids << registration_id[index] if is_not_registered?(result)
235
282
  end
236
283
  end
@@ -240,17 +287,17 @@ class FCM
240
287
 
241
288
  def execute_notification(body)
242
289
  for_uri(BASE_URI) do |connection|
243
- response = connection.post('/fcm/send', body.to_json)
290
+ response = connection.post("/fcm/send", body.to_json)
244
291
  build_response(response)
245
292
  end
246
293
  end
247
294
 
248
295
  def has_canonical_id?(result)
249
- !result['registration_id'].nil?
296
+ !result["registration_id"].nil?
250
297
  end
251
298
 
252
299
  def is_not_registered?(result)
253
- result['error'] == 'NotRegistered'
300
+ result["error"] == "NotRegistered"
254
301
  end
255
302
 
256
303
  def validate_condition?(condition)
@@ -269,4 +316,14 @@ class FCM
269
316
  topics = condition.scan(/(?:^|\S|\s)'([^']*?)'(?:$|\S|\s)/).flatten
270
317
  topics.all? { |topic| topic.gsub(TOPIC_REGEX, "").length == 0 }
271
318
  end
319
+
320
+ def jwt_token
321
+ scope = "https://www.googleapis.com/auth/firebase.messaging"
322
+ authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
323
+ json_key_io: File.open(@json_key_path),
324
+ scope: scope,
325
+ )
326
+ token = authorizer.fetch_access_token!
327
+ token["access_token"]
328
+ end
272
329
  end
data/spec/fcm_spec.rb CHANGED
@@ -1,12 +1,12 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe FCM do
4
4
  let(:send_url) { "#{FCM::BASE_URI}/fcm/send" }
5
5
  let(:group_notification_base_uri) { "#{FCM::GROUP_NOTIFICATION_BASE_URI}/gcm/notification" }
6
- let(:api_key) { 'AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA' }
7
- let(:registration_id) { '42' }
8
- let(:registration_ids) { ['42'] }
9
- let(:key_name) { 'appUser-Chris' }
6
+ let(:api_key) { "AIzaSyB-1uEai2WiUapxCs2Q0GZYzPu7Udno5aA" }
7
+ let(:registration_id) { "42" }
8
+ let(:registration_ids) { ["42"] }
9
+ let(:key_name) { "appUser-Chris" }
10
10
  let(:project_id) { "123456789" } # https://developers.google.com/cloud-messaging/gcm#senderid
11
11
  let(:notification_key) { "APA91bGHXQBB...9QgnYOEURwm0I3lmyqzk2TXQ" }
12
12
  let(:valid_topic) { "TopicA" }
@@ -15,15 +15,19 @@ describe FCM do
15
15
  let(:invalid_condition) { "'TopicA' in topics and some other text ('TopicB' in topics || 'TopicC' in topics)" }
16
16
  let(:invalid_condition_topic) { "'TopicA$' in topics" }
17
17
 
18
- it 'should raise an error if the api key is not provided' do
18
+ it "should raise an error if the api key is not provided" do
19
19
  expect { FCM.new }.to raise_error(ArgumentError)
20
20
  end
21
21
 
22
- it 'should raise error if time_to_live is given' do
22
+ it "should raise error if time_to_live is given" do
23
23
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#ttl
24
24
  end
25
25
 
26
- describe 'sending notification' do
26
+ describe "#send_v1" do
27
+ pending "should send message"
28
+ end
29
+
30
+ describe "sending notification" do
27
31
  let(:valid_request_body) do
28
32
  { registration_ids: registration_ids }
29
33
  end
@@ -32,39 +36,39 @@ describe FCM do
32
36
  end
33
37
  let(:valid_request_headers) do
34
38
  {
35
- 'Content-Type' => 'application/json',
36
- 'Authorization' => "key=#{api_key}"
39
+ "Content-Type" => "application/json",
40
+ "Authorization" => "key=#{api_key}",
37
41
  }
38
42
  end
39
43
 
40
44
  let(:stub_fcm_send_request) do
41
45
  stub_request(:post, send_url).with(
42
46
  body: valid_request_body.to_json,
43
- headers: valid_request_headers
47
+ headers: valid_request_headers,
44
48
  ).to_return(
45
49
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
46
- body: '{}',
50
+ body: "{}",
47
51
  headers: {},
48
- status: 200
52
+ status: 200,
49
53
  )
50
54
  end
51
55
 
52
56
  let(:stub_fcm_send_request_with_string) do
53
57
  stub_request(:post, send_url).with(
54
58
  body: valid_request_body_with_string.to_json,
55
- headers: valid_request_headers
59
+ headers: valid_request_headers,
56
60
  ).to_return(
57
- body: '{}',
61
+ body: "{}",
58
62
  headers: {},
59
- status: 200
63
+ status: 200,
60
64
  )
61
65
  end
62
66
 
63
67
  let(:stub_fcm_send_request_with_basic_auth) do
64
68
  uri = URI.parse(send_url)
65
- uri.user = 'a'
66
- uri.password = 'b'
67
- stub_request(:post, uri.to_s).to_return(body: '{}', headers: {}, status: 200)
69
+ uri.user = "a"
70
+ uri.password = "b"
71
+ stub_request(:post, uri.to_s).to_return(body: "{}", headers: {}, status: 200)
68
72
  end
69
73
 
70
74
  before(:each) do
@@ -73,30 +77,30 @@ describe FCM do
73
77
  stub_fcm_send_request_with_basic_auth
74
78
  end
75
79
 
76
- it 'should send notification using POST to FCM server' do
80
+ it "should send notification using POST to FCM server" do
77
81
  fcm = FCM.new(api_key)
78
- fcm.send(registration_ids).should eq(response: 'success', body: '{}', headers: {}, status_code: 200, canonical_ids: [], not_registered_ids: [])
82
+ fcm.send(registration_ids).should eq(response: "success", body: "{}", headers: {}, status_code: 200, canonical_ids: [], not_registered_ids: [])
79
83
  stub_fcm_send_request.should have_been_made.times(1)
80
84
  end
81
85
 
82
- it 'should send notification using POST to FCM if id provided as string' do
86
+ it "should send notification using POST to FCM if id provided as string" do
83
87
  fcm = FCM.new(api_key)
84
- fcm.send(registration_id).should eq(response: 'success', body: '{}', headers: {}, status_code: 200, canonical_ids: [], not_registered_ids: [])
88
+ fcm.send(registration_id).should eq(response: "success", body: "{}", headers: {}, status_code: 200, canonical_ids: [], not_registered_ids: [])
85
89
  stub_fcm_send_request.should have_been_made.times(1)
86
90
  end
87
91
 
88
- context 'send notification with data' do
92
+ context "send notification with data" do
89
93
  let!(:stub_with_data) do
90
94
  stub_request(:post, send_url)
91
95
  .with(body: '{"registration_ids":["42"],"data":{"score":"5x1","time":"15:10"}}',
92
96
  headers: valid_request_headers)
93
- .to_return(status: 200, body: '', headers: {})
97
+ .to_return(status: 200, body: "", headers: {})
94
98
  end
95
99
  before do
96
100
  end
97
- it 'should send the data in a post request to fcm' do
101
+ it "should send the data in a post request to fcm" do
98
102
  fcm = FCM.new(api_key)
99
- fcm.send(registration_ids, data: { score: '5x1', time: '15:10' })
103
+ fcm.send(registration_ids, data: { score: "5x1", time: "15:10" })
100
104
  stub_with_data.should have_been_requested
101
105
  end
102
106
  end
@@ -106,25 +110,25 @@ describe FCM do
106
110
  stub_request(:post, send_url)
107
111
  .with(body: '{"to":"/topics/TopicA","data":{"score":"5x1","time":"15:10"}}',
108
112
  headers: valid_request_headers)
109
- .to_return(status: 200, body: '', headers: {})
113
+ .to_return(status: 200, body: "", headers: {})
110
114
  end
111
115
  let!(:stub_with_invalid_topic) do
112
116
  stub_request(:post, send_url)
113
117
  .with(body: '{"condition":"/topics/TopicA$","data":{"score":"5x1","time":"15:10"}}',
114
118
  headers: valid_request_headers)
115
- .to_return(status: 200, body: '', headers: {})
119
+ .to_return(status: 200, body: "", headers: {})
116
120
  end
117
121
 
118
122
  describe "#send_to_topic" do
119
- it 'should send the data in a post request to fcm' do
123
+ it "should send the data in a post request to fcm" do
120
124
  fcm = FCM.new(api_key)
121
- fcm.send_to_topic(valid_topic, data: { score: '5x1', time: '15:10' })
125
+ fcm.send_to_topic(valid_topic, data: { score: "5x1", time: "15:10" })
122
126
  stub_with_valid_topic.should have_been_requested
123
127
  end
124
128
 
125
- it 'should not send to invalid topics' do
129
+ it "should not send to invalid topics" do
126
130
  fcm = FCM.new(api_key)
127
- fcm.send_to_topic(invalid_topic, data: { score: '5x1', time: '15:10' })
131
+ fcm.send_to_topic(invalid_topic, data: { score: "5x1", time: "15:10" })
128
132
  stub_with_invalid_topic.should_not have_been_requested
129
133
  end
130
134
  end
@@ -135,143 +139,143 @@ describe FCM do
135
139
  stub_request(:post, send_url)
136
140
  .with(body: '{"condition":"\'TopicA\' in topics && (\'TopicB\' in topics || \'TopicC\' in topics)","data":{"score":"5x1","time":"15:10"}}',
137
141
  headers: valid_request_headers)
138
- .to_return(status: 200, body: '', headers: {})
142
+ .to_return(status: 200, body: "", headers: {})
139
143
  end
140
144
  let!(:stub_with_invalid_condition) do
141
145
  stub_request(:post, send_url)
142
146
  .with(body: '{"condition":"\'TopicA\' in topics and some other text (\'TopicB\' in topics || \'TopicC\' in topics)","data":{"score":"5x1","time":"15:10"}}',
143
147
  headers: valid_request_headers)
144
- .to_return(status: 200, body: '', headers: {})
148
+ .to_return(status: 200, body: "", headers: {})
145
149
  end
146
150
  let!(:stub_with_invalid_condition_topic) do
147
151
  stub_request(:post, send_url)
148
152
  .with(body: '{"condition":"\'TopicA$\' in topics","data":{"score":"5x1","time":"15:10"}}',
149
153
  headers: valid_request_headers)
150
- .to_return(status: 200, body: '', headers: {})
154
+ .to_return(status: 200, body: "", headers: {})
151
155
  end
152
156
 
153
157
  describe "#send_to_topic_condition" do
154
- it 'should send the data in a post request to fcm' do
158
+ it "should send the data in a post request to fcm" do
155
159
  fcm = FCM.new(api_key)
156
- fcm.send_to_topic_condition(valid_condition, data: { score: '5x1', time: '15:10' })
160
+ fcm.send_to_topic_condition(valid_condition, data: { score: "5x1", time: "15:10" })
157
161
  stub_with_valid_condition.should have_been_requested
158
162
  end
159
163
 
160
- it 'should not send to invalid conditions' do
164
+ it "should not send to invalid conditions" do
161
165
  fcm = FCM.new(api_key)
162
- fcm.send_to_topic_condition(invalid_condition, data: { score: '5x1', time: '15:10' })
166
+ fcm.send_to_topic_condition(invalid_condition, data: { score: "5x1", time: "15:10" })
163
167
  stub_with_invalid_condition.should_not have_been_requested
164
168
  end
165
169
 
166
- it 'should not send to invalid topics in a condition' do
170
+ it "should not send to invalid topics in a condition" do
167
171
  fcm = FCM.new(api_key)
168
- fcm.send_to_topic_condition(invalid_condition_topic, data: { score: '5x1', time: '15:10' })
172
+ fcm.send_to_topic_condition(invalid_condition_topic, data: { score: "5x1", time: "15:10" })
169
173
  stub_with_invalid_condition_topic.should_not have_been_requested
170
174
  end
171
175
  end
172
176
  end
173
177
 
174
- context 'when send_notification responds with failure' do
178
+ context "when send_notification responds with failure" do
175
179
  let(:mock_request_attributes) do
176
180
  {
177
181
  body: valid_request_body.to_json,
178
- headers: valid_request_headers
182
+ headers: valid_request_headers,
179
183
  }
180
184
  end
181
185
 
182
186
  subject { FCM.new(api_key) }
183
187
 
184
- context 'on failure code 400' do
188
+ context "on failure code 400" do
185
189
  before do
186
190
  stub_request(:post, send_url).with(
187
191
  mock_request_attributes
188
192
  ).to_return(
189
193
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
190
- body: '{}',
194
+ body: "{}",
191
195
  headers: {},
192
- status: 400
196
+ status: 400,
193
197
  )
194
198
  end
195
- it 'should not send notification due to 400' do
196
- subject.send(registration_ids).should eq(body: '{}',
199
+ it "should not send notification due to 400" do
200
+ subject.send(registration_ids).should eq(body: "{}",
197
201
  headers: {},
198
- response: 'Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields.',
202
+ response: "Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields.",
199
203
  status_code: 400)
200
204
  end
201
205
  end
202
206
 
203
- context 'on failure code 401' do
207
+ context "on failure code 401" do
204
208
  before do
205
209
  stub_request(:post, send_url).with(
206
210
  mock_request_attributes
207
211
  ).to_return(
208
212
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
209
- body: '{}',
213
+ body: "{}",
210
214
  headers: {},
211
- status: 401
215
+ status: 401,
212
216
  )
213
217
  end
214
218
 
215
- it 'should not send notification due to 401' do
216
- subject.send(registration_ids).should eq(body: '{}',
219
+ it "should not send notification due to 401" do
220
+ subject.send(registration_ids).should eq(body: "{}",
217
221
  headers: {},
218
- response: 'There was an error authenticating the sender account.',
222
+ response: "There was an error authenticating the sender account.",
219
223
  status_code: 401)
220
224
  end
221
225
  end
222
226
 
223
- context 'on failure code 503' do
227
+ context "on failure code 503" do
224
228
  before do
225
229
  stub_request(:post, send_url).with(
226
230
  mock_request_attributes
227
231
  ).to_return(
228
232
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
229
- body: '{}',
233
+ body: "{}",
230
234
  headers: {},
231
- status: 503
235
+ status: 503,
232
236
  )
233
237
  end
234
238
 
235
- it 'should not send notification due to 503' do
236
- subject.send(registration_ids).should eq(body: '{}',
239
+ it "should not send notification due to 503" do
240
+ subject.send(registration_ids).should eq(body: "{}",
237
241
  headers: {},
238
- response: 'Server is temporarily unavailable.',
242
+ response: "Server is temporarily unavailable.",
239
243
  status_code: 503)
240
244
  end
241
245
  end
242
246
 
243
- context 'on failure code 5xx' do
247
+ context "on failure code 5xx" do
244
248
  before do
245
249
  stub_request(:post, send_url).with(
246
250
  mock_request_attributes
247
251
  ).to_return(
248
252
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
249
253
  body: '{"body-key" => "Body value"}',
250
- headers: { 'header-key' => 'Header value' },
251
- status: 599
254
+ headers: { "header-key" => "Header value" },
255
+ status: 599,
252
256
  )
253
257
  end
254
258
 
255
- it 'should not send notification due to 599' do
259
+ it "should not send notification due to 599" do
256
260
  subject.send(registration_ids).should eq(body: '{"body-key" => "Body value"}',
257
- headers: { 'header-key' => 'Header value' },
258
- response: 'There was an internal error in the FCM server while trying to process the request.',
261
+ headers: { "header-key" => "Header value" },
262
+ response: "There was an internal error in the FCM server while trying to process the request.",
259
263
  status_code: 599)
260
264
  end
261
265
  end
262
266
  end
263
267
 
264
- context 'when send_notification responds canonical_ids' do
268
+ context "when send_notification responds canonical_ids" do
265
269
  let(:mock_request_attributes) do
266
270
  {
267
271
  body: valid_request_body.to_json,
268
- headers: valid_request_headers
272
+ headers: valid_request_headers,
269
273
  }
270
274
  end
271
275
 
272
276
  let(:valid_response_body_with_canonical_ids) do
273
277
  {
274
- failure: 0, canonical_ids: 1, results: [{ registration_id: '43', message_id: '0:1385025861956342%572c22801bb3' }]
278
+ failure: 0, canonical_ids: 1, results: [{ registration_id: "43", message_id: "0:1385025861956342%572c22801bb3" }],
275
279
  }
276
280
  end
277
281
 
@@ -284,35 +288,35 @@ describe FCM do
284
288
  # ref: https://firebase.google.com/docs/cloud-messaging/http-server-ref#interpret-downstream
285
289
  body: valid_response_body_with_canonical_ids.to_json,
286
290
  headers: {},
287
- status: 200
291
+ status: 200,
288
292
  )
289
293
  end
290
294
 
291
- it 'should contain canonical_ids' do
295
+ it "should contain canonical_ids" do
292
296
  response = subject.send(registration_ids)
293
297
 
294
298
  response.should eq(headers: {},
295
- canonical_ids: [{ old: '42', new: '43' }],
299
+ canonical_ids: [{ old: "42", new: "43" }],
296
300
  not_registered_ids: [],
297
301
  status_code: 200,
298
- response: 'success',
302
+ response: "success",
299
303
  body: '{"failure":0,"canonical_ids":1,"results":[{"registration_id":"43","message_id":"0:1385025861956342%572c22801bb3"}]}')
300
304
  end
301
305
  end
302
306
 
303
- context 'when send_notification responds with NotRegistered' do
307
+ context "when send_notification responds with NotRegistered" do
304
308
  subject { FCM.new(api_key) }
305
309
 
306
310
  let(:mock_request_attributes) do
307
311
  {
308
312
  body: valid_request_body.to_json,
309
- headers: valid_request_headers
313
+ headers: valid_request_headers,
310
314
  }
311
315
  end
312
316
 
313
317
  let(:valid_response_body_with_not_registered_ids) do
314
318
  {
315
- canonical_ids: 0, failure: 1, results: [{ error: 'NotRegistered' }]
319
+ canonical_ids: 0, failure: 1, results: [{ error: "NotRegistered" }],
316
320
  }
317
321
  end
318
322
 
@@ -322,31 +326,31 @@ describe FCM do
322
326
  ).to_return(
323
327
  body: valid_response_body_with_not_registered_ids.to_json,
324
328
  headers: {},
325
- status: 200
329
+ status: 200,
326
330
  )
327
331
  end
328
332
 
329
- it 'should contain not_registered_ids' do
333
+ it "should contain not_registered_ids" do
330
334
  response = subject.send(registration_ids)
331
335
  response.should eq(
332
336
  headers: {},
333
337
  canonical_ids: [],
334
338
  not_registered_ids: registration_ids,
335
339
  status_code: 200,
336
- response: 'success',
337
- body: '{"canonical_ids":0,"failure":1,"results":[{"error":"NotRegistered"}]}'
340
+ response: "success",
341
+ body: '{"canonical_ids":0,"failure":1,"results":[{"error":"NotRegistered"}]}',
338
342
  )
339
343
  end
340
344
  end
341
345
  end
342
346
 
343
- describe 'sending group notifications' do
347
+ describe "sending group notifications" do
344
348
  # TODO: refactor to should_behave_like
345
349
  let(:valid_request_headers) do
346
350
  {
347
351
  "Authorization" => "key=#{api_key}",
348
- "Content-Type" => 'application/json',
349
- "Project-Id" => project_id
352
+ "Content-Type" => "application/json",
353
+ "Project-Id" => project_id,
350
354
  }
351
355
  end
352
356
  let(:valid_response_body) do
@@ -357,24 +361,24 @@ describe FCM do
357
361
  {
358
362
  registration_ids: registration_ids,
359
363
  operation: "create",
360
- notification_key_name: key_name
364
+ notification_key_name: key_name,
361
365
  }
362
366
  end
363
367
 
364
368
  subject { FCM.new(api_key) }
365
369
 
366
370
  # ref: https://firebase.google.com/docs/cloud-messaging/notifications#managing-device-groups-on-the-app-server
367
- context 'create' do
371
+ context "create" do
368
372
  let(:valid_request_body) do
369
373
  default_valid_request_body.merge({
370
- operation: "create"
374
+ operation: "create",
371
375
  })
372
376
  end
373
377
 
374
378
  let(:mock_request_attributes) do
375
379
  {
376
380
  body: valid_request_body.to_json,
377
- headers: valid_request_headers
381
+ headers: valid_request_headers,
378
382
  }
379
383
  end
380
384
 
@@ -384,33 +388,33 @@ describe FCM do
384
388
  ).to_return(
385
389
  body: valid_response_body.to_json,
386
390
  headers: {},
387
- status: 200
391
+ status: 200,
388
392
  )
389
393
  end
390
394
 
391
- it 'should send a post request' do
395
+ it "should send a post request" do
392
396
  response = subject.create(key_name, project_id, registration_ids)
393
397
  response.should eq(
394
398
  headers: {},
395
399
  status_code: 200,
396
- response: 'success',
397
- body: valid_response_body.to_json
400
+ response: "success",
401
+ body: valid_response_body.to_json,
398
402
  )
399
403
  end
400
404
  end # create context
401
405
 
402
- context 'add' do
406
+ context "add" do
403
407
  let(:valid_request_body) do
404
408
  default_valid_request_body.merge({
405
409
  operation: "add",
406
- notification_key: notification_key
410
+ notification_key: notification_key,
407
411
  })
408
412
  end
409
413
 
410
414
  let(:mock_request_attributes) do
411
415
  {
412
416
  body: valid_request_body.to_json,
413
- headers: valid_request_headers
417
+ headers: valid_request_headers,
414
418
  }
415
419
  end
416
420
 
@@ -420,33 +424,33 @@ describe FCM do
420
424
  ).to_return(
421
425
  body: valid_response_body.to_json,
422
426
  headers: {},
423
- status: 200
427
+ status: 200,
424
428
  )
425
429
  end
426
430
 
427
- it 'should send a post request' do
431
+ it "should send a post request" do
428
432
  response = subject.add(key_name, project_id, notification_key, registration_ids)
429
433
  response.should eq(
430
434
  headers: {},
431
435
  status_code: 200,
432
- response: 'success',
433
- body: valid_response_body.to_json
436
+ response: "success",
437
+ body: valid_response_body.to_json,
434
438
  )
435
439
  end
436
440
  end # add context
437
441
 
438
- context 'remove' do
442
+ context "remove" do
439
443
  let(:valid_request_body) do
440
444
  default_valid_request_body.merge({
441
445
  operation: "remove",
442
- notification_key: notification_key
446
+ notification_key: notification_key,
443
447
  })
444
448
  end
445
449
 
446
450
  let(:mock_request_attributes) do
447
451
  {
448
452
  body: valid_request_body.to_json,
449
- headers: valid_request_headers
453
+ headers: valid_request_headers,
450
454
  }
451
455
  end
452
456
 
@@ -456,23 +460,42 @@ describe FCM do
456
460
  ).to_return(
457
461
  body: valid_response_body.to_json,
458
462
  headers: {},
459
- status: 200
463
+ status: 200,
460
464
  )
461
465
  end
462
466
 
463
- it 'should send a post request' do
467
+ it "should send a post request" do
464
468
  response = subject.remove(key_name, project_id, notification_key, registration_ids)
465
469
  response.should eq(
466
470
  headers: {},
467
471
  status_code: 200,
468
- response: 'success',
469
- body: valid_response_body.to_json
472
+ response: "success",
473
+ body: valid_response_body.to_json,
470
474
  )
471
475
  end
472
476
  end # remove context
473
477
  end
474
478
 
475
- describe 'subscribing to a topic' do
479
+ describe "#recover_notification_key" do
480
+ it "sends a 'retrieve notification key' request" do
481
+ uri = "#{FCM::GROUP_NOTIFICATION_BASE_URI}/gcm/notification"
482
+ endpoint = stub_request(:get, uri).with(
483
+ headers: {
484
+ "Content-Type" => "application/json",
485
+ "Authorization" => "key=TEST_SERVER_KEY",
486
+ "project_id" => "TEST_PROJECT_ID",
487
+ },
488
+ query: { notification_key_name: "TEST_KEY_NAME" },
489
+ )
490
+ client = FCM.new("TEST_SERVER_KEY")
491
+
492
+ client.recover_notification_key("TEST_KEY_NAME", "TEST_PROJECT_ID")
493
+
494
+ expect(endpoint).to have_been_requested
495
+ end
496
+ end
497
+
498
+ describe "subscribing to a topic" do
476
499
  # TODO
477
500
  end
478
501
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fcm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kashif Rasul
@@ -9,28 +9,28 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-05-16 00:00:00.000000000 Z
12
+ date: 2021-11-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - '='
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: 1.0.0
20
+ version: '1'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '='
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: 1.0.0
27
+ version: '1'
28
28
  description: fcm provides ruby bindings to Firebase Cloud Messaging (FCM) a cross-platform
29
29
  messaging solution that lets you reliably deliver messages and notifications at
30
30
  no cost to Android, iOS or Web browsers.
31
31
  email:
32
- - kashif@spacialdb.com
33
- - shoaib@spacialdb.com
32
+ - kashif@decision-labs.com
33
+ - shoaib@decision-labs.com
34
34
  executables: []
35
35
  extensions: []
36
36
  extra_rdoc_files: []
@@ -65,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
67
  requirements: []
68
- rubygems_version: 3.1.2
68
+ rubygems_version: 3.0.3.1
69
69
  signing_key:
70
70
  specification_version: 4
71
71
  summary: Reliably deliver messages and notifications via FCM