Skip to content

Commit

Permalink
Add Pushy (pushy.me) support
Browse files Browse the repository at this point in the history
  • Loading branch information
rslota committed Jun 25, 2019
1 parent b9f5213 commit 6469f96
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 14 deletions.
43 changes: 39 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ notifications** to `FCM` (Firebase Cloud Messaging) and/or

#### Running from DockerHub

We provide already built MongoosePush images. If you just want to use it, then all you need is `docker`, `FCM` app token and/or `APNS` app certificates.
We provide already built MongoosePush images. If you just want to use it, then all you need is `docker`, `FCM` app token and/or `APNS` app certificates and/or `Pushy` app token.
In case of certificates you need to setup the following directory structure:
* priv/
* ssl/
Expand All @@ -24,16 +24,23 @@ In case of certificates you need to setup the following directory structure:
* dev_cert.pem - Development APNS app certificate
* dev_key.pem - Development APNS app certificate's private key (has to be unencrypted)

Assuming that your `FCM` app token is "MY_FCM_SECRET_TOKEN" and you have the `priv` directory with all ceriticates in current directory, then you may start MongoosePush with the following command:
Assuming that your `FCM` app token is "MY_FCM_SECRET_TOKEN", `Pushy` app token is "MY_PUSHY_SECRET_TOKEN" and you have the `priv` directory with all ceriticates in current directory, then you may start MongoosePush with the following command:

```bash
docker run -v `pwd`/priv:/opt/app/priv \
-e PUSH_FCM_ENABLED=true \
-e PUSH_FCM_APP_KEY="MY_FCM_SECRET_TOKEN" \
-e PUSH_PUSHY_ENABLED=true \
-e PUSH_PUSHY_APP_KEY="MY_PUSHY_SECRET_TOKEN" \
-e PUSH_APNS_ENABLED=true \
-e PUSH_HTTPS_CERTFILE="/opt/app/priv/ssl/rest_cert.pem" \
-e PUSH_HTTPS_KEYFILE="/opt/app/priv/ssl/rest_key.pem" \
-it --rm mongooseim/mongoose-push:latest
```

Please note that each push service that is being used has to be enabled by `PUSH_FCM_ENABLED` / `PUSH_APNS_ENABLED` / `PUSH_PUSHY_ENABLED` as
all services are disabled by default. In the example above, we enable all push services, but you may want to skip some of them.

#### Building

Building docker is really easy, just type:
Expand Down Expand Up @@ -64,14 +71,20 @@ Environmental variables to configure production release:

##### General settings:
* `PUSH_LOGLEVEL` - `debug`/`info`/`warn`/`error` - Log level of the application. `info` is the default one
* `PUSH_FCM_ENABLED` - `true`/`false` - Enable or disable `FCM` support. Enabled by default
* `PUSH_APNS_ENABLED` - `true`/`false` - Enable or disable `APNS` support. Enabled by default
* `PUSH_FCM_ENABLED` - `true`/`false` - Enable or disable `FCM` support. Disabled by default
* `PUSH_APNS_ENABLED` - `true`/`false` - Enable or disable `APNS` support. Disabled by default
* `PUSH_PUSHY_ENABLED` - `true`/`false` - Enable or disable `Pushy` support. Disabled by default

##### Settings for FCM service:
* `PUSH_FCM_ENDPOINT` - Hostname of `FCM` service. Set only for local testing. By default this option points to the Google's official hostname
* `PUSH_FCM_APP_KEY` - App key token to use with `FCM` service
* `PUSH_FCM_POOL_SIZE` - Connection pool size for `FCM` service

##### Settings for Pushy service:
* `PUSH_PUSHY_ENDPOINT` - Hostname of `Pushy` service. Set only for local testing. By default this option points to the Google's official hostname
* `PUSH_PUSHY_APP_KEY` - App key token to use with `Pushy` service
* `PUSH_PUSHY_POOL_SIZE` - Connection pool size for `Pushy` service. Please note that `Pushy` uses HTTP 1.1, so the pool size has be be significantly bigger then in case of other services that run on HTTP/2

##### Settings for development APNS service:
* `PUSH_APNS_DEV_ENDPOINT` - Hostname of `APNS` service. Set only for local testing. By default this option points to the Apple's official hostname
* `PUSH_APNS_DEV_CERT` - Path Apple's development certfile used to communicate with `APNS`
Expand Down Expand Up @@ -159,6 +172,28 @@ Each `FCM` pool may be configured by setting the following fields:

You may entirely skip the `FCM` config entry to disable `FCM` support.

### Pushy configuration
Lets take a look at sample `Pushy` service configuration:
```elixir
config :mongoose_push, pushy: [
default: [
key: "fake_app_key",
pool_size: 50,
mode: :prod
]
]
```

