fcm 1.0.3 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +51 -3
- data/fcm.gemspec +2 -2
- data/lib/fcm.rb +115 -54
- data/spec/fcm_spec.rb +116 -112
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3378dc24fca1ced3cbda70c31a427261044eaa4b9d0580884e73981a04b1ce3
|
4
|
+
data.tar.gz: 6801d93f67ace2faeda17ad8ccf99b363cc6526a99573d8e5938eca7563e78c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 657dd3db45b913993cbd876a918ed69affaefe076ca649722c0c6c61937978b20337f8677398f735e7ff40ea983193d58c420811a2ed90778bbe8efc00f4f033
|
7
|
+
data.tar.gz: 662b4fc1b8b04962f957518ebaf86d68ca92d905a989e6baed51d4705af55b83f30388030da66237fec93b7fe2d66c0fdd3b14132915e75d0dcccccf312eb58d
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -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
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:
|
@@ -166,6 +209,11 @@ You can find a guide to implement an Android Client app to receive notifications
|
|
166
209
|
The guide to set up an iOS app to get notifications is here: [Setting up a FCM Client App on iOS](https://firebase.google.com/docs/cloud-messaging/ios/client).
|
167
210
|
|
168
211
|
## ChangeLog
|
212
|
+
|
213
|
+
### 1.0.3
|
214
|
+
|
215
|
+
- Fix overly strict faraday depenecy
|
216
|
+
|
169
217
|
### 1.0.2
|
170
218
|
|
171
219
|
- Bug fix: retrieve notification key" params: https://github.com/spacialdb/fcm/commit/b328a75c11d779a06d0ceda83527e26aa0495774
|
@@ -202,7 +250,7 @@ Update version in `fcm.gemspec` with `VERSION` and update `README.md` `## Change
|
|
202
250
|
|
203
251
|
```bash
|
204
252
|
# set the version
|
205
|
-
# VERSION="1.0.
|
253
|
+
# VERSION="1.0.4"
|
206
254
|
gem build fcm.gemspec
|
207
255
|
git tag -a v${VERSION} -m "Releasing version v${VERSION}"
|
208
256
|
git push origin --tags
|
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.
|
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@
|
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.}
|
data/lib/fcm.rb
CHANGED
@@ -1,24 +1,71 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "faraday"
|
2
|
+
require "cgi"
|
3
|
+
require "json"
|
4
|
+
require "googleauth"
|
4
5
|
|
5
6
|
class FCM
|
6
|
-
BASE_URI =
|
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 =
|
12
|
-
INSTANCE_ID_API =
|
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,68 +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(
|
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:
|
46
|
-
|
93
|
+
post_body = build_post_body(registration_ids, operation: "create",
|
94
|
+
notification_key_name: key_name)
|
47
95
|
|
48
96
|
extra_headers = {
|
49
|
-
|
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(
|
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:
|
61
|
-
|
62
|
-
|
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
|
-
|
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(
|
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:
|
77
|
-
|
78
|
-
|
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
|
-
|
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(
|
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 = {notification_key_name: key_name}
|
93
|
-
|
143
|
+
params = { notification_key_name: key_name }
|
144
|
+
|
94
145
|
extra_headers = {
|
95
|
-
|
146
|
+
"project_id" => project_id,
|
96
147
|
}
|
97
148
|
|
98
149
|
for_uri(GROUP_NOTIFICATION_BASE_URI, extra_headers) do |connection|
|
99
|
-
response = connection.get(
|
150
|
+
response = connection.get("/gcm/notification", params)
|
100
151
|
build_response(response)
|
101
152
|
end
|
102
153
|
end
|
@@ -114,11 +165,11 @@ class FCM
|
|
114
165
|
end
|
115
166
|
|
116
167
|
def batch_topic_subscription(topic, registration_ids)
|
117
|
-
manage_topics_relationship(topic, registration_ids,
|
168
|
+
manage_topics_relationship(topic, registration_ids, "Add")
|
118
169
|
end
|
119
170
|
|
120
171
|
def batch_topic_unsubscription(topic, registration_ids)
|
121
|
-
manage_topics_relationship(topic, registration_ids,
|
172
|
+
manage_topics_relationship(topic, registration_ids, "Remove")
|
122
173
|
end
|
123
174
|
|
124
175
|
def manage_topics_relationship(topic, registration_ids, action)
|
@@ -132,35 +183,35 @@ class FCM
|
|
132
183
|
|
133
184
|
def send_to_topic(topic, options = {})
|
134
185
|
if topic.gsub(TOPIC_REGEX, "").length == 0
|
135
|
-
send_with_notification_key(
|
186
|
+
send_with_notification_key("/topics/" + topic, options)
|
136
187
|
end
|
137
188
|
end
|
138
189
|
|
139
|
-
def get_instance_id_info
|
190
|
+
def get_instance_id_info(iid_token, options = {})
|
140
191
|
params = {
|
141
|
-
query: options
|
192
|
+
query: options,
|
142
193
|
}
|
143
|
-
|
194
|
+
|
144
195
|
for_uri(INSTANCE_ID_API) do |connection|
|
145
|
-
response = connection.get(
|
196
|
+
response = connection.get("/iid/info/" + iid_token, params)
|
146
197
|
build_response(response)
|
147
198
|
end
|
148
199
|
end
|
149
200
|
|
150
|
-
def subscribe_instance_id_to_topic
|
201
|
+
def subscribe_instance_id_to_topic(iid_token, topic_name)
|
151
202
|
batch_subscribe_instance_ids_to_topic([iid_token], topic_name)
|
152
203
|
end
|
153
204
|
|
154
|
-
def unsubscribe_instance_id_from_topic
|
205
|
+
def unsubscribe_instance_id_from_topic(iid_token, topic_name)
|
155
206
|
batch_unsubscribe_instance_ids_from_topic([iid_token], topic_name)
|
156
207
|
end
|
157
208
|
|
158
|
-
def batch_subscribe_instance_ids_to_topic
|
159
|
-
manage_topics_relationship(topic_name, instance_ids,
|
209
|
+
def batch_subscribe_instance_ids_to_topic(instance_ids, topic_name)
|
210
|
+
manage_topics_relationship(topic_name, instance_ids, "Add")
|
160
211
|
end
|
161
212
|
|
162
|
-
def batch_unsubscribe_instance_ids_from_topic
|
163
|
-
manage_topics_relationship(topic_name, instance_ids,
|
213
|
+
def batch_unsubscribe_instance_ids_from_topic(instance_ids, topic_name)
|
214
|
+
manage_topics_relationship(topic_name, instance_ids, "Remove")
|
164
215
|
end
|
165
216
|
|
166
217
|
def send_to_topic_condition(condition, options = {})
|
@@ -174,7 +225,7 @@ class FCM
|
|
174
225
|
|
175
226
|
def for_uri(uri, extra_headers = {})
|
176
227
|
connection = ::Faraday.new(:url => uri) do |faraday|
|
177
|
-
faraday.adapter
|
228
|
+
faraday.adapter Faraday.default_adapter
|
178
229
|
faraday.headers["Content-Type"] = "application/json"
|
179
230
|
faraday.headers["Authorization"] = "key=#{api_key}"
|
180
231
|
extra_headers.each do |key, value|
|
@@ -194,18 +245,18 @@ class FCM
|
|
194
245
|
response_hash = { body: body, headers: response.headers, status_code: response.status }
|
195
246
|
case response.status
|
196
247
|
when 200
|
197
|
-
response_hash[:response] =
|
248
|
+
response_hash[:response] = "success"
|
198
249
|
body = JSON.parse(body) unless body.empty?
|
199
250
|
response_hash[:canonical_ids] = build_canonical_ids(body, registration_ids) unless registration_ids.empty?
|
200
251
|
response_hash[:not_registered_ids] = build_not_registered_ids(body, registration_ids) unless registration_ids.empty?
|
201
252
|
when 400
|
202
|
-
response_hash[:response] =
|
253
|
+
response_hash[:response] = "Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields."
|
203
254
|
when 401
|
204
|
-
response_hash[:response] =
|
255
|
+
response_hash[:response] = "There was an error authenticating the sender account."
|
205
256
|
when 503
|
206
|
-
response_hash[:response] =
|
257
|
+
response_hash[:response] = "Server is temporarily unavailable."
|
207
258
|
when 500..599
|
208
|
-
response_hash[:response] =
|
259
|
+
response_hash[:response] = "There was an internal error in the FCM server while trying to process the request."
|
209
260
|
end
|
210
261
|
response_hash
|
211
262
|
end
|
@@ -213,9 +264,9 @@ class FCM
|
|
213
264
|
def build_canonical_ids(body, registration_ids)
|
214
265
|
canonical_ids = []
|
215
266
|
unless body.empty?
|
216
|
-
if body[
|
217
|
-
body[
|
218
|
-
canonical_ids << { old: registration_ids[index], new: 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)
|
219
270
|
end
|
220
271
|
end
|
221
272
|
end
|
@@ -225,8 +276,8 @@ class FCM
|
|
225
276
|
def build_not_registered_ids(body, registration_id)
|
226
277
|
not_registered_ids = []
|
227
278
|
unless body.empty?
|
228
|
-
if body[
|
229
|
-
body[
|
279
|
+
if body["failure"] > 0
|
280
|
+
body["results"].each_with_index do |result, index|
|
230
281
|
not_registered_ids << registration_id[index] if is_not_registered?(result)
|
231
282
|
end
|
232
283
|
end
|
@@ -236,17 +287,17 @@ class FCM
|
|
236
287
|
|
237
288
|
def execute_notification(body)
|
238
289
|
for_uri(BASE_URI) do |connection|
|
239
|
-
response = connection.post(
|
290
|
+
response = connection.post("/fcm/send", body.to_json)
|
240
291
|
build_response(response)
|
241
292
|
end
|
242
293
|
end
|
243
294
|
|
244
295
|
def has_canonical_id?(result)
|
245
|
-
!result[
|
296
|
+
!result["registration_id"].nil?
|
246
297
|
end
|
247
298
|
|
248
299
|
def is_not_registered?(result)
|
249
|
-
result[
|
300
|
+
result["error"] == "NotRegistered"
|
250
301
|
end
|
251
302
|
|
252
303
|
def validate_condition?(condition)
|
@@ -265,4 +316,14 @@ class FCM
|
|
265
316
|
topics = condition.scan(/(?:^|\S|\s)'([^']*?)'(?:$|\S|\s)/).flatten
|
266
317
|
topics.all? { |topic| topic.gsub(TOPIC_REGEX, "").length == 0 }
|
267
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
|
268
329
|
end
|
data/spec/fcm_spec.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
require
|
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) {
|
7
|
-
let(:registration_id) {
|
8
|
-
let(:registration_ids) { [
|
9
|
-
let(:key_name) {
|
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
|
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
|
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
|
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
|
-
|
36
|
-
|
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 =
|
66
|
-
uri.password =
|
67
|
-
stub_request(:post, uri.to_s).to_return(body:
|
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
|
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:
|
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
|
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:
|
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
|
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:
|
97
|
+
.to_return(status: 200, body: "", headers: {})
|
94
98
|
end
|
95
99
|
before do
|
96
100
|
end
|
97
|
-
it
|
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:
|
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:
|
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:
|
119
|
+
.to_return(status: 200, body: "", headers: {})
|
116
120
|
end
|
117
121
|
|
118
122
|
describe "#send_to_topic" do
|
119
|
-
it
|
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:
|
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
|
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:
|
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:
|
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:
|
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:
|
154
|
+
.to_return(status: 200, body: "", headers: {})
|
151
155
|
end
|
152
156
|
|
153
157
|
describe "#send_to_topic_condition" do
|
154
|
-
it
|
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:
|
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
|
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:
|
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
|
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:
|
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
|
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
|
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
|
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:
|
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
|
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
|
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:
|
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
|
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
|
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:
|
242
|
+
response: "Server is temporarily unavailable.",
|
239
243
|
status_code: 503)
|
240
244
|
end
|
241
245
|
end
|
242
246
|
|
243
|
-
context
|
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: {
|
251
|
-
status: 599
|
254
|
+
headers: { "header-key" => "Header value" },
|
255
|
+
status: 599,
|
252
256
|
)
|
253
257
|
end
|
254
258
|
|
255
|
-
it
|
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: {
|
258
|
-
response:
|
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
|
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:
|
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
|
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:
|
299
|
+
canonical_ids: [{ old: "42", new: "43" }],
|
296
300
|
not_registered_ids: [],
|
297
301
|
status_code: 200,
|
298
|
-
response:
|
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
|
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:
|
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
|
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:
|
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
|
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" =>
|
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
|
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
|
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:
|
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
|
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
|
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:
|
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
|
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,17 +460,17 @@ 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
|
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:
|
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
|
@@ -477,11 +481,11 @@ describe FCM do
|
|
477
481
|
uri = "#{FCM::GROUP_NOTIFICATION_BASE_URI}/gcm/notification"
|
478
482
|
endpoint = stub_request(:get, uri).with(
|
479
483
|
headers: {
|
480
|
-
|
481
|
-
|
482
|
-
|
484
|
+
"Content-Type" => "application/json",
|
485
|
+
"Authorization" => "key=TEST_SERVER_KEY",
|
486
|
+
"project_id" => "TEST_PROJECT_ID",
|
483
487
|
},
|
484
|
-
query: {notification_key_name: "TEST_KEY_NAME"}
|
488
|
+
query: { notification_key_name: "TEST_KEY_NAME" },
|
485
489
|
)
|
486
490
|
client = FCM.new("TEST_SERVER_KEY")
|
487
491
|
|
@@ -491,7 +495,7 @@ describe FCM do
|
|
491
495
|
end
|
492
496
|
end
|
493
497
|
|
494
|
-
describe
|
498
|
+
describe "subscribing to a topic" do
|
495
499
|
# TODO
|
496
500
|
end
|
497
501
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fcm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kashif Rasul
|
8
8
|
- Shoaib Burq
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-11-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -29,8 +29,8 @@ description: fcm provides ruby bindings to Firebase Cloud Messaging (FCM) a cros
|
|
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@
|
33
|
-
- shoaib@
|
32
|
+
- kashif@decision-labs.com
|
33
|
+
- shoaib@decision-labs.com
|
34
34
|
executables: []
|
35
35
|
extensions: []
|
36
36
|
extra_rdoc_files: []
|
@@ -50,7 +50,7 @@ homepage: https://github.com/spacialdb/fcm
|
|
50
50
|
licenses:
|
51
51
|
- MIT
|
52
52
|
metadata: {}
|
53
|
-
post_install_message:
|
53
|
+
post_install_message:
|
54
54
|
rdoc_options: []
|
55
55
|
require_paths:
|
56
56
|
- lib
|
@@ -65,8 +65,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: '0'
|
67
67
|
requirements: []
|
68
|
-
rubygems_version: 3.0.3
|
69
|
-
signing_key:
|
68
|
+
rubygems_version: 3.0.3.1
|
69
|
+
signing_key:
|
70
70
|
specification_version: 4
|
71
71
|
summary: Reliably deliver messages and notifications via FCM
|
72
72
|
test_files:
|