[![Hex.pm](https://img.shields.io/hexpm/v/express.svg)](https://hex.pm/packages/express) [![API Docs](https://img.shields.io/badge/api-docs-yellow.svg?style=flat)](http://hexdocs.pm/express/) [![Build Status](https://travis-ci.org/madeinussr/express.svg?branch=master)](https://travis-ci.org/madeinussr/express)
# 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
```elixir
# 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
```elixir
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
```elixir
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
```elixir
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:
```elixir
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)_
```elixir
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:
```elixir
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
```elixir
%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
```elixir
%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)
```elixir
# 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
```elixir
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.