This is a definition of a pool - each pool has a name and configuration. It is possible to have multiple named pools with different configuration, which includes pool size, environment mode etc. Currently the only reason you may want to do this, is to create separate production and development pools which may be selected by an `HTTP` client by specifying matching `:mode` in their push request.

Each `Pushy` pool may be configured by setting the following fields:
* **key** (*required*) - you `Pushy` Server Key
* **pool_size** (*required*) - maximum number of used `HTTP 1.1` connections to Pushy service
* **mode** (*either `:prod` or `:dev`*) - pool's mode. The `HTTP` client may select pool used to push a notification by specifying matching option in the request
* **endpoint** (*optional*) - URL override for `Pushy` service. Useful mainly in tests

You may entirely skip the `Pushy` config entry to disable `Pushy` support.

### APNS configuration

Lets take a look at sample `APNS` service configuration:
Expand Down
17 changes: 15 additions & 2 deletions config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ config :maru, MongoosePush.Router,

config :mongoose_push, loglevel:
{:system, :atom, "PUSH_LOGLEVEL", :info}

config :mongoose_push, pushy_enabled:
{:system, :boolean, "PUSH_PUSHY_ENABLED", false}

config :mongoose_push, fcm_enabled:
{:system, :boolean, "PUSH_FCM_ENABLED", true}
{:system, :boolean, "PUSH_FCM_ENABLED", false}

config :mongoose_push, apns_enabled:
{:system, :boolean, "PUSH_APNS_ENABLED", true}
{:system, :boolean, "PUSH_APNS_ENABLED", false}

