Skip to content
28 changes: 9 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,45 +26,36 @@ A version of supported Ruby, currently:
To use this gem, you need to instantiate a client with your firebase credentials:

```ruby
fcm = FCM.new(
GOOGLE_APPLICATION_CREDENTIALS_PATH,
FIREBASE_PROJECT_ID
)
fcm = FCM.new(GOOGLE_APPLICATION_CREDENTIALS_PATH)
```

**Note:** The Firebase project ID is automatically extracted from your Firebase service credentials file.

## About the `GOOGLE_APPLICATION_CREDENTIALS_PATH`
The `GOOGLE_APPLICATION_CREDENTIALS_PATH` is meant to contain your firebase credentials.

The easiest way to provide them is to pass here an absolute path to a file with your credentials:

```ruby
fcm = FCM.new(
'/path/to/credentials.json',
FIREBASE_PROJECT_ID
)
fcm = FCM.new('/path/to/credentials.json')
```

As per their secret nature, you might not want to have them in your repository. In that case, another supported solution is to pass a `StringIO` that contains your credentials:

```ruby
fcm = FCM.new(
StringIO.new(ENV.fetch('FIREBASE_CREDENTIALS')),
FIREBASE_PROJECT_ID
)

fcm = FCM.new(StringIO.new(ENV.fetch('FIREBASE_CREDENTIALS')))
```

The gem will automatically extract the `project_id` from your Firebase service credentials file.

## Usage

## HTTP v1 API

To migrate to HTTP v1 see: https://firebase.google.com/docs/cloud-messaging/migrate-v1

