diff --git a/applications/crossbar/priv/api/swagger.json b/applications/crossbar/priv/api/swagger.json index 89b6d2db916..f673fd1f85a 100644 --- a/applications/crossbar/priv/api/swagger.json +++ b/applications/crossbar/priv/api/swagger.json @@ -33094,9 +33094,59 @@ }, "type": "object" }, + "firebase": { + "default": {}, + "description": "pusher firebase", + "properties": { + "api_key": { + "description": "API Key for firebase", + "type": "string" + }, + "extra_headers": { + "default": {}, + "description": "Additional headers for INVITEs sent to push devices in the UA", + "properties": { + "Invite-Format": { + "description": "When set to push_and_invite, sets the X-KAZOO-PUSHER-Invite-Format header to push_and_invite, resulting in both a push notification and a SIP INVITE being sent to the endpoint.", + "enum": [ + "invite", + "push", + "push_and_invite" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, "google": { + "default": {}, "description": "pusher google", - "type": "string" + "properties": { + "api_key": { + "description": "API Key for gcm", + "type": "string" + }, + "extra_headers": { + "default": {}, + "description": "Additional headers for INVITEs sent to push devices in the UA", + "properties": { + "Invite-Format": { + "description": "When set to push_and_invite, sets the X-KAZOO-PUSHER-Invite-Format header to push_and_invite, resulting in both a push notification and a SIP INVITE being sent to the endpoint.", + "enum": [ + "invite", + "push", + "push_and_invite" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" }, "modules": { "default": [], diff --git a/applications/crossbar/priv/couchdb/schemas/system_config.pusher.json b/applications/crossbar/priv/couchdb/schemas/system_config.pusher.json index 0e88c03042a..27d40668973 100644 --- a/applications/crossbar/priv/couchdb/schemas/system_config.pusher.json +++ b/applications/crossbar/priv/couchdb/schemas/system_config.pusher.json @@ -44,9 +44,59 @@ }, "type": "object" }, + "firebase": { + "default": {}, + "description": "pusher firebase", + "properties": { + "api_key": { + "description": "API Key for firebase", + "type": "string" + }, + "extra_headers": { + "default": {}, + "description": "Additional headers for INVITEs sent to push devices in the UA", + "properties": { + "Invite-Format": { + "description": "When set to push_and_invite, sets the X-KAZOO-PUSHER-Invite-Format header to push_and_invite, resulting in both a push notification and a SIP INVITE being sent to the endpoint.", + "enum": [ + "invite", + "push", + "push_and_invite" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, "google": { + "default": {}, "description": "pusher google", - "type": "string" + "properties": { + "api_key": { + "description": "API Key for gcm", + "type": "string" + }, + "extra_headers": { + "default": {}, + "description": "Additional headers for INVITEs sent to push devices in the UA", + "properties": { + "Invite-Format": { + "description": "When set to push_and_invite, sets the X-KAZOO-PUSHER-Invite-Format header to push_and_invite, resulting in both a push notification and a SIP INVITE being sent to the endpoint.", + "enum": [ + "invite", + "push", + "push_and_invite" + ], + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" }, "modules": { "default": [], diff --git a/applications/pusher/src/modules/pm_firebase.erl b/applications/pusher/src/modules/pm_firebase.erl new file mode 100644 index 00000000000..dc148296d4d --- /dev/null +++ b/applications/pusher/src/modules/pm_firebase.erl @@ -0,0 +1,101 @@ +%%%----------------------------------------------------------------------------- +%%% @copyright (C) 2010-2019, 2600Hz +%%% @doc +%%% @end +%%%----------------------------------------------------------------------------- +-module(pm_firebase). +-behaviour(gen_server). + +-include("pusher.hrl"). + +-define(SERVER, ?MODULE). + +-export([start_link/0]). + +-export([init/1 + ,handle_call/3 + ,handle_cast/2 + ,handle_info/2 + ,terminate/2 + ,code_change/3 + ]). + +-record(state, {tab :: ets:tid()}). +-type state() :: #state{}. + +-spec start_link() -> kz_types:startlink_ret(). +start_link() -> + gen_server:start_link({'local', ?SERVER}, ?MODULE, [],[]). + +-spec init([]) -> {'ok', state()}. +init([]) -> + kz_util:put_callid(?MODULE), + lager:debug("starting server"), + {'ok', #state{tab=ets:new(?MODULE, [])}}. + +-spec handle_call(any(), kz_term:pid_ref(), state()) -> kz_types:handle_call_ret_state(state()). +handle_call(_Request, _From, State) -> + {'reply', {'error', 'not_implemented'}, State}. + +-spec handle_cast(any(), state()) -> kz_types:handle_cast_ret_state(state()). +handle_cast({'push', JObj}, #state{tab=ETS}=State) -> + lager:debug("process a push"), + TokenApp = kz_json:get_value(<<"Token-App">>, JObj), + maybe_send_push_notification(get_fcm(TokenApp, ETS), JObj), + {'noreply', State}; +handle_cast('stop', State) -> + {'stop', 'normal', State}. + +-spec handle_info(any(), state()) -> kz_types:handle_info_ret_state(state()). +handle_info(_Request, State) -> + {'noreply', State}. + +-spec terminate(any(), state()) -> 'ok'. +terminate(_Reason, #state{tab=ETS}) -> + ets:delete(ETS), + 'ok'. + +-spec code_change(any(), state(), any()) -> {'ok', state()}. +code_change(_OldVsn, State, _Extra) -> + {'ok', State}. + +-spec maybe_send_push_notification(kz_term:api_pid(), kz_json:object()) -> any(). +maybe_send_push_notification('undefined', _JObj) -> lager:debug("no pid to send push"); +maybe_send_push_notification(Pid, JObj) -> + TokenID = kz_json:get_value(<<"Token-ID">>, JObj), + CallId = kz_json:get_value(<<"Call-ID">>, JObj), + Message = kz_json:from_list([{<<"data">>,kz_json:from_list([{<<"Call-ID">>, CallId}])}]), + + lager:debug("pushing to ~p: ~s: ~p", [Pid, TokenID, Message]), + + fcm:push(Pid, [TokenID], kz_json:to_map(Message)). + +-spec get_fcm(kz_term:api_binary(), ets:tid()) -> kz_term:api_pid(). +get_fcm('undefined', _) -> 'undefined'; +get_fcm(App, ETS) -> + case ets:lookup(ETS, App) of + [] -> maybe_load_fcm(App, ETS); + [{App, Pid}] -> Pid + end. + +-spec maybe_load_fcm(kz_term:api_binary(), ets:tid()) -> kz_term:api_pid(). +maybe_load_fcm(App, ETS) -> + lager:debug("loading fcm secret for ~s", [App]), + maybe_load_fcm(App, ETS, kapps_config:get_binary(?CONFIG_CAT, [<<"firebase">>, <<"api_key">>], 'undefined', App)). + +-spec maybe_load_fcm(kz_term:api_binary(), ets:tid(), kz_term:api_binary()) -> kz_term:api_pid(). +maybe_load_fcm(App, _, 'undefined') -> + lager:debug("firebase pusher api_key for app ~s not found", [App]), + 'undefined'; +maybe_load_fcm(App, ETS, APIKey) -> + case fcm:start(kz_term:to_atom(App, 'true'), APIKey) of + {'ok', Pid} -> + ets:insert(ETS, {App, Pid}), + Pid; + {'error', {'already_started', Pid}} -> + ets:insert(ETS, {App, Pid}), + Pid; + {'error', Reason} -> + lager:error("error loading fcm ~p", [Reason]), + 'undefined' + end. diff --git a/applications/pusher/src/modules/pm_google.erl b/applications/pusher/src/modules/pm_google.erl index 03c34d8671a..5d2a1e0a24e 100644 --- a/applications/pusher/src/modules/pm_google.erl +++ b/applications/pusher/src/modules/pm_google.erl @@ -81,7 +81,7 @@ get_gcm(App, ETS) -> -spec maybe_load_gcm(kz_term:api_binary(), ets:tid()) -> kz_term:api_pid(). maybe_load_gcm(App, ETS) -> lager:debug("loading gcm secret for ~s", [App]), - maybe_load_gcm(App, ETS, kapps_config:get_binary(?CONFIG_CAT, <<"google">>, 'undefined', App)). + maybe_load_gcm(App, ETS, kapps_config:get_binary(?CONFIG_CAT, [<<"google">>, <<"api_key">>], 'undefined', App)). -spec maybe_load_gcm(kz_term:api_binary(), ets:tid(), kz_term:api_binary()) -> kz_term:api_pid(). maybe_load_gcm(App, _, 'undefined') -> diff --git a/applications/pusher/src/pusher.app.src b/applications/pusher/src/pusher.app.src index 778aef625ac..194f6440088 100644 --- a/applications/pusher/src/pusher.app.src +++ b/applications/pusher/src/pusher.app.src @@ -1,7 +1,7 @@ {application,pusher, - [{applications,[apns,gcm,kazoo,kazoo_amqp,kazoo_apps,kazoo_data, - kazoo_sip,kazoo_stdlib,kernel,lager,public_key, - stdlib]}, + [{applications,[apns,fcm,gcm,kazoo,kazoo_amqp,kazoo_apps, + kazoo_data,kazoo_sip,kazoo_stdlib,kernel,lager, + public_key,stdlib]}, {description,"pusher - wake the dead"}, {env,[{is_kazoo_app,true}]}, {mod,{pusher_app,[]}}, diff --git a/make/deps.mk b/make/deps.mk index 3db8bfeed22..5c6308278bb 100644 --- a/make/deps.mk +++ b/make/deps.mk @@ -16,6 +16,7 @@ DEPS = amqp_client \ erlydtl \ escalus \ exml \ + fcm \ folsom \ fs_event \ fs_sync \ @@ -110,3 +111,5 @@ dep_proper = git https://github.com/proper-testing/proper/ v1.3 dep_syslog = git https://github.com/2600hz/erlang-syslog bbad537a1cb5e4f37e672d2e2665659e850662d0 dep_meta = git https://github.com/efcasado/meta 0.1.3 + +dep_fcm = git https://github.com/softwarejoint/fcm-erlang.git b2f68a4c6f0f59475597a35e2dc9be13d9ba2910 diff --git a/make/kz.mk b/make/kz.mk index b2edcb81a07..0cb99893568 100644 --- a/make/kz.mk +++ b/make/kz.mk @@ -39,7 +39,13 @@ else endif ERLC_OPTS += -Iinclude -Isrc -I../ +'{parse_transform, lager_transform}' ## Use pedantic flags when compiling apps from applications/ & core/ -ERLC_OPTS += -Werror +warn_export_all +warn_unused_import +warn_unused_vars +warn_missing_spec +deterministic +ERLC_OPTS += +warn_export_all +warn_unused_import +warn_unused_vars +warn_missing_spec +deterministic + +ifneq (,$(findstring 21._,$(OTP_VERSION))) + ERLC_OPTS += -Werror +endif + + #ERLC_OPTS += +warn_untyped_record ELIBS ?= $(if $(ERL_LIBS),$(ERL_LIBS):)$(ROOT)/deps:$(ROOT)/core:$(ROOT)/applications