config :mongoose_push, fcm: [
default: [
Expand All @@ -32,6 +36,15 @@ config :mongoose_push, fcm: [
]
]

config :mongoose_push, pushy: [
default: [
endpoint: {:system, :string, "PUSH_PUSHY_ENDPOINT", nil},
key: {:system, :string, "PUSH_PUSHY_APP_KEY", "fake_app_key"},
pool_size: {:system, :integer, "PUSH_PUSHY_POOL_SIZE", 100},
mode: :prod,
]
]

config :mongoose_push, apns: [
dev: [
endpoint: {:system, :string, "PUSH_APNS_DEV_ENDPOINT", nil},
Expand Down
3 changes: 3 additions & 0 deletions lib/mongoose_push/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ defmodule MongoosePush.API do
{500, %{:details => reason}}
end
end
def to_status({:error, reason}) when is_binary(reason) do
{400, reason}
end
def to_status({:error, _reason}) do
{500, nil}
end
Expand Down
2 changes: 1 addition & 1 deletion lib/mongoose_push/api/v1.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule MongoosePush.API.V1 do
parsers: [:urlencoded, :json, :multipart]

params do
requires :service, type: Atom, values: [:fcm, :apns]
requires :service, type: Atom, values: [:fcm, :apns, :pushy]
requires :body, type: String
requires :title, type: String
optional :badge, type: Integer
Expand Down
2 changes: 1 addition & 1 deletion lib/mongoose_push/api/v2.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule MongoosePush.API.V2 do
parsers: [:urlencoded, :json, :multipart]

params do
requires :service, type: Atom, values: [:fcm, :apns]
requires :service, type: Atom, values: [:fcm, :apns, :pushy]
optional :mode, type: Atom, values: [:prod, :dev]
optional :priority, type: Atom, values: [:normal, :high]
optional :time_to_live, type: Integer
Expand Down
5 changes: 3 additions & 2 deletions lib/mongoose_push/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ defmodule MongoosePush.Application do
def services do
[
fcm: MongoosePush.Service.FCM,
apns: MongoosePush.Service.APNS
apns: MongoosePush.Service.APNS,
pushy: MongoosePush.Service.Pushy
]
end

Expand All @@ -68,7 +69,7 @@ defmodule MongoosePush.Application do
case service do
:apns ->
[:cert, :key]
:fcm ->
_ ->
[]
end
config
Expand Down
6 changes: 6 additions & 0 deletions lib/mongoose_push/pools.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ defmodule MongoosePush.Pools do
Enum.map(config, &(elem(&1, 0)))
end

@spec pools_by_mode(MongoosePush.service, MongoosePush.mode) :: list(atom)
def pools_by_mode(:pushy = service, _mode) do
config = pools_config(service)
Enum.map(config, &(elem(&1, 0)))
end

def pools_by_mode(:apns = service, mode) do
config = pools_config(service)

Expand Down
2 changes: 2 additions & 0 deletions lib/mongoose_push/service/fcm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ defmodule MongoosePush.Service.FCM do
|> Enum.reduce(%{}, fn(field, map) ->
Map.put(map, field, alert[field])
end)
|> Enum.filter(fn({_, value}) -> value != nil end)
|> Map.new()

Notification.new(device_id, msg, request[:data])
|> Notification.put_priority(@priority_mapping[request[:priority]])
Expand Down
45 changes: 45 additions & 0 deletions lib/mongoose_push/service/pushy.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
defmodule MongoosePush.Service.Pushy do
@moduledoc """
Pushy service provider implementation.
"""

@behaviour MongoosePush.Service
alias MongoosePush.Pools
require Logger

@spec prepare_notification(String.t(), MongoosePush.request) ::
Service.notification
def prepare_notification(device_id, request) do
MongoosePush.Service.FCM.prepare_notification(device_id, request)
end

@spec push(Service.notification(), String.t(), atom(), Service.options()) ::
:ok | {:error, term}
def push(notification, device_id, worker, opts \\ []) do
MongoosePush.Service.FCM.push(notification, device_id, worker, opts)
end

@spec workers({atom, Keyword.t()} | nil) :: list(Supervisor.Spec.spec())
def workers(nil), do: []
def workers({pool_name, pool_config}) do
Logger.info ~s"Starting Pushy pool with API key #{filter_secret(pool_config[:key])}"
pool_size = pool_config[:pool_size]
Enum.map(1..pool_size, fn(id) ->
worker_name = Pools.worker_name(:pushy, pool_name, id)
Supervisor.Spec.worker(Pigeon.PushyWorker,
[worker_name, pool_config], [id: worker_name])
end)
end

defp filter_secret(secret) when is_binary(secret) do
prefix = String.slice(secret, 0..2)
suffix =
secret
|> String.slice(3..-1)
|> String.slice(-3..-1)

prefix <> "*******" <> suffix
end
defp filter_secret(secret), do: secret

end
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule MongoosePush.Mixfile do

defp deps do
[
{:pigeon, github: "rslota/pigeon", ref: "2860eee35b58e2d8674f805f1151f57b9faeca21"},
{:pigeon, github: "rslota/pigeon", ref: "fa7fa5e"},
{:chatterbox, github: "joedevivo/chatterbox", ref: "ff0c2e0", override: true},

{:maru, github: "rslota/maru", ref: "54fc038", override: true},
Expand All @@ -51,7 +51,7 @@ defmodule MongoosePush.Mixfile do
# Until eproxus/meck #fcc551e3 is in a release, we need to use master version
# to include this commit (fixes mocking in Erlang 20.x + Elixir 1.5.x)
{:meck, github: "eproxus/meck", override: true},
{:httpoison, "~> 0.13"},
{:httpoison, "~> 1.4"},
{:excoveralls, "~> 0.7", only: :test},
{:dialyxir, "~> 0.4", only: [:dev, :test], runtime: false},
{:credo, "~> 0.5", only: [:dev, :test]},
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"},
"hackney": {:hex, :hackney, "1.11.0", "4951ee019df102492dabba66a09e305f61919a8a183a7860236c0fde586134b6", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"hpack": {:git, "https://github.com/joedevivo/hpack.git", "6b58b6231e9b6ab83096715120578976f72f4f7c", [tag: "0.2.3"]},
"httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "1.5.1", "0f55b5b673b03c5c327dac7015a67cb571b99b631acc0bc1b0b98dcd6b9f2104", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"hut": {:hex, :hut, "1.2.0", "0089df0faa2827c605bbada88153f24fff5ea7a4be32ecf0250a7fdc2719cafb", [:"erlang.mk", :rebar, :rebar3], [], "hexpm"},
"idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
Expand All @@ -37,7 +37,7 @@
"mix_docker": {:hex, :mix_docker, "0.5.0", "c7ad34008c43d4a949d69721f39c4d2a2afc509c179926a683117ea8dff8af59", [:mix], [{:distillery, "~> 1.2", [hex: :distillery, repo: "hexpm", optional: false]}], "hexpm"},
"mock": {:hex, :mock, "0.3.1", "994f00150f79a0ea50dc9d86134cd9ebd0d177ad60bd04d1e46336cdfdb98ff9", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.1.0", "1bad3b959941cc53ffd6f4769a5d2666f9cdf179b2d62826636497d3fbad9ec0", [:rebar3], [], "hexpm"},
"pigeon": {:git, "https://github.com/rslota/pigeon.git", "2860eee35b58e2d8674f805f1151f57b9faeca21", [ref: "2860eee35b58e2d8674f805f1151f57b9faeca21"]},
"pigeon": {:git, "https://github.com/rslota/pigeon.git", "fa7fa5e8686477c1ce19a7592bf4f13d994bc0f2", [ref: "fa7fa5e"]},
"plug": {:hex, :plug, "1.5.0", "224b25b4039bedc1eac149fb52ed456770b9678bbf0349cdd810460e1e09195b", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
"pobox": {:hex, :pobox, "1.0.4", "e695bc3b2b547dd6c8e5a19cb743396e6670e306ad3ff498844738f9418bd686", [:rebar3], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
Expand Down

0 comments on commit 6469f96

Please sign in to comment.