```ruby
fcm = FCM.new(
GOOGLE_APPLICATION_CREDENTIALS_PATH,
FIREBASE_PROJECT_ID
)
fcm = FCM.new(GOOGLE_APPLICATION_CREDENTIALS_PATH)
message = {
'token': "000iddqd", # send to a specific device
# 'topic': "yourTopic",
Expand Down Expand Up @@ -108,8 +99,7 @@ https://firebase.google.com/docs/cloud-messaging/android/device-group#managing_d

Then you will need a notification key which you can create for a particular `key_name` which needs to be uniquely named per app in case you have multiple apps for the same `project_id`. This ensures that notifications only go to the intended target app. The `create` method will do this and return the token `notification_key`, that represents the device group, in the response:

`project_id` is the SENDER_ID in your cloud settings.
https://firebase.google.com/docs/cloud-messaging/concept-options#senderid
**Note:** The `project_id` parameter used in device group management is your Firebase project's sender ID, which can be found in your Firebase project settings under Cloud Messaging.

```ruby
params = { key_name: "appUser-Chris",
Expand Down
49 changes: 41 additions & 8 deletions lib/fcm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,27 @@ class FCM
INSTANCE_ID_API = "https://iid.googleapis.com"
TOPIC_REGEX = /[a-zA-Z0-9\-_.~%]+/

def initialize(json_key_path = "", project_name = "", http_options = {})
# Service account credentials file schema (JSON format):
# {
# "type": "service_account",
# "project_id": "your-project-id",
# "private_key_id": "key-id",
# "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
Comment thread
xavarius marked this conversation as resolved.
# "client_email": "firebase-adminsdk@your-project-id.iam.gserviceaccount.com",
Comment thread
xavarius marked this conversation as resolved.
# "client_id": "client-id",
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
# "token_uri": "https://oauth2.googleapis.com/token",
# "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
Comment thread
xavarius marked this conversation as resolved.
# "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk%40your-project-id.iam.gserviceaccount.com",
Comment thread
xavarius marked this conversation as resolved.
# "universe_domain": "googleapis.com"
# }
# The project_id field is automatically extracted from this file for FCM API calls.
Comment thread
xavarius marked this conversation as resolved.
def initialize(json_key_path = '', http_options = {})
@json_key_path = json_key_path
@project_name = project_name
@http_options = http_options
end

# See https://firebase.google.com/docs/cloud-messaging/send-message
# See https://firebase.google.com/docs/cloud-messaging/send/v1-api
# {
# "token": "4sdsx",
# "notification": {
Expand All @@ -42,17 +56,17 @@ def initialize(json_key_path = "", project_name = "", http_options = {})
# }
# }
# }
# fcm = FCM.new(json_key_path, project_name)
# fcm = FCM.new(json_key_path)
# fcm.send_v1(
# { "token": "4sdsx",, "to" : "notification": {}.. }
# )
def send_notification_v1(message)
return if @project_name.empty?
return if firebase_project_id.empty?

post_body = { 'message': message }
for_uri(BASE_URI_V1) do |connection|
response = connection.post(
"#{@project_name}/messages:send", post_body.to_json
"#{firebase_project_id}/messages:send", post_body.to_json
)
build_response(response)
end
Expand Down Expand Up @@ -168,7 +182,7 @@ def send_to_topic(topic, options = {})

for_uri(BASE_URI_V1) do |connection|
response = connection.post(
"#{@project_name}/messages:send", body.to_json
"#{firebase_project_id}/messages:send", body.to_json
)
build_response(response)
end
Expand All @@ -181,7 +195,7 @@ def send_to_topic_condition(condition, options = {})

for_uri(BASE_URI_V1) do |connection|
response = connection.post(
"#{@project_name}/messages:send", body.to_json
"#{firebase_project_id}/messages:send", body.to_json
)
build_response(response)
end
Expand Down Expand Up @@ -298,4 +312,23 @@ def json_key
File.open(@json_key_path)
end
end

def firebase_project_id
@firebase_project_id ||= extract_project_id
end

def extract_project_id
return '' if @json_key_path.nil? || @json_key_path == ''

json_content = if @json_key_path.respond_to?(:read)
@json_key_path.read.tap { @json_key_path.rewind }
else
File.read(@json_key_path)
end

credentials = JSON.parse(json_content)
credentials['project_id'] || ''
rescue JSON::ParserError, Errno::ENOENT
''
end
end
20 changes: 11 additions & 9 deletions spec/fcm_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require "spec_helper"

describe FCM do
let(:project_name) { 'test-project' }
let(:firebase_project_id) { 'test-project' }
let(:json_key_path) { 'path/to/json/key.json' }
let(:client) { FCM.new(json_key_path) }

Expand All @@ -15,6 +15,8 @@

before do
allow(client).to receive(:json_key)
allow(client).to receive(:extract_project_id)
.and_return(firebase_project_id)

# Mock the Google::Auth::ServiceAccountCredentials
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds).
Expand All @@ -38,9 +40,9 @@
end

describe "#send_v1 or #send_notification_v1" do
let(:client) { FCM.new(json_key_path, project_name) }
let(:client) { FCM.new(json_key_path) }

let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
let(:uri) { "#{FCM::BASE_URI_V1}#{firebase_project_id}/messages:send" }
let(:status_code) { 200 }

let(:stub_fcm_send_v1_request) do
Expand Down Expand Up @@ -177,8 +179,8 @@
include_examples 'succesfuly send notification'
end

context 'when project_name is empty' do
let(:project_name) { '' }
context 'when firebase_project_id is empty' do
let(:firebase_project_id) { '' }
let(:send_v1_params) do
{
'token' => '4sdsx',
Expand Down Expand Up @@ -249,9 +251,9 @@
end

describe '#send_to_topic' do
let(:client) { FCM.new(json_key_path, project_name) }
let(:client) { FCM.new(json_key_path) }

let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
let(:uri) { "#{FCM::BASE_URI_V1}#{firebase_project_id}/messages:send" }

let(:topic) { 'news' }
let(:params) do
Expand Down Expand Up @@ -300,9 +302,9 @@
end

describe "#send_to_topic_condition" do
let(:client) { FCM.new(json_key_path, project_name) }
let(:client) { FCM.new(json_key_path) }

let(:uri) { "#{FCM::BASE_URI_V1}#{project_name}/messages:send" }
let(:uri) { "#{FCM::BASE_URI_V1}#{firebase_project_id}/messages:send" }

let(:topic_condition) { "'foo' in topics" }
let(:params) do
Expand Down