Express
Library for sending push notifications. Supports Apple APNS and Google FCM services.
At the moment sends pushes to FCM via HTTP and to APNS via HTTP/2 (with either certificate or JWT).
Uses GenServer in order to balance the load. Default buffer (producer) size is 5000. Default consumer max demand is number_of_available_schedulers * 5 (multiplier can be adjusted).
Installation
# in your mix.exs file
def deps do
{:express, "~> 1.1.3"}
end
# in your config.exs file (more in configuration section below)
config :express,
apns: [
mode: :prod,
cert_path: System.get_env("EXPRESS_APNS_CERT_PATH"),
key_path: System.get_env("EXPRESS_APNS_KEY_PATH")
],
fcm: [
api_key: System.get_env("EXPRESS_FCM_API_KEY")
]
Quick examples
APNS
alias Express.APNS
push_message =
%APNS.PushMessage{
token: "your_device_token",
topic: "your_app_topic",
acme: %{},
aps: %APNS.PushMessage.Aps{
badge: 1,
content_available: 1,
alert: %APNS.PushMessage.Alert{
title: "Hello",
body: "World"
}
}
}
opts = [delay: 5] # in seconds
callback_fun =
fn(push_message, response) ->
IO.inspect("==Push message==")
IO.inspect(push_message)
IO.inspect("==APNS response==")
IO.inspect(response)
end
APNS.push(push_message, opts, callback_fun)
FCM
alias Express.FCM
push_message =
%FCM.PushMessage{
to: "your_device_registration_id",
priority: "high",
content_available: true,
data: %{},
notification: %FCM.PushMessage.Notification{
title: "Hello",
body: "World"
}
}
opts = [delay: 5] # in seconds
callback_fun =
fn(push_message, response) ->
IO.inspect("==Push message==")
IO.inspect(push_message)
IO.inspect("==FCM response==")
IO.inspect(response)
end
FCM.push(push_message, opts, callback_fun)
Configuration
Most of options can be changed in config file, but try to start with defaults. Every config option (except basics) has a default value.
Basic
config :express,
apns: [
mode: :prod,
cert_path: System.get_env("EXPRESS_APNS_CERT_PATH"),
key_path: System.get_env("EXPRESS_APNS_KEY_PATH")
],
fcm: [
api_key: System.get_env("EXPRESS_FCM_API_KEY")
]
There is an option:
for APNS you can provide also cert
and key
options (values/content of your certificate and key files respectively) the priority of the options is: cert_path > cert
and key_path > key
Buffer
There are all possible options for the buffer:
config :express,
buffer: [
max_size: 5000,
consumers_count: 5,
consumer_demand_multiplier: 5,
adders_pool_config: [
{:name, {:local, :buffer_adders_pool}},
{:worker_module, Express.PushRequests.Adder},
{:size, 5},
{:max_overflow, 1}
]
]
APNS
Possible options for APNS:
you should provide either (cert_path & key_path) or (cert & key) or (key_id & team_id & auth_key_path)
config :express,
apns: [
mode: :prod,
# for requests with jwt
key_id: System.get_env("EXPRESS_APNS_KEY_ID"),
team_id: System.get_env("EXPRESS_APNS_TEAM_ID"),
auth_key_path: System.get_env("EXPRESS_APNS_AUTH_KEY_PATH"),
# for requests with a certificate
cert_path: System.get_env("EXPRESS_APNS_CERT_PATH"),
key_path: System.get_env("EXPRESS_APNS_KEY_PATH"),
# workers config (if default doesn't meet you requirements)
workers_pool_config: [
{:name, {:local, :apns_workers_pool}},
{:worker_module, Express.APNS.Worker},
{:size, 8},
{:max_overflow, 1}
]
]
FCM
Possible options for FCM:
config :express,
fcm: [
api_key: System.get_env("EXPRESS_FCM_API_KEY")
# workers config (if default doesn't meet you requirements)
workers_pool_config: [
{:name, {:local, :fcm_workers_pool}},
{:worker_module, Express.FCM.Worker},
{:size, 8},
{:max_overflow, 1}
]
]
Push message structure
You should construct %Express.APNS.PushMessage{}
and %Express.FCM.PushMessage{}
structures and pass them to Express.APNS.push/3
and Express.FCM.push/3
respectively
in order to send a push message.
Express’s Express.APNS.PushMessage
as well as Express.FCM.PushMessage
conforms official
Apple & Google push message structures, so there should not be any confusion with it.
Here are their structures:
APNS
%Express.APNS.PushMessage{
token: String.t,
topic: String.t,
aps: Express.APNS.PushMessage.Aps.t,
apple_watch: map(),
acme: map()
}
%Express.APNS.PushMessage.Aps{
content_available: pos_integer(),
mutable_content: pos_integer(),
badge: pos_integer(),
sound: String.t,
category: String.t,
thread_id: String.t,
alert: Express.APNS.PushMessage.Alert.t | String.t
}
%Express.APNS.PushMessage.Alert{
title: String.t,
body: String.t
}
FCM
%Express.FCM.PushMessage{
to: String.t,
registration_ids: [String.t],
priority: String.t,
content_available: boolean(),
collapse_key: String.t,
data: map(),
notification: PushMessage.Notification.t
}
%Express.FCM.PushMessage.Notification{
title: String.t,
body: String.t,
icon: String.t,
sound: String.t,
click_action: String.t,
badge: pos_integer(),
category: String.t
}
Send a push message
In order to send a push message you should to construct a valid message structure,
define a callback function, which will be invoked on provider’s response (APNS or FCM)
and pass them along with options to either Express.FCM.push/3
or Express.APNS.push/3
function (see quick examples above).
Nothing to add here, but:
a callback function has to take two arguments:
- a push message (which push message structure you tried to send)
- a push result (response received from a provider and handled by Express)
# push result type
@type push_result :: {:ok, %{status: pos_integer(), body: any()}} |
{:error, %{status: pos_integer(), body: any()}}
at this moment the single option you can pass with
opts
argument -delay
- it defines a delay in seconds for a push worker (a worker will push a message after that delay)
Supervision tree
Application
|
Supervisor
|
-----------------------------------------------------------------------
| | | |
APNS.Supervisor FCM.Supervisor PushRequests.Supervisor TasksSupervisor
| | | |
| ------------------------- | ------------------------
| | | | | | |
| FCM.DelayedPushes :fcm_workers_pool | Task Task Task
| | |
| ------------------------ |
| | | | |
| FCM.Worker FCM.Worker FCM.Worker |
| |
| ----------------------------------------
| | | |
| PushRequests.Buffer :buffer_adders_pool PushRequests.ConsumersSupervisor
| | |
| ------------- ----------------
| | | | |
| | |PushRequests.Consumer PushRequests.Consumer
| | |
| PushRequests.Adder PushRequests.Adder
|
---------------------------------------------
| | |
APNS.JWTHolder APNS.DelayedPushes :apns_workers_pool
|
--------------------------------------
| | |
APNS.Worker APNS.Worker APNS.Worker
| | |
APNS.Connection APNS.Connection APNS.Connection
LICENSE
Copyright © 2017 Andrey Chernykh ( andrei.chernykh@gmail.com )
This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the LICENSE file for more details.