firebase_cloud_messenger 0.1.1
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 +7 -0
- data/.gitignore +9 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +143 -0
- data/Rakefile +10 -0
- data/firebase_cloud_messenger.gemspec +28 -0
- data/lib/firebase_cloud_messenger.rb +30 -0
- data/lib/firebase_cloud_messenger/android.rb +2 -0
- data/lib/firebase_cloud_messenger/android/config.rb +12 -0
- data/lib/firebase_cloud_messenger/android/notification.rb +12 -0
- data/lib/firebase_cloud_messenger/apns.rb +5 -0
- data/lib/firebase_cloud_messenger/apns/alert.rb +12 -0
- data/lib/firebase_cloud_messenger/apns/apns_object.rb +15 -0
- data/lib/firebase_cloud_messenger/apns/config.rb +12 -0
- data/lib/firebase_cloud_messenger/apns/payload.rb +13 -0
- data/lib/firebase_cloud_messenger/auth_client.rb +29 -0
- data/lib/firebase_cloud_messenger/client.rb +96 -0
- data/lib/firebase_cloud_messenger/error.rb +56 -0
- data/lib/firebase_cloud_messenger/firebase_object.rb +29 -0
- data/lib/firebase_cloud_messenger/message.rb +47 -0
- data/lib/firebase_cloud_messenger/notification.rb +10 -0
- data/lib/firebase_cloud_messenger/schema.rb +121 -0
- data/lib/firebase_cloud_messenger/version.rb +3 -0
- data/lib/firebase_cloud_messenger/webpush.rb +2 -0
- data/lib/firebase_cloud_messenger/webpush/config.rb +12 -0
- data/lib/firebase_cloud_messenger/webpush/notification.rb +12 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: df760f55ce074ae2ec5f8e45d43ba2f0446e94f2
|
4
|
+
data.tar.gz: f7aef2b712e689775f41d30d55897d27dc9ce3a6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 71ae814f893ee647dc1149219787ba06481e536b35134e94f24ad82f86100e2833da1d5c789c1e70c31bf7080795ed0cb0c336c46ab2e75fedba7635c1ddcfd1
|
7
|
+
data.tar.gz: b12035cc991df505d0a7196a01e37a18a0574e88a2bd9a823910f54b3937861aefee4830dd9cb1bc279127391f2b2e5020835a666d3627f20a19442edce81c0f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2017 PatientsLikeMe
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# firebase-cloud-messenger
|
2
|
+
|
3
|
+
firebase-cloud-messenger wraps Google's API to make sending push notifications to iOS, android, and
|
4
|
+
web push notifications from your server easy.
|
5
|
+
|
6
|
+
NB: Google released the [FCM HTTP v1 API](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages)
|
7
|
+
in November 2017, giving legacy status to the older but still supported HTTP and XMPP apis.
|
8
|
+
This gem only targets the [FCM HTTP v1 API], which Google [recommends using for new projects](https://firebase.google.com/docs/cloud-messaging/server),
|
9
|
+
because it is the most up-to-date and secure.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'firebase_cloud_messenger'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install firebase_cloud_messenger
|
26
|
+
|
27
|
+
In order for google to authenticate requests to Firebase Cloud Messenger, you must have your
|
28
|
+
credentials file in a place that's accessible. You can tell firebase-cloud-messenger where it is in
|
29
|
+
two ways:
|
30
|
+
|
31
|
+
1. `FirebaseCloudMessenger.credentials_path = "path/to/credentials/file.json"`
|
32
|
+
2. `export GOOGLE_APPLICATION_CREDENTIALS = "path/to/credentials/file.json"`
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
### Sending a Message
|
37
|
+
|
38
|
+
You can see how your message should be structured [here](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages)
|
39
|
+
firebase-cloud-messenger provides built-in data classes for each json object type in the FCM API
|
40
|
+
specification, but you can also build up a hash message on your own, or use some combination of the
|
41
|
+
two.
|
42
|
+
|
43
|
+
Send messages with built-in data objects, which can be built through a hash argument to the
|
44
|
+
initializer or via writer methods:
|
45
|
+
```ruby
|
46
|
+
android_notification = FirebaseCloudMessenger::Android::Notification.new(title: "title")
|
47
|
+
android_config = FirebaseCloudMessenger::Android::AndroidConfig.new(notification: android_notification)
|
48
|
+
message = FirebaseCloudMessenger::Message.new(android: android_config, token "a_device_token")
|
49
|
+
|
50
|
+
FirebaseCloudMessenger.send(message: message) # => { "name" => "name_from_fcm" }
|
51
|
+
|
52
|
+
# OR
|
53
|
+
|
54
|
+
android_notification = FirebaseCloudMessenger::Android::Notification.new
|
55
|
+
android_notification.title = "title"
|
56
|
+
|
57
|
+
android_config = FirebaseCloudMessenger::Android::AndroidConfig.new
|
58
|
+
android_config.notification = android_notification
|
59
|
+
|
60
|
+
message = FirebaseCloudMessenger::Message.new
|
61
|
+
message.android = android_config
|
62
|
+
message.token = "a_device_token"
|
63
|
+
|
64
|
+
FirebaseCloudMessenger.send(message: message) # => { "name" => "name_from_fcm" }
|
65
|
+
```
|
66
|
+
|
67
|
+
or with just a hash:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
message = {
|
71
|
+
android: {
|
72
|
+
notification: {
|
73
|
+
title: "title"
|
74
|
+
}
|
75
|
+
},
|
76
|
+
token: "a_device_token"
|
77
|
+
}
|
78
|
+
|
79
|
+
FirebaseCloudMessenger.send(message: message) # => { "name" => "name_from_fcm" }
|
80
|
+
```
|
81
|
+
|
82
|
+
or some combination of the two:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
message = FirebaseCloudMessenger::Message.new(android: { notification: { title: "title" }, token: "a_device_token" })
|
86
|
+
FirebaseCloudMessenger.send(message: message) # => { "name" => "name_from_fcm" }
|
87
|
+
```
|
88
|
+
|
89
|
+
### Error Handling
|
90
|
+
|
91
|
+
If something goes wrong, `::send` will raise an instance of a subclass of `FirebaseCloudMessenger::Error` with
|
92
|
+
helpful info on what went wrong:
|
93
|
+
```ruby
|
94
|
+
message = FirebaseCloudMessenger::Message.new(android: { bad: "data" }, token: "a_device_token"})
|
95
|
+
begin
|
96
|
+
FirebaseCloudMessenger.send(message: message)
|
97
|
+
rescue FirebaseCloudMessenger::Error => e
|
98
|
+
e.class # => FirebaseCloudMessenger::BadRequest
|
99
|
+
e.message # => A message from fcm about what's wrong with the request
|
100
|
+
e.status # => 400
|
101
|
+
e.details # => An array of error details from fcm
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
### Message Validation
|
106
|
+
|
107
|
+
Many errors can be caught before sending by validating a message before sending it.
|
108
|
+
|
109
|
+
Validate your message either by via the Firebase Cloud Messenger API:
|
110
|
+
```ruby
|
111
|
+
message = FirebaseCloudMessenger.new(android: { bad: "data" })
|
112
|
+
message.valid?(against_api: true) # => false
|
113
|
+
message.errors # => [<error_msg>]
|
114
|
+
```
|
115
|
+
|
116
|
+
or client-side, via json-schema:
|
117
|
+
```ruby
|
118
|
+
message = FirebaseCloudMessenger.new(android: { bad: "data" }, token: "a_device_token")
|
119
|
+
message.valid? # => false
|
120
|
+
message.errors # => ["The property '#/android' contains additional properties [\"bad\"] outside of the schema when none are allowed in schema..."]
|
121
|
+
```
|
122
|
+
|
123
|
+
Validate your hash message (returns only true or false):
|
124
|
+
```ruby
|
125
|
+
message = {
|
126
|
+
android: { bad: "data" },
|
127
|
+
token: "a_device_token"
|
128
|
+
}
|
129
|
+
|
130
|
+
#api-side
|
131
|
+
FirebaseCloudMessenger.validate_message(message, against_api: true) # => false
|
132
|
+
|
133
|
+
#OR
|
134
|
+
|
135
|
+
#client-side
|
136
|
+
FirebaseCloudMessenger.validate_message(message) # => false
|
137
|
+
|
138
|
+
```
|
139
|
+
|
140
|
+
|
141
|
+
## Development
|
142
|
+
|
143
|
+
After checking out the repo, run `bundle` to install dependencies. Then, run `rake test` to run the tests.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'firebase_cloud_messenger/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "firebase_cloud_messenger"
|
8
|
+
spec.version = FirebaseCloudMessenger::VERSION
|
9
|
+
spec.license = "MIT"
|
10
|
+
spec.authors = ["Vince DeVendra"]
|
11
|
+
spec.email = ["vdevendra@patientslikeme.com"]
|
12
|
+
spec.homepage = "https://github.com/patientslikeme/firebase_cloud_messenger"
|
13
|
+
|
14
|
+
spec.summary = %q{A ruby api wrapper for the FirebaseCloudMesenger API}
|
15
|
+
spec.description = spec.summary
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "googleauth", "~> 0.6"
|
22
|
+
spec.add_runtime_dependency "json-schema", "~> 2.8"
|
23
|
+
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "minitest"
|
26
|
+
spec.add_development_dependency "mocha"
|
27
|
+
spec.add_development_dependency "pry"
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'googleauth'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require 'firebase_cloud_messenger/auth_client'
|
6
|
+
require 'firebase_cloud_messenger/error'
|
7
|
+
require 'firebase_cloud_messenger/client'
|
8
|
+
require 'firebase_cloud_messenger/firebase_object'
|
9
|
+
require 'firebase_cloud_messenger/message'
|
10
|
+
require 'firebase_cloud_messenger/notification'
|
11
|
+
require 'firebase_cloud_messenger/android'
|
12
|
+
require 'firebase_cloud_messenger/apns'
|
13
|
+
require 'firebase_cloud_messenger/webpush'
|
14
|
+
|
15
|
+
|
16
|
+
module FirebaseCloudMessenger
|
17
|
+
class << self
|
18
|
+
attr_accessor :credentials_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.send(message: {}, validate_only: false, conn: nil)
|
22
|
+
Client.new(credentials_path).send(message, validate_only, conn)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.validate_message(message, conn = nil, against_api: false)
|
26
|
+
message = Message.new(message) if message.is_a?(Hash)
|
27
|
+
|
28
|
+
message.valid?(conn, against_api: against_api)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
module Android
|
3
|
+
class Config < FirebaseObject
|
4
|
+
FIELDS = %i(collapse_key priority ttl restricted_package_name data notification).freeze
|
5
|
+
attr_accessor(*FIELDS)
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
super(data, FIELDS)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
module Android
|
3
|
+
class Notification < FirebaseObject
|
4
|
+
FIELDS = %i(title body icon color sound tag click_action body_loc_key body_loc_args title_loc_key title_loc_args).freeze
|
5
|
+
attr_accessor(*FIELDS)
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
super(data, FIELDS)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
module Apns
|
3
|
+
class Alert < ApnsObject
|
4
|
+
FIELDS = %i(title body title_loc_key title_loc_args action_loc_key loc_key loc_args launch_image).freeze
|
5
|
+
attr_accessor(*FIELDS)
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
super(data, FIELDS)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
module Apns
|
3
|
+
class ApnsObject < FirebaseObject
|
4
|
+
def to_h
|
5
|
+
fields.each_with_object({}) do |field, object_hash|
|
6
|
+
val = send(field)
|
7
|
+
next if val.nil?
|
8
|
+
val = val.to_h if val.is_a?(FirebaseObject)
|
9
|
+
|
10
|
+
object_hash[field.to_s.gsub("_", "-").to_sym] = val
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
class AuthClient
|
3
|
+
attr_reader :credentials_path
|
4
|
+
|
5
|
+
AUTH_SCOPE = "https://www.googleapis.com/auth/firebase.messaging".freeze
|
6
|
+
|
7
|
+
def initialize(credentials_path)
|
8
|
+
@credentials_path = credentials_path
|
9
|
+
@authorizer = get_authorizer
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch_access_token_info
|
13
|
+
authorizer.fetch_access_token!
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :authorizer
|
19
|
+
|
20
|
+
def get_authorizer
|
21
|
+
begin
|
22
|
+
file = File.open(credentials_path)
|
23
|
+
Google::Auth::ServiceAccountCredentials.make_creds(json_key_io: file, scope: AUTH_SCOPE)
|
24
|
+
ensure
|
25
|
+
file.close
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'firebase_cloud_messenger/error'
|
2
|
+
|
3
|
+
module FirebaseCloudMessenger
|
4
|
+
class Client
|
5
|
+
attr_writer :max_retry_count, :project_id, :access_token
|
6
|
+
attr_accessor :credentials_path
|
7
|
+
|
8
|
+
def initialize(credentials_path = nil)
|
9
|
+
@credentials_path = credentials_path || ENV['GOOGLE_APPLICATION_CREDENTIALS']
|
10
|
+
end
|
11
|
+
|
12
|
+
def send(message, validate_only, conn)
|
13
|
+
retry_count = 0
|
14
|
+
|
15
|
+
loop do
|
16
|
+
response = make_fcm_request(message, validate_only, conn || request_conn)
|
17
|
+
|
18
|
+
retry_count += 1
|
19
|
+
if response_successful?(response) || retry_count > max_retry_count
|
20
|
+
return message_from_response(response)
|
21
|
+
elsif response.code == "401"
|
22
|
+
refresh_access_token_info
|
23
|
+
else
|
24
|
+
raise Error.from_response(response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def request_conn
|
30
|
+
conn = Net::HTTP.new(send_url.host, 443)
|
31
|
+
conn.use_ssl = true
|
32
|
+
conn
|
33
|
+
end
|
34
|
+
|
35
|
+
def request_headers
|
36
|
+
{
|
37
|
+
"Content-Type" => "application/json",
|
38
|
+
"Authorization" => "Bearer #{access_token}"
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def request_body(message, validate_only)
|
43
|
+
{
|
44
|
+
message: message.to_h,
|
45
|
+
validateOnly: validate_only
|
46
|
+
}.to_json
|
47
|
+
end
|
48
|
+
|
49
|
+
def access_token
|
50
|
+
@access_token ||= access_token_info["access_token"]
|
51
|
+
end
|
52
|
+
|
53
|
+
def auth_client
|
54
|
+
@auth_client ||= AuthClient.new(credentials_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def access_token_info
|
58
|
+
@access_token_info ||= auth_client.fetch_access_token_info
|
59
|
+
end
|
60
|
+
|
61
|
+
def refresh_access_token_info
|
62
|
+
self.access_token = nil
|
63
|
+
self.access_token_info = auth_client.fetch_access_token_info
|
64
|
+
end
|
65
|
+
|
66
|
+
def project_id
|
67
|
+
@project_id ||= JSON.parse(File.read(credentials_path)).fetch("project_id")
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_url
|
71
|
+
URI "https://fcm.googleapis.com/v1/projects/#{project_id}/messages:send"
|
72
|
+
end
|
73
|
+
|
74
|
+
def max_retry_count
|
75
|
+
@max_retry_count ||= 1
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
attr_writer :access_token_info
|
81
|
+
|
82
|
+
def make_fcm_request(message, validate_only, conn = request_conn)
|
83
|
+
conn.post(send_url.path,
|
84
|
+
request_body(message, validate_only),
|
85
|
+
request_headers)
|
86
|
+
end
|
87
|
+
|
88
|
+
def message_from_response(response)
|
89
|
+
JSON.parse(response.body)
|
90
|
+
end
|
91
|
+
|
92
|
+
def response_successful?(response)
|
93
|
+
("200".."299").include?(response.code)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
class Error < StandardError
|
3
|
+
attr_reader :response
|
4
|
+
|
5
|
+
def self.from_response(response)
|
6
|
+
status = response.code
|
7
|
+
|
8
|
+
klass = case status.to_i
|
9
|
+
when 400 then FirebaseCloudMessenger::BadRequest
|
10
|
+
when 401 then FirebaseCloudMessenger::Unauthorized
|
11
|
+
when 403 then FirebaseCloudMessenger::Forbidden
|
12
|
+
else self
|
13
|
+
end
|
14
|
+
|
15
|
+
klass.new(response)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(response = nil)
|
19
|
+
@response = response
|
20
|
+
|
21
|
+
super(error_message)
|
22
|
+
end
|
23
|
+
|
24
|
+
def response_status
|
25
|
+
response.code.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
def response_body
|
29
|
+
response.body
|
30
|
+
end
|
31
|
+
|
32
|
+
def parsed_response
|
33
|
+
JSON.parse(response_body)
|
34
|
+
end
|
35
|
+
|
36
|
+
def details
|
37
|
+
parsed_response["error"]["details"]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def error_message
|
43
|
+
return nil if response.nil?
|
44
|
+
|
45
|
+
<<-MSG
|
46
|
+
Status: #{response_status}
|
47
|
+
|
48
|
+
#{parsed_response["error"]["message"]}
|
49
|
+
MSG
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class BadRequest < Error; end
|
54
|
+
class Unauthorized < Error; end
|
55
|
+
class Forbidden < Error; end
|
56
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
class FirebaseObject
|
3
|
+
def initialize(data, fields)
|
4
|
+
@fields = fields
|
5
|
+
|
6
|
+
fields.each do |field|
|
7
|
+
send(:"#{field}=", data.delete(field))
|
8
|
+
end
|
9
|
+
|
10
|
+
if data.any?
|
11
|
+
raise ArgumentError, "Keys must be one on #{fields.inspect}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_h
|
16
|
+
fields.each_with_object({}) do |field, object_hash|
|
17
|
+
val = send(field)
|
18
|
+
next if val.nil?
|
19
|
+
val = val.to_h if val.is_a?(FirebaseObject)
|
20
|
+
|
21
|
+
object_hash[field] = val
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :fields
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'firebase_cloud_messenger/schema'
|
2
|
+
require 'json-schema'
|
3
|
+
|
4
|
+
module FirebaseCloudMessenger
|
5
|
+
class Message < FirebaseObject
|
6
|
+
FIELDS = %i(name data notification android webpush apns token topic condition).freeze
|
7
|
+
attr_accessor(*FIELDS)
|
8
|
+
|
9
|
+
attr_writer :errors
|
10
|
+
|
11
|
+
def initialize(data)
|
12
|
+
super(data, FIELDS)
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?(conn = nil, against_api: false)
|
16
|
+
if against_api
|
17
|
+
validate_against_api!(conn)
|
18
|
+
else
|
19
|
+
validate_against_schema
|
20
|
+
end
|
21
|
+
|
22
|
+
errors.none?
|
23
|
+
end
|
24
|
+
|
25
|
+
def errors
|
26
|
+
@errors ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def validate_against_api!(conn)
|
32
|
+
begin
|
33
|
+
FirebaseCloudMessenger.send(message: self, validate_only: true, conn: conn)
|
34
|
+
rescue FirebaseCloudMessenger::Error => e
|
35
|
+
if e.details
|
36
|
+
self.errors += e.details
|
37
|
+
else
|
38
|
+
self.errors << e.parsed_response["error"]["message"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_against_schema
|
44
|
+
self.errors += JSON::Validator.fully_validate(FirebaseCloudMessenger::SCHEMA, to_h)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module FirebaseCloudMessenger
|
2
|
+
SCHEMA = {
|
3
|
+
"definitions" => {
|
4
|
+
"data" => {
|
5
|
+
"type" => "object",
|
6
|
+
"additionalProperties" => { "type" => "string" }
|
7
|
+
},
|
8
|
+
"notification" => {
|
9
|
+
"type" => "object",
|
10
|
+
"properties" => {
|
11
|
+
"title" => { "type" => "string" },
|
12
|
+
"body" => { "type" => "string" }
|
13
|
+
},
|
14
|
+
"additionalProperties" => false
|
15
|
+
},
|
16
|
+
"android_config" => {
|
17
|
+
"type" => "object",
|
18
|
+
"properties" => {
|
19
|
+
"collapse_key" => { "type" => "string" },
|
20
|
+
"priority" => { "type" => "string", "enum" => ["NORMAL", "HIGH"] },
|
21
|
+
"ttl" => { "type" => "string", "format" => "^[0-9]+\.[0-9]+s$" },
|
22
|
+
"restricted_package_name" => { "type" => "string" },
|
23
|
+
"data" => { "$ref" => "#/definitions/data" },
|
24
|
+
"notification" => { "$ref" => "#/definitions/android_notification" }
|
25
|
+
},
|
26
|
+
"additionalProperties" => false
|
27
|
+
},
|
28
|
+
"android_notification" => {
|
29
|
+
"type" => "object",
|
30
|
+
"properties" => {
|
31
|
+
"title" => { "type" => "string" },
|
32
|
+
"body" => { "type" => "string" },
|
33
|
+
"icon" => { "type" => "string" },
|
34
|
+
"color" => { "type" => "string" },
|
35
|
+
"sound" => { "type" => "string" },
|
36
|
+
"tag" => { "type" => "string" },
|
37
|
+
"click_action" => { "type" => "string" },
|
38
|
+
"body_loc_key" => { "type" => "string" },
|
39
|
+
"body_loc_args" => { "type" => "array", "items" => { "type" => "string" } },
|
40
|
+
"title_loc_key" => { "type" => "string" },
|
41
|
+
"title_loc_args" => { "type" => "array", "items" => { "type" => "string" } }
|
42
|
+
},
|
43
|
+
"additionalProperties" => false
|
44
|
+
},
|
45
|
+
"webpush_config" => {
|
46
|
+
"type" => "object",
|
47
|
+
"properties" => {
|
48
|
+
"headers" => { "$ref" => "#/definitions/data" },
|
49
|
+
"data" => { "$ref" => "#/definitions/data" },
|
50
|
+
"notification" => { "$ref" => "#/definitions/webpush_notification" }
|
51
|
+
},
|
52
|
+
"additionalProperties" => false
|
53
|
+
},
|
54
|
+
"webpush_notification" => {
|
55
|
+
"type" => "object",
|
56
|
+
"properties" => {
|
57
|
+
"title" => { "type" => "string" },
|
58
|
+
"body" => { "type" => "string" },
|
59
|
+
"icon" => { "type" => "string" }
|
60
|
+
},
|
61
|
+
"additionalProperties" => false
|
62
|
+
},
|
63
|
+
"apns" => {
|
64
|
+
"type" => "object",
|
65
|
+
"properties" => {
|
66
|
+
"headers" => { "$ref" => "#/definitions/data" },
|
67
|
+
"payload" => { "$ref" => "#/definitions/apns_payload" }
|
68
|
+
},
|
69
|
+
"additionalProperties" => false
|
70
|
+
},
|
71
|
+
"apns_alert" => {
|
72
|
+
"anyOf" => [
|
73
|
+
{ "type" => "string" },
|
74
|
+
{
|
75
|
+
"type" => "object",
|
76
|
+
"properties" => {
|
77
|
+
"title" => { "type" => "string" },
|
78
|
+
"body" => { "type" => "string" },
|
79
|
+
"title-loc-key " => { "anyOf" => [ { "type" => "string" }, { "type" => "null" } ] },
|
80
|
+
"title-loc-args" => { "anyOf" => [ { "type" => "array", "items" => { "type" => "string" } }, { "type" => "null" }] },
|
81
|
+
"action-loc-key" => { "anyOf" => [ { "type" => "string" }, { "type" => "null" } ] },
|
82
|
+
"loc-key" => { "type" => "string" },
|
83
|
+
"loc-args" => { "type" => "array", "items" => { "type" => "string" } },
|
84
|
+
"launch-image" => { "type" => "string" }
|
85
|
+
},
|
86
|
+
"additionalProperties" => false
|
87
|
+
}
|
88
|
+
]
|
89
|
+
},
|
90
|
+
"apns_payload" => {
|
91
|
+
"type" => "object",
|
92
|
+
"properties" => {
|
93
|
+
"alert" => { "$ref" => "#/definitions/apns_alert" },
|
94
|
+
"badge" => { "type" => "number" },
|
95
|
+
"sound" => { "type" => "string" },
|
96
|
+
"content-available" => { "type" => "number" },
|
97
|
+
"category" => { "type" => "string" },
|
98
|
+
"thread-id" => { "type" => "string" }
|
99
|
+
},
|
100
|
+
"additionalProperties" => false
|
101
|
+
}
|
102
|
+
},
|
103
|
+
"type" => "object",
|
104
|
+
"properties" => {
|
105
|
+
"data" => { "$ref" => "#/definitions/data" },
|
106
|
+
"notification" => { "$ref" => "#/definitions/notification" },
|
107
|
+
"android" => { "$ref" => "#/definitions/android_config" },
|
108
|
+
"webpush" => { "$ref" => "#/definitions/webpush_config" },
|
109
|
+
"apns" => { "$ref" => "#/definitions/apns" },
|
110
|
+
"token" => { "type" => "string" },
|
111
|
+
"topic" => { "type" => "string" },
|
112
|
+
"condition" => { "type" => "string" }
|
113
|
+
},
|
114
|
+
"additionalProperties" => false,
|
115
|
+
"oneOf" => [
|
116
|
+
{ "required" => ["token"] },
|
117
|
+
{ "required" => ["topic"] },
|
118
|
+
{ "required" => ["condition"] }
|
119
|
+
]
|
120
|
+
}
|
121
|
+
end
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: firebase_cloud_messenger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vince DeVendra
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-12-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: googleauth
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json-schema
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: A ruby api wrapper for the FirebaseCloudMesenger API
|
98
|
+
email:
|
99
|
+
- vdevendra@patientslikeme.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- Gemfile
|
106
|
+
- LICENSE.txt
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- firebase_cloud_messenger.gemspec
|
110
|
+
- lib/firebase_cloud_messenger.rb
|
111
|
+
- lib/firebase_cloud_messenger/android.rb
|
112
|
+
- lib/firebase_cloud_messenger/android/config.rb
|
113
|
+
- lib/firebase_cloud_messenger/android/notification.rb
|
114
|
+
- lib/firebase_cloud_messenger/apns.rb
|
115
|
+
- lib/firebase_cloud_messenger/apns/alert.rb
|
116
|
+
- lib/firebase_cloud_messenger/apns/apns_object.rb
|
117
|
+
- lib/firebase_cloud_messenger/apns/config.rb
|
118
|
+
- lib/firebase_cloud_messenger/apns/payload.rb
|
119
|
+
- lib/firebase_cloud_messenger/auth_client.rb
|
120
|
+
- lib/firebase_cloud_messenger/client.rb
|
121
|
+
- lib/firebase_cloud_messenger/error.rb
|
122
|
+
- lib/firebase_cloud_messenger/firebase_object.rb
|
123
|
+
- lib/firebase_cloud_messenger/message.rb
|
124
|
+
- lib/firebase_cloud_messenger/notification.rb
|
125
|
+
- lib/firebase_cloud_messenger/schema.rb
|
126
|
+
- lib/firebase_cloud_messenger/version.rb
|
127
|
+
- lib/firebase_cloud_messenger/webpush.rb
|
128
|
+
- lib/firebase_cloud_messenger/webpush/config.rb
|
129
|
+
- lib/firebase_cloud_messenger/webpush/notification.rb
|
130
|
+
homepage: https://github.com/patientslikeme/firebase_cloud_messenger
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.5.2
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: A ruby api wrapper for the FirebaseCloudMesenger API
|
154
|
+
test_files: []
|