diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b1bc852..9da6f00d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,90 +1,111 @@ # Changelog # v1.5.1 -* Fixed various typespecs. + +- Added APNS InvalidPushType error ([#172](https://github.com/codedge-llc/pigeon/pull/172)). +- Fixed various typespecs ([#170](https://github.com/codedge-llc/pigeon/pull/170)). ## v1.5.0 -* Bumped minimum Elixir version to 1.6 -* Raise `Pigeon.ConfigError` when booting invalid config structs. + +- Bumped minimum Elixir version to 1.6 +- Raise `Pigeon.ConfigError` when booting invalid config structs. See below for validated keys and error types. -* `APNS.JWTConfig` now validates key p8 content before connecting. -* Relaxed `gen_stage` dependency to allow `~> 1.0` +- `APNS.JWTConfig` now validates key p8 content before connecting. +- Relaxed `gen_stage` dependency to allow `~> 1.0` Validated config keys: + - `ADM.Config` - `:client_id`, `:client_secret` - `APNS.Config` - `:cert`, `:key` - `APNS.JWTConfig` - `:team_id`, `:key`, `:key_identifier` - `FCM.Config` - `:key` Possible error values: - - `{:error, {:invalid, value}}` - - `{:error, {:nofile, value}}` + +- `{:error, {:invalid, value}}` +- `{:error, {:nofile, value}}` ## v1.4.0 -* `apns-push-type` header support for iOS 13. An additional `:push_type` key has been + +- `apns-push-type` header support for iOS 13. An additional `:push_type` key has been added to the `APNS.Notification` struct. ## v1.3.2 -* Document workers configuration for run-time configuration of push workers. -* Modify run-time configuration of push workers so that multiple (or no) + +- Document workers configuration for run-time configuration of push workers. +- Modify run-time configuration of push workers so that multiple (or no) workers may be returned by the startup configuration. ## v1.3.1 -* Joken dependency bumped to 2.1 + +- Joken dependency bumped to 2.1 ## v1.3.0 -* Support for FCM `content_available`, `mutable_content`, and `condition` keys -* Set `priority` of APNS notifications -* Joken dependency bumped to 2.0.1 + +- Support for FCM `content_available`, `mutable_content`, and `condition` keys +- Set `priority` of APNS notifications +- Joken dependency bumped to 2.0.1 ## v1.2.4 -* Fixed ADM handling of connection timeouts + +- Fixed ADM handling of connection timeouts ## v1.2.3 -* Fixed APNS, FCM and ADM error response parse crashes. Error responses not + +- Fixed APNS, FCM and ADM error response parse crashes. Error responses not listed in the documentation are returned as `:unknown_error` ## v1.2.2 -* Fixed APNS handling of notification `expiration` -* Added APNS support for `collapse_id` + +- Fixed APNS handling of notification `expiration` +- Added APNS support for `collapse_id` ## v1.2.1 -* FCM notifications can now handle `time_to_live`, `collapse_key`, `restricted_package_name` + +- FCM notifications can now handle `time_to_live`, `collapse_key`, `restricted_package_name` and `dry_run` keys ## v1.2.0 -* Support for APNS JWT configuration -* Bump `kadabra` dependency to `v0.4.2` + +- 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 `v0.3.7` + +- Relax `gen_stage` dependency to `~> 0.12` +- Bump `kadabra` dependency to `v0.3.7` ## v1.1.5 -* Fix: relax `httpoison` dependency to allow `0.x` or `1.0` + +- Fix: relax `httpoison` dependency to allow `0.x` or `1.0` ## v1.1.4 -* Fix: `:on_response` callbacks spawned as supervised task instead of running + +- Fix: `:on_response` callbacks spawned as supervised task instead of running in the `Worker` process -* Fix: ADM token refresh failure returns updated notification instead of +- Fix: ADM token refresh failure returns updated notification instead of error tuple ## v1.1.3 -* More robust FCM/APNS backpressure -* Bumped minimum Kadabra version to `v0.3.6` + +- More robust FCM/APNS backpressure +- Bumped minimum Kadabra version to `v0.3.6` ## v1.1.2 -* Auto-restart connections if max stream ID is reached -* FCM/APNS Workers now use GenStage to queue pending pushes -* Bumped minimum Kadabra version to `v0.3.5` + +- Auto-restart connections if max stream ID is reached +- FCM/APNS Workers now use GenStage to queue pending pushes +- Bumped minimum Kadabra version to `v0.3.5` ## v1.1.1 -* Bumped minimum Kadabra version to `v0.3.4` + +- Bumped minimum Kadabra version to `v0.3.4` ## v1.1.0 -* Minimum requirements now Elixir v1.4 and OTP 19.2 (Kadabra bumped + +- Minimum requirements now Elixir v1.4 and OTP 19.2 (Kadabra bumped to `v0.3.0`) -* Startup worker configs. Create a functions that return config +- Startup worker configs. Create a functions that return config structs and specify them your `config.exs` with ```elixir @@ -97,125 +118,150 @@ config :pigeon, workers: [ ``` **APNS** -* `APNS.Config.config/1` renamed to `APNS.Config.new/1` -* `APNS.push/2` tagged tuples done away with in favor of a `:response` key on + +- `APNS.Config.config/1` renamed to `APNS.Config.new/1` +- `APNS.push/2` tagged tuples done away with in favor of a `:response` key on the notification. -* Override push server endpoint with `:uri` option in `APNS.Config.new/1` -* `:use_2197` renamed to `:port` -* `:uri` config option for overriding push server endpoint -* `:reconnect` now false by default +- Override push server endpoint with `:uri` option in `APNS.Config.new/1` +- `:use_2197` renamed to `:port` +- `:uri` config option for overriding push server endpoint +- `:reconnect` now false by default **FCM** -* `NotificationResponse` done away with in favor of a `:response` key + +- `NotificationResponse` done away with in favor of a `:response` key on `Notification` -* Override push server endpoint with `:uri` and `:port` options +- Override push server endpoint with `:uri` and `:port` options in `FCM.Config.new/1` -* `:uri` and `:port` config options for overriding push server endpoint +- `:uri` and `:port` config options for overriding push server endpoint **ADM** -* `ADM.Config.config/1` renamed to `ADM.Config.new/1` -* `ADM.push/2` tagged tuples done away with in favor of a `:response` key on + +- `ADM.Config.config/1` renamed to `ADM.Config.new/1` +- `ADM.push/2` tagged tuples done away with in favor of a `:response` key on the notification. -* `ADM.start_connection/1` and `ADM.stop_connection/1` added +- `ADM.start_connection/1` and `ADM.stop_connection/1` added ## v1.0.4 -* Fix: removed connection pinging from FCM.Worker (`:ping_period` option + +- Fix: removed connection pinging from FCM.Worker (`:ping_period` option left in FCM config to not break API) ## v1.0.3 -* Fixed proper handling of large FCM push batches + +- Fixed proper handling of large FCM push batches ## v1.0.2 -* Fixed FCM infinite `GOAWAY session_timed_out` loop + +- Fixed FCM infinite `GOAWAY session_timed_out` loop ## v1.0.1 -* Configurable `:ping_period` for FCM connections + +- Configurable `:ping_period` for FCM connections ## v1.0.0 -* GCM migrated to FCM API (http2) -* `GCM` modules renamed to `FCM` -* Set priority of FCM notifications -* `APNSWorker` and `ADMWorker` renamed to `APNS.Worker` and `ADM.Worker` -* Disable auto-reconnect for APNS workers with `reconnect: false` -* Removed Chatterbox http2 client adapter + +- GCM migrated to FCM API (http2) +- `GCM` modules renamed to `FCM` +- Set priority of FCM notifications +- `APNSWorker` and `ADMWorker` renamed to `APNS.Worker` and `ADM.Worker` +- Disable auto-reconnect for APNS workers with `reconnect: false` +- Removed Chatterbox http2 client adapter ## v0.13.0 -* Configurable `:ping_period` for APNS connections + +- Configurable `:ping_period` for APNS connections ## v0.12.1 -* Various `chatterbox` client adapter fixes + +- Various `chatterbox` client adapter fixes ## V0.12.0 -* Configurable `Pigeon.Http2.Client`. Currently supports `kadabra` + +- Configurable `Pigeon.Http2.Client`. Currently supports `kadabra` and `chatterbox` -* `kadabra` bumped to `v0.2.0` +- `kadabra` bumped to `v0.2.0` ## v0.11.0 -* APNS workers can be started and referenced with pids and/or atom names -* Fix: Push `:name` option renamed to `:to` -* Fix: GCM/ADM async pushes now use `spawn/1` instead of `Task.async/1` + +- APNS workers can be started and referenced with pids and/or atom names +- Fix: Push `:name` option renamed to `:to` +- Fix: GCM/ADM async pushes now use `spawn/1` instead of `Task.async/1` ## v0.10.3 -* Fix: cleaned up Elixir v1.4 warnings + +- Fix: cleaned up Elixir v1.4 warnings ## v0.10.2 -* Fix: poison dependency version made optionally `~> 2.0 or ~> 3.0` + +- Fix: poison dependency version made optionally `~> 2.0 or ~> 3.0` ## v0.10.1 -* Fix: kadabra not started + +- Fix: kadabra not started ## v0.10.0 -* Migrated HTTP/2 client from `chatterbox` to `kadabra` -* Support for ADM (Amazon Android) push -* APNS pushes are now synchronous by default. For async pushes use the + +- Migrated HTTP/2 client from `chatterbox` to `kadabra` +- Support for ADM (Amazon Android) push +- APNS pushes are now synchronous by default. For async pushes use the new `on_response` option. GCM and ADM will have it in the next major release. -* Bulk APNS pushing -* Handling of multiple APNS worker connections with different configs -* Re-implemented APNS socket pings to keep connections open -* `:invalid_jSON` corrected to `:invalid_json` +- Bulk APNS pushing +- Handling of multiple APNS worker connections with different configs +- Re-implemented APNS socket pings to keep connections open +- `:invalid_jSON` corrected to `:invalid_json` ## v0.9.2 -* Fixed GCM error response atom conversion + +- Fixed GCM error response atom conversion ## v0.9.1 -* Fixed :eaddrinuse error when restarting Pigeon too quickly with + +- Fixed :eaddrinuse error when restarting Pigeon too quickly with :apns_2197 enabled ## v0.9.0 -* APNS topic made optional -* APNS `put_mutable_content` helper function added -* GCM can be configured on a per-push basis -* Updated to use Macro.underscore + +- APNS topic made optional +- APNS `put_mutable_content` helper function added +- GCM can be configured on a per-push basis +- Updated to use Macro.underscore ## v0.8.0 -* Implemented Chatterbox as APNS HTTP2 client -* APNS server responses now caught asynchronously -* GCM support for `notification` and `data` payload keys + +- Implemented Chatterbox as APNS HTTP2 client +- APNS server responses now caught asynchronously +- GCM support for `notification` and `data` payload keys (`Pigeon.GCM.Notification.new` API changes) ## v0.7.0 -* APNS cert/key configs can now either be a file path, full-text string, + +- APNS cert/key configs can now either be a file path, full-text string, or `{:your_app, "path/to/file.pem"}` (which looks in the `/priv` directory of your app) -* Fixed APNSWorker crash on `:ssl.send/2` timeout -* Better error-handling for invalid APNS configs +- Fixed APNSWorker crash on `:ssl.send/2` timeout +- Better error-handling for invalid APNS configs ## v0.6.0 -* `Pigeon.APNS.Notification.new/3` returns `%Pigeon.APNS.Notification{}` struct -* Configure APNS to use SSL port 2197 with `apns_2197: true` in + +- `Pigeon.APNS.Notification.new/3` returns `%Pigeon.APNS.Notification{}` struct +- Configure APNS to use SSL port 2197 with `apns_2197: true` in your `config.exs` -* Error feedback symbols converted from `CamelCase` to `snake_case` -* APNS expiration values supported with `expiration` key in +- Error feedback symbols converted from `CamelCase` to `snake_case` +- APNS expiration values supported with `expiration` key in `%Pigeon.APNS.Notification{}` ## v0.5.2 -* Fixed bug where APNSWorker would hang up if SSL connection failed. Now + +- Fixed bug where APNSWorker would hang up if SSL connection failed. Now retries the connection twice more before gracefully shutting down. ## v0.5.1 -* GCM error responses return proper chunk of regstration IDs + +- GCM error responses return proper chunk of regstration IDs ## v0.5.0 -* `Pigeon.GCM.Notification.new/2` returns `%Pigeon.GCM.Notification{}` struct -* Multiple registration IDs allowed in `Pigeon.GCM.push/2` -* Remove `:gcm_worker` + +- `Pigeon.GCM.Notification.new/2` returns `%Pigeon.GCM.Notification{}` struct +- Multiple registration IDs allowed in `Pigeon.GCM.push/2` +- Remove `:gcm_worker` diff --git a/README.md b/README.md index f3e0ebde..4c10427c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ![logo](https://raw.githubusercontent.com/codedge-llc/pigeon/master/docs/logo.png) + > 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) @@ -27,41 +28,41 @@ end docs](https://hexdocs.pm/pigeon/apns-apple-ios.html) for setting up your certificate and key. - ```elixir - config :pigeon, :apns, - apns_default: %{ - cert: "cert.pem", - key: "key_unencrypted.pem", - mode: :dev - } - ``` - - This config sets up a default connection to APNS servers. `cert` and - `key` can be any of the following: - - * Static file path - * 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 + ```elixir + config :pigeon, :apns, + apns_default: %{ + cert: "cert.pem", + key: "key_unencrypted.pem", + mode: :dev + } + ``` + + This config sets up a default connection to APNS servers. `cert` and + `key` can be any of the following: + + - Static file path + - 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.** @@ -73,17 +74,17 @@ end 3. Send the packet. Pushes are synchronous and return the notification with an updated `:response` key. - ```elixir - iex> Pigeon.APNS.push(n) - %Pigeon.APNS.Notification{device_token: "your device token", - expiration: nil, id: "963B9FDA-EA60-E869-AAB5-9C88C8E7396B", - payload: %{"aps" => %{"alert" => "your message"}}, response: :success, - topic: "your push topic"} - - # Add an `:on_response` callback for async pushes. - iex> Pigeon.APNS.push(n, on_response: fn(x) -> IO.inspect(x) end) - :ok - ``` + ```elixir + iex> Pigeon.APNS.push(n) + %Pigeon.APNS.Notification{device_token: "your device token", + expiration: nil, id: "963B9FDA-EA60-E869-AAB5-9C88C8E7396B", + payload: %{"aps" => %{"alert" => "your message"}}, response: :success, + topic: "your push topic"} + + # Add an `:on_response` callback for async pushes. + iex> Pigeon.APNS.push(n, on_response: fn(x) -> IO.inspect(x) end) + :ok + ``` Additional documentation: [APNS (Apple iOS)](https://hexdocs.pm/pigeon/apns-apple-ios.html) @@ -93,36 +94,36 @@ Looking for GCM? Try `v0.13` or earlier. 1. Add a default worker config to your mix config. - ```elixir - config :pigeon, :fcm, - fcm_default: %{ - key: "your_fcm_key_here" - } - ``` + ```elixir + config :pigeon, :fcm, + fcm_default: %{ + key: "your_fcm_key_here" + } + ``` 2. Create a notification packet. FCM notifications support - ```elixir - iex> msg = %{ "body" => "your message" } - iex> n = Pigeon.FCM.Notification.new("your device registration ID", msg) - ``` + ```elixir + iex> msg = %{ "body" => "your message" } + iex> n = Pigeon.FCM.Notification.new("your device registration ID", msg) + ``` 3. Send the packet. Pushes are synchronous and return the notification with updated `:status` and `:response` keys. If `:status` is success, `:response` will contain a keyword list of individual registration ID responses. - ```elixir - iex> Pigeon.FCM.push(n) - %Pigeon.FCM.Notification{message_id: "0:1512580747839227%8911a9178911a917", - payload: %{"notification" => %{"body" => "your message"}}, priority: :normal, - registration_id: "your device registration ID", - response: [success: "your device registration ID"], - status: :success} - - # Add an `:on_response` callback for async pushes. - iex> Pigeon.FCM.push(n, on_response: fn(x) -> IO.inspect(x) end) - :ok - ``` + ```elixir + iex> Pigeon.FCM.push(n) + %Pigeon.FCM.Notification{message_id: "0:1512580747839227%8911a9178911a917", + payload: %{"notification" => %{"body" => "your message"}}, priority: :normal, + registration_id: "your device registration ID", + response: [success: "your device registration ID"], + status: :success} + + # Add an `:on_response` callback for async pushes. + iex> Pigeon.FCM.push(n, on_response: fn(x) -> IO.inspect(x) end) + :ok + ``` Additional documentation: [FCM (Android)](https://hexdocs.pm/pigeon/fcm-android.html) @@ -130,35 +131,35 @@ Additional documentation: [FCM (Android)](https://hexdocs.pm/pigeon/fcm-android. 1. Add a default worker config to your mix config. - ```elixir - config :pigeon, :adm, - adm_default: %{ - client_id: "your_oauth2_client_id_here", - client_secret: "your_oauth2_client_secret_here" - } - ``` + ```elixir + config :pigeon, :adm, + adm_default: %{ + client_id: "your_oauth2_client_id_here", + client_secret: "your_oauth2_client_secret_here" + } + ``` 2. Create a notification packet. - ```elixir - iex> msg = %{ "body" => "your message" } - iex> n = Pigeon.ADM.Notification.new("your device registration ID", msg) - ``` + ```elixir + iex> msg = %{ "body" => "your message" } + iex> n = Pigeon.ADM.Notification.new("your device registration ID", msg) + ``` 3. Send the packet. - ```elixir - iex> Pigeon.ADM.push(n) - %Pigeon.ADM.Notification{consolidation_key: nil, - expires_after: 604800, md5: "M13RuG4uDWqajseQcCiyiw==", - payload: %{"data" => %{"body" => "your message"}}, - registration_id: "your device registration ID", - response: :success, updated_registration_id: nil} - - # Add an `:on_response` callback for async pushes. - iex> Pigeon.ADM.push(n, on_response: fn(x) -> IO.inspect(x) end) - :ok - ``` + ```elixir + iex> Pigeon.ADM.push(n) + %Pigeon.ADM.Notification{consolidation_key: nil, + expires_after: 604800, md5: "M13RuG4uDWqajseQcCiyiw==", + payload: %{"data" => %{"body" => "your message"}}, + registration_id: "your device registration ID", + response: :success, updated_registration_id: nil} + + # Add an `:on_response` callback for async pushes. + iex> Pigeon.ADM.push(n, on_response: fn(x) -> IO.inspect(x) end) + :ok + ``` Additional documentation: [ADM (Amazon Android)](https://hexdocs.pm/pigeon/adm-amazon-android.html) @@ -208,69 +209,69 @@ more complex. 1. Modify your `mix.exs` to _not_ start `pigeon` by default: - ```elixir - def deps do - [ - {:pigeon, "~> 1.3.1", runtime: false}, - {:kadabra, "~> 0.4.4"}, - {:ecto, "~> 2.0 or ~> 3.0"} - ] - end - ``` + ```elixir + def deps do + [ + {:pigeon, "~> 1.3.1", runtime: false}, + {:kadabra, "~> 0.4.4"}, + {:ecto, "~> 2.0 or ~> 3.0"} + ] + end + ``` 2. Modify `config.exs` to specify a single worker function: - ```elixir - config :pigeon, workers: [{YourApp.Pigeon, :config}] - ``` + ```elixir + config :pigeon, workers: [{YourApp.Pigeon, :config}] + ``` 3. Modify your main application to start `pigeon` after your `Repo` has been started under your application’s supervision tree: - ```elixir - def start(_type, _args) do - children = [ - YourApp.Repo, - ] - - opts = [strategy: :one_for_one, name: YourApp.Supervisor] - - with {:ok, sup} <- Supervisor.start_link(children, opts), - {:ok, _} <- Application.ensure_all_started(:pigeon, :permanent) do - {:ok, sup} - end - end - ``` + ```elixir + def start(_type, _args) do + children = [ + YourApp.Repo, + ] + + opts = [strategy: :one_for_one, name: YourApp.Supervisor] + + with {:ok, sup} <- Supervisor.start_link(children, opts), + {:ok, _} <- Application.ensure_all_started(:pigeon, :permanent) do + {:ok, sup} + end + end + ``` 4. Implement your database query as part of your Pigeon module: - ```elixir - defmodule YourApp.Pigeon do - @moduledoc false - - alias YourApp.{PushApplication, Repo} - - def config_workers do - PushApplication - |> Repo.all() - |> Enum.map(&build_config/1) - end - - defp build_config(%{type: "apns"} = config) - Pigeon.APNS.ConfigParser.parse( - key: config.key, - key_identifier: config.key_identifier, - team_id: config.team_id, - mode: config.mode, - name: Atom.to_string(config.name) # This is bad, but keep it simple! - ) - end - - defp build_config(%{type: "fcm"} = config) do - Pigeon.FCM.Config.new( - name: Atom.to_string(config.name), - key: config.key - ) - end - end - ``` + ```elixir + defmodule YourApp.Pigeon do + @moduledoc false + + alias YourApp.{PushApplication, Repo} + + def config_workers do + PushApplication + |> Repo.all() + |> Enum.map(&build_config/1) + end + + defp build_config(%{type: "apns"} = config) + Pigeon.APNS.ConfigParser.parse( + key: config.key, + key_identifier: config.key_identifier, + team_id: config.team_id, + mode: config.mode, + name: Atom.to_string(config.name) # This is bad, but keep it simple! + ) + end + + defp build_config(%{type: "fcm"} = config) do + Pigeon.FCM.Config.new( + name: Atom.to_string(config.name), + key: config.key + ) + end + end + ``` diff --git a/docs/APNS Apple iOS.md b/docs/APNS Apple iOS.md index 0e4c2997..f2f29754 100644 --- a/docs/APNS Apple iOS.md +++ b/docs/APNS Apple iOS.md @@ -222,6 +222,7 @@ second parameter. |`:expired_provider_token` |The provider token is stale and a new token should be generated.| |`:forbidden` |The specified action is not allowed.| |`:invalid_provider_token` |The provider token is not valid or the token signature could not be verified.| +|`:invalid_push_type` |The apns-push-type value is invalid.| |`:missing_provider_token` |No provider certificate was used to connect to APNs and Authorization header was missing or no provider token was specified.| |`:bad_path` |The request contained a bad :path value.| |`:method_not_allowed` |The specified :method was not POST.|