Skip to content

Commit

Permalink
feat: v1.2.0 (#109)
Browse files Browse the repository at this point in the history
* chore: update to 1.6 style guide

Might be some weird formatting I may have missed. Will need to double
check before release.

* feat: upgraded to kadabra v0.4.0

APNS.Config `:reconnect` option is now completely ignored. Key should be
removed in whatever next major version.

* feat: support for token based APNS authentication (#116)

* Support for APNS JWT authentication tokens.

Update docs to reflect changes.

* Pigeon.ConfigError: Fixed moduledoc typo.

* fix: APNS.CertConfig moved back to APNS.Config

Required for backwards compatability. Config parsing is now handled in
APNS.ConfigParser, which returns the appropriate struct for the given
opts.

`jwt_` prefix has been removed from the various attributes on JWTConfig.
Still unsure whether or not to remove it for static Mix.Config options.

TODO: Cleanup documentation and tests.

* refactor: move shared config functionality back to APNS.Shared

Necessary to clean up compile warnings.

* test: tests added for APNS JWT configs

Requires the following keys to be set:
- APNS_JWT_KEY (file or plaintext)
- APNS_JWT_KEY_IDENTIFIER
- APNS_JWT_TEAM_ID

TODO: update docs for config_opts

* Alter APNS.Token storage key

Tokens are now stored keyed on the JWTConfig key_identifier and team_id.

* docs: update JWT documentation

Minor fixes included in other parts of the docs. Also updated kadabra to
v0.4.1-- I think unit tests run a little faster now?

* ci: add secrets to travis

* ci: global env instead of matrix

* ci: fix env var?

* ci: use JWT file instead

* fix: elixir 1.4 compatability

`use Agent` doesn't exist in 1.4

* fix: typo (#117)

* chore: update version and changelog
  • Loading branch information
hpopp committed May 25, 2018
1 parent 0a83d53 commit 9721449
Show file tree
Hide file tree
Showing 48 changed files with 1,308 additions and 598 deletions.
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
line_length: 80
]
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
erl_crash.dump
*.ez
*.pem
*.p8
scratchpad.txt
.DS_Store
.iex.exs
22 changes: 12 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
language: elixir
elixir:
- 1.4.5
- 1.5.3
- 1.6.0
- 1.4.5
- 1.5.3
- 1.6.5
otp_release:
- 19.2
- 20.0
- 19.2
- 20.0
before_install:
- openssl aes-256-cbc -K $encrypted_3033c569c247_key -iv
$encrypted_3033c569c247_iv -in cert_key.tar.enc -out
cert_key.tar -d
- tar xvf cert_key.tar
- openssl aes-256-cbc -K $encrypted_3033c569c247_key -iv $encrypted_3033c569c247_iv -in secrets.tar.enc -out secrets.tar -d
- tar xvf secrets.tar
script:
- "MIX_ENV=test mix do deps.get, compile, coveralls.travis"
- MIX_ENV=test mix do deps.get, compile, coveralls.travis
env:
global:
- secure: kd6KF3eMgtaWIQbd/MG8m8L2cR3fqxmgRHWng4fqxRwPH45hP4IVLf2cQctGA9mm6nOV5wrNtys1fQzYPuXAKA/LAYJ/pc66ISdEjKjNpqxIGRCCJsB6lZft5XPgpAJTClwKGpFvEd7cLtz2ir4PBN0JjDyWtMTKHXarKzn7cOQNpRypwc5wWU1dmuqSsNNjr2hk3HUAwSNqB8W960Zqd39lqo0m+/tlq3L8LghcFlibn8XFNlzAmERpUpERkJZPbRplgx+ofigATGaC9Tzuc04WWLUthsPyBJkli2tYs/8Yd1tCjAYz90Tde0E1/Mzj6b4/LW4AGXrb4I5pKZrkkDw0gjqJ1Buu4U2d/TGZc+wU13BD5icpjq7zx8ZZ08N3XmQ6Y2ydd5p9MwCtUx5Dg2d9ybvdeCI1kj4HVcYAxqoBmf12od3fRZ+IwzbpUIvFn8R05wlvhA8nSLGloUceag3qUHJLD/tJOj9GRgR8ubw6hAQz7w7uWJhq3XSvHvdtYw2tfc9pGpjloA2gOhaStHrjJGFpzMFPITCEcQF1B7KraY/FPxMyvQL70l9RhQZ11F+56uwZ9oZFEQeyr5Tr6mPISyp1enz50HjwRrVy4pw5hOaTOCxA1dxrstWPhdKHOyYNtZDMFopn8yAIGzPE3z6UQ4bABqIlz8tpEEy13Eg=
- secure: f7LJkl/HyS/YghhADhVSNZjOsYkzuWXL9kqYAqjTxzB4g/2HPxkyjGjtlUHv82MvjvoIs7icDNjIveVf0ZP/is5t5T88HXfobYIW1H4hexXbN94oLJTaehdlkv0Fdi9Rfm7sBBH+tyd9HwS25R9MMSndRkxcnr78huFP8iWgRFFF4dDHyh8Dozzo+tHbrPPJtt8iJPz1Fs8U3nMQOdwHj8cA3fZXg65S7IzeavGAnkxtUDV22FgJd0PLD2i2RHAXAxjpmjLY3GIZDquSAO9DWJGElNRphg91nIT726vylm6IZ0JQ0H13hXbVfD4akYYb9ipL8CS/FzZexArtd1cg/nJKSz4G6AJyfByw0KqJwUgaPhvhlCeG8RDse4CH9dH7d8ODbUpxkcvh3Z31EUECY/2BC7Ma4ieLC4VAMBBgc21XxMPklt5z+wW8v2mblVudTSIRnXLL3QiiM2SHV0PLHyH7TPGiKU92J5HrhT3cKkrUMwcePLb6GUBKg6tSiF52DEiiGXWDWbj3rMEnwRbvAa5QkHNhVLwre1tsOt8nCX6cK3rY5fygR3W7lfzMQGMs/XzMdMaqFamJ0YRqUMh2msL0SdzltqFsXlS80JWS8U3zM1IlS448/qmhHzDjyV5mW/bd5iC7uDPQ/5rJfSaicfS1pNNp83Ufg7byNVaIbL4=
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# Changelog

## v1.2.0
* Support for APNS JWT configuration
* Bump `kadabra` dependency to `v0.4.2`

## v1.1.6
* Relax `gen_stage` dependency to `~> 0.12`
* Bump `kadabra` dependency to 0.3.7
* Bump `kadabra` dependency to `v0.3.7`

## v1.1.5
* Fix: relax `httpoison` dependency to allow `0.x` or `1.0`
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ Add pigeon and kadabra as `mix.exs` dependencies:
```elixir
def deps do
[
{:pigeon, "~> 1.1.6"},
{:kadabra, "~> 0.3.7"}
{:pigeon, "~> 1.2.0"},
{:kadabra, "~> 0.4.2"}
]
end
```
Expand All @@ -39,6 +39,22 @@ Add pigeon and kadabra as `mix.exs` dependencies:
* Full-text string of the file contents
* `{:my_app, "certs/cert.pem"}` (indicates path relative to the `priv` folder of the given application)

Alternatively, you can use token based authentication:

```elixir
config :pigeon, :apns,
apns_default: %{
key: "AuthKey.p8",
key_identifier: "ABC1234567",
team_id: "DEF8901234",
mode: :dev
}
```

* `:key` - Created and downloaded via your developer account. Like `:cert` this can be a file path, file contents string or tuple
* `:key_identifier` - The 10-character key identifier associated with `:key`, obtained from your developer account
* `:team_id` - Your 10-character Team ID, obtained from your developer account

2. Create a notification packet. **Note: Your push topic is generally the app's bundle identifier.**

```elixir
Expand Down
Binary file removed cert_key.tar.enc
Binary file not shown.
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
use Mix.Config
import_config "#{Mix.env}.exs"
import_config "#{Mix.env()}.exs"
19 changes: 14 additions & 5 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ config :pigeon, :test,
apns_key: "key_unencrypted.pem",
apns_topic: System.get_env("APNS_TOPIC")

config :pigeon, workers: [
{Pigeon.TestConfig, :apns_dynamic},
{Pigeon.TestConfig, :fcm_dynamic},
{Pigeon.TestConfig, :adm_dynamic}
]
config :pigeon,
debug_log: true,
workers: [
{Pigeon.TestConfig, :apns_dynamic},
{Pigeon.TestConfig, :apns_jwt_dynamic},
{Pigeon.TestConfig, :fcm_dynamic},
{Pigeon.TestConfig, :adm_dynamic}
]

config :pigeon, :fcm,
fcm_default: %{
Expand All @@ -24,6 +27,12 @@ config :pigeon, :apns,
cert: "cert.pem",
key: "key_unencrypted.pem",
mode: :dev
},
apns_jwt_static: %{
key: "AuthKey.p8",
key_identifier: System.get_env("APNS_JWT_KEY_IDENTIFIER"),
team_id: System.get_env("APNS_JWT_TEAM_ID"),
mode: :dev
}

config :pigeon, :adm,
Expand Down
16 changes: 16 additions & 0 deletions docs/APNS Apple iOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@
* Full-text string of the file contents (useful for environment variables)
* `{:my_app, "certs/cert.pem"}` (indicates path relative to the `priv` folder of the given application)

Alternatively, you can use token based authentication:

```elixir
config :pigeon, :apns,
apns_default: %{
key: "AuthKey.p8",
key_identifier: "ABC1234567",
team_id: "DEF8901234",
mode: :dev
}
```

* `:key` - Created and downloaded via your developer account. Like `:cert` this can be a file path, file contents string or tuple
* `:key_identifier` - The 10-character key identifier associated with `:key`, obtained from your developer account
* `:team_id` - Your 10-character Team ID, obtained from your developer account

2. Create a notification packet. **Note: Your push topic is generally the app's bundle identifier.**

```elixir
Expand Down
10 changes: 5 additions & 5 deletions docs/Getting Started.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
> HTTP2-compliant wrapper for sending iOS and Android push notifications.
[![Build Status](https://travis-ci.org/codedge-llc/pigeon.svg?branch=master)](https://travis-ci.org/codedge-llc/pigeon)
[![Coverage Status](https://coveralls.io/repos/github/codedge-llc/pigeon/badge.svg?branch=v1.1.0)](https://coveralls.io/github/codedge-llc/pigeon)
[![Hex.pm](http://img.shields.io/hexpm/v/pigeon.svg)](https://hex.pm/packages/pigeon) [![Hex.pm](http://img.shields.io/hexpm/dt/pigeon.svg)](https://hex.pm/packages/pigeon)
[![Deps Status](https://beta.hexfaktor.org/badge/all/github/codedge-llc/pigeon.svg)](https://beta.hexfaktor.org/github/codedge-llc/pigeon)
[![Coverage Status](https://coveralls.io/repos/github/codedge-llc/pigeon/badge.svg)](https://coveralls.io/github/codedge-llc/pigeon)
[![Hex.pm](http://img.shields.io/hexpm/v/pigeon.svg)](https://hex.pm/packages/pigeon)
[![Hex.pm](http://img.shields.io/hexpm/dt/pigeon.svg)](https://hex.pm/packages/pigeon)

## Installation

Add pigeon and kadabra as `mix.exs` dependencies:
```elixir
def deps do
[
{:pigeon, "~> 1.1.6"},
{:kadabra, "~> 0.3.7"}
{:pigeon, "~> 1.2.0"},
{:kadabra, "~> 0.4.2"}
]
end
```
Expand Down
35 changes: 24 additions & 11 deletions lib/pigeon.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,19 @@ defmodule Pigeon do
end

defp workers do
adm_workers()
++ apns_workers()
++ fcm_workers()
++ env_workers()
++ task_supervisors()
[
adm_workers(),
apns_workers(),
fcm_workers(),
env_workers(),
apns_token_agent(),
task_supervisors()
]
|> List.flatten()
end

defp apns_token_agent do
[worker(APNS.Token, [%{}], restart: :permanent, shutdown: 5_000)]
end

defp task_supervisors do
Expand All @@ -32,9 +40,11 @@ defmodule Pigeon do

defp env_workers do
case Application.get_env(:pigeon, :workers) do
nil -> []
nil ->
[]

workers ->
Enum.map(workers, fn({mod, fun}) ->
Enum.map(workers, fn {mod, fun} ->
config = apply(mod, fun, [])
worker(config)
end)
Expand All @@ -44,6 +54,7 @@ defmodule Pigeon do
defp worker(%ADM.Config{} = config) do
worker(ADM.Worker, [config], id: config.name, restart: :temporary)
end

defp worker(config) do
worker(Pigeon.Worker, [config], id: config.name, restart: :temporary)
end
Expand All @@ -53,7 +64,7 @@ defmodule Pigeon do
end

defp apns_workers do
workers_for(:apns, &APNS.Config.new/1, Pigeon.Worker)
workers_for(:apns, &APNS.ConfigParser.parse/1, Pigeon.Worker)
end

defp fcm_workers do
Expand All @@ -62,9 +73,11 @@ defmodule Pigeon do

defp workers_for(name, config_fn, mod) do
case Application.get_env(:pigeon, name) do
nil -> []
nil ->
[]

workers ->
Enum.map(workers, fn({worker_name, _config}) ->
Enum.map(workers, fn {worker_name, _config} ->
config = config_fn.(worker_name)
worker(mod, [config], id: config.name, restart: :temporary)
end)
Expand All @@ -73,7 +86,7 @@ defmodule Pigeon do

@doc false
def start_connection(state) do
opts = [restart: :temporary, id: :erlang.make_ref]
opts = [restart: :temporary, id: :erlang.make_ref()]
spec = worker(Pigeon.Connection, [state], opts)
Supervisor.start_child(:pigeon, spec)
end
Expand Down
49 changes: 28 additions & 21 deletions lib/pigeon/adm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule Pigeon.ADM do
n = Pigeon.ADM.Notification.new("token", %{"message" => "test"})
Pigeon.ADM.push(n, on_response: handler)
"""
@type on_response :: ((Notification.t) -> no_return)
@type on_response :: (Notification.t() -> no_return)

@typedoc ~S"""
Options for sending push notifications.
Expand All @@ -36,12 +36,13 @@ defmodule Pigeon.ADM do
See `t:on_response/0`
"""
@type push_opts :: [
to: atom | pid | nil,
on_response: on_response | nil
]
to: atom | pid | nil,
on_response: on_response | nil
]

@type connection_response :: {:ok, pid}
| {:error, {:already_started, pid}}
@type connection_response ::
{:ok, pid}
| {:error, {:already_started, pid}}

@default_timeout 5_000

Expand Down Expand Up @@ -87,28 +88,30 @@ defmodule Pigeon.ADM do
iex> notif.response
:timeout
"""
@spec push(Notification.t | [Notification.t], Keyword.t) :: no_return
@spec push(Notification.t() | [Notification.t()], Keyword.t()) :: no_return
def push(notifications, opts \\ [])

def push(notifications, opts) when is_list(notifications) do
worker_name = opts[:to] || Config.default_name
worker_name = opts[:to] || Config.default_name()

if Keyword.has_key?(opts, :on_response) do
cast_push(worker_name, notifications, opts[:on_response])
else
notifications
|> Enum.map(& Task.async(fn -> sync_push(worker_name, &1) end))
|> Enum.map(&Task.async(fn -> sync_push(worker_name, &1) end))
|> Task.yield_many(@default_timeout + 500)
|> Enum.map(fn {task, response} ->
case response do
nil -> Task.shutdown(task, :brutal_kill)
{:ok, resp} -> resp
_error -> nil
end
end)
case response do
nil -> Task.shutdown(task, :brutal_kill)
{:ok, resp} -> resp
_error -> nil
end
end)
end
end

def push(notification, opts) do
worker_name = opts[:to] || Config.default_name
worker_name = opts[:to] || Config.default_name()

if Keyword.has_key?(opts, :on_response) do
cast_push(worker_name, notification, opts[:on_response])
Expand All @@ -117,17 +120,19 @@ defmodule Pigeon.ADM do
end
end

defp cast_push(worker_name, notifications, on_response) when is_list(notifications) do
defp cast_push(worker_name, notifications, on_response)
when is_list(notifications) do
for n <- notifications, do: cast_push(worker_name, n, on_response)
end

defp cast_push(worker_name, notification, on_response) do
GenServer.cast(worker_name, {:push, :adm, notification, on_response})
end

defp sync_push(worker_name, notification) do
pid = self()
ref = :erlang.make_ref
on_response = fn(x) -> send pid, {ref, x} end
ref = :erlang.make_ref()
on_response = fn x -> send(pid, {ref, x}) end

GenServer.cast(worker_name, {:push, :adm, notification, on_response})

Expand All @@ -148,17 +153,19 @@ defmodule Pigeon.ADM do
iex> Process.alive?(pid)
true
"""
@spec start_connection(atom | Config.t | Keyword.t) :: connection_response
@spec start_connection(atom | Config.t() | Keyword.t()) :: connection_response
def start_connection(name) when is_atom(name) do
config = Config.new(name)
Supervisor.start_child(:pigeon, worker(Worker, [config], id: name))
end

def start_connection(%Config{} = config) do
Worker.start_link(config)
end

def start_connection(opts) when is_list(opts) do
opts
|> Config.new
|> Config.new()
|> start_connection()
end

Expand Down
Loading

0 comments on commit 9721449

Please sign in to comment.