class Rpush::Daemon::Fcm::Delivery

firebase.google.com/docs/cloud-messaging/server

Constants

HOST
SCOPE

Public Class Methods

new(app, http, notification, batch) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 11
def initialize(app, http, notification, batch)
  if necessary_data_exists?(app)
    @app = app
    @http = http
    @notification = notification
    @batch = batch

    @uri = URI.parse("#{HOST}/v1/projects/#{@app.firebase_project_id || ENV['FIREBASE_PROJECT_ID']}/messages:send")
  else
    Rpush.logger.error("Cannot find necessary configuration! Please make sure you have set all necessary ENV variables or firebase_project_id and json_key attributes.")
  end
end

Public Instance Methods

perform() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 24
def perform
  handle_response(do_post)
rescue SocketError => error
  mark_retryable(@notification, Time.now + 10.seconds, error)
  raise
rescue StandardError => error
  mark_failed(error)
  raise
ensure
  @batch.notification_processed
end

Protected Instance Methods

bad_gateway(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 98
def bad_gateway(response)
  retry_delivery(@notification, response)
  log_warn("FCM responded with a Bad Gateway Error. " + retry_message)
end
bad_request(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 71
def bad_request(response)
  fail Rpush::DeliveryError.new(400, @notification.id, "FCM failed to handle the JSON request. (#{parse_error(response)})")
end
deliver_after_header(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 118
def deliver_after_header(response)
  Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
end
do_post() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 139
def do_post
  token = obtain_access_token['access_token']
  post = Net::HTTP::Post.new(@uri.path, 'Content-Type' => 'application/json',
                             'Authorization' => "Bearer #{token}")
  post.body = @notification.as_json.to_json
  @http.request(@uri, post)
end
handle_response(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 38
def handle_response(response)
  case response.code.to_i
  when 200
    ok
  when 400
    bad_request(response)
  when 401
    unauthorized
  when 403
    sender_id_mismatch
  when 404
    unregistered(response)
  when 429
    too_many_requests
  when 500
    internal_server_error(response)
  when 502
    bad_gateway(response)
  when 503
    service_unavailable(response)
  when 500..599
    other_5xx_error(response)
  else
    fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
  end
end
internal_server_error(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 93
def internal_server_error(response)
  retry_delivery(@notification, response)
  log_warn("FCM responded with an Internal Error. " + retry_message)
end
necessary_data_exists?(app) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 147
def necessary_data_exists?(app)
  # Needed for Google Auth
  # See https://github.com/googleapis/google-auth-library-ruby#example-environment-variables
  # for further information
  (app.firebase_project_id || ENV.key?('FIREBASE_PROJECT_ID')) &&
    (app.json_key || (
      ENV.key?('GOOGLE_ACCOUNT_TYPE') &&
        ENV.key?('GOOGLE_CLIENT_ID') &&
        ENV.key?('GOOGLE_CLIENT_EMAIL') &&
        ENV.key?('GOOGLE_PRIVATE_KEY')
    ))
end
obtain_access_token() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 135
def obtain_access_token
  GoogleCredentialCache.instance.access_token(SCOPE, @app.json_key)
end
ok() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 65
def ok
  reflect(:fcm_delivered_to_recipient, @notification)
  mark_delivered
  log_info("#{@notification.id} sent to #{@notification.device_token}")
end
other_5xx_error(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 108
def other_5xx_error(response)
  retry_delivery(@notification, response)
  log_warn("FCM responded with a 5xx Error. " + retry_message)
end
parse_error(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 113
def parse_error(response)
  error = multi_json_load(response.body)['error']
  "#{error['status']}: #{error['message']}"
end
retry_delivery(notification, response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 122
def retry_delivery(notification, response)
  time = deliver_after_header(response)
  if time
    mark_retryable(notification, time)
  else
    mark_retryable_exponential(notification)
  end
end
retry_message() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 131
def retry_message
  "Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} (retry #{@notification.retries})."
end
sender_id_mismatch() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 79
def sender_id_mismatch
  fail Rpush::DeliveryError.new(403, @notification.id, 'The sender ID was mismatched. It seems the device token is wrong.')
end
service_unavailable(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 103
def service_unavailable(response)
  retry_delivery(@notification, response)
  log_warn("FCM responded with an Service Unavailable Error. " + retry_message)
end
too_many_requests() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 89
def too_many_requests
  fail Rpush::DeliveryError.new(429, @notification.id, 'Slow down. Too many requests were sent!')
end
unauthorized() click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 75
def unauthorized
  fail Rpush::DeliveryError.new(401, @notification.id, 'Unauthorized, Bearer token could not be validated.')
end
unregistered(response) click to toggle source
# File lib/rpush/daemon/fcm/delivery.rb, line 83
def unregistered(response)
  error = parse_error(response)
  reflect(:fcm_invalid_device_token, @app, error, @notification.device_token)
  fail Rpush::DeliveryError.new(404, @notification.id, "Client was not registered for your app. (#{error})")
end