module Pushlet class API < Sinatra::Base register Sinatra::JSONAPI set :show_exceptions, false class << self def gateway_pool @gateway_pool ||= GatewayPool.new end def uuid @uuid ||= UUID.new end def http_client @http_client ||= HTTPClient.new end def http_basic_auth(user, pass) use AuthMiddleware, user, pass end end get '/application_feedback' do if p[:application_id].nil? api_error :missing_application_id, 'an application_id is required for this call' end app = self.class.gateway_pool.get p[:application_id] api_error :application_not_found, 'no application found for the provided application_id' if app.nil? api_response response: app.feedback.collect {|fda| {device_token: fda.device_token, timestamp: fda.timestamp.utc.iso8601}} end post '/register_application' do id = p[:application_id] || self.class.uuid.generate(:compact) if p[:apns_p12].nil? && p[:apns_pem].nil? api_error :missing_apns_certificate, 'an apns_p12 or apns_pem certificate is required for this call' end api_error :apns_mode_required, 'specify apns_mode=production or apns_mode=development' unless ['production','development'].include? p[:apns_mode] apns_p12 = p[:apns_p12].is_a?(Hash) ? p[:apns_p12][:tempfile].read : p[:apns_p12] apns_pem = p[:apns_pem].is_a?(Hash) ? p[:apns_pem][:tempfile].read : p[:apns_pem] begin apns_gateway = APNSGateway.new({ mode: p[:apns_mode], p12: apns_p12, pem: apns_pem, p12_pass: p[:apns_p12_pass] }) rescue OpenSSL::PKCS12::PKCS12Error api_error :invalid_apns_p12, 'could not process apns_p12 file, check that it is in p12 format and is valid' end self.class.gateway_pool.add id, apns_gateway api_response application_id: id end post '/send_notification' do if p[:application_id].nil? && p[:gcm_api_key].nil? api_error :missing_identifier, 'an application_id or gcm_api_key are required for this call' end if p[:device_token].nil? || p[:device_token].empty? api_error :device_token_required, 'device_token is required, which is registed with the application on your phone' end if p[:application_id] app = self.class.gateway_pool.get p[:application_id] api_error :application_not_found, 'no application found for the provided application_id' if app.nil? api_error :missing_payload, 'at least one of alert, badge, sound or data is required' if p[:alert].nil? && p[:badge].nil? && p[:sound].nil? && p[:data].nil? app.push({ device_token: p[:device_token], alert: p[:alert], badge: p[:badge], sound: p[:sound], identifier: p[:identifier], custom: p[:data] }) api_response response: 'ok' else api_error :gcm_api_key_required, 'must provide gcm_api_key' if p[:gcm_api_key].nil? payload = {registration_ids: [p[:device_token]]} [:collapse_key, :data, :delay_while_idle, :time_to_live, :restricted_package_name, :dry_run].each do |key| if key == :data begin payload[key] = JSON.parse params[:data] if params[:data] rescue JSON::ParserError => e api_error :invalid_data_json, "data could not be converted to json: #{e.message}" end else payload[key] = p[key] unless p[key].nil? end end headers = { 'Content-Type' => 'application/json', 'Authorization' => "key=#{p[:gcm_api_key]}" } resp = self.class.http_client.post 'https://android.googleapis.com/gcm/send', payload.to_json, headers if resp.status == 401 api_error :invalid_gcm_credentials, 'the provided credentials were rejected by google servers' else api_response response: JSON.parse(resp.body) end end end def p; params end end end