Application Helpers
-------------------

The payload delivered to push-client will be passed onto a helper program that can modify it as needed before passing it onto
the postal service (see `Helper Output Format <#helper-output-format>`__).

The helper receives two arguments ``infile`` and ``outfile``. The message is delivered via ``infile`` and the transformed
version is placed in ``outfile``.

This is the simplest possible useful helper, which simply passes the message through unchanged:

    #!/usr/bin/python3
    import sys
    f1, f2 = sys.argv[1:3]
    open(f2, "w").write(open(f1).read())

Helpers need to be added to the click package manifest:

    {
        "architecture": "all",
        "description": "Example app for push notifications.",
        "framework": "ubuntu-sdk-16.04.5",
        "hooks": {
            "hello": {
                "apparmor": "hello.json",
                "desktop": "hello.desktop"
            },
            "helloHelper": {
                "apparmor": "helloHelper-apparmor.json",
                "push-helper": "helloHelper.json"
            }
        },
        "maintainer": "Example Developer <dev@example.com>",
        "name": "hello.example",
        "title": "Hello",
        "version": "0.5.0"
    }

Here, we created a helloHelper entry in hooks that has an apparmor profile and an additional JSON file for the push-helper hook.

helloHelper-apparmor.json must contain **only** the push-notification-client policy group and the lomiri-push-helper template:

    {
        "template": "ubuntu-push-helper",
        "policy_groups": [
            "push-notification-client"
        ],
        "policy_version": 16.04
    }

And helloHelper.json must have at least a exec key with the path to the helper executable relative to the json, and optionally
an app_id key containing the short id of one of the apps in the package (in the format packagename_appname without a version).
If the app_id is not specified, the helper will be used for all apps in the package::

    {
        "exec": "helloHelper",
        "app_id": "hello.example_hello"
    }

.. note:: For deb packages, helpers should be installed into /usr/lib/lomiri-push-service/legacy-helpers/ as part of the package.

Helper Output Format
--------------------

Helpers output has two parts, the postal message (in the "message" key) and a notification to be presented to the user (in the "notification" key).

.. note:: This format **will** change with future versions of the SDK and it **may** be incompatible.

Here's a simple example::

    {
        "message": "foobar",
        "notification": {
            "tag": "foo",
            "card": {
                "summary": "yes",
                "body": "hello",
                "popup": true,
                "persist": true,
                "timestamp": 1407160197
            }
            "sound": "buzz.mp3",
            "vibrate": {
                "pattern": [200, 100],
                "repeat": 2
            }
            "emblem-counter": {
                "count": 12,
                "visible": true
            }
        }
    }

The notification can contain a **tag** field, which can later be used by the `persistent notification management API. <#persistent-notification-management>`__

:message: (optional) A JSON object that is passed as-is to the application via PopAll.
:notification: (optional) Describes the user-facing notifications triggered by this push message.

The notification can contain a **card**. A card describes a specific notification to be given to the user,
and has the following fields:

:summary: (required) a title. The card will not be presented if this is missing.
:body: longer text, defaults to empty.
:actions: If empty (the default), a bubble notification is non-clickable.
          If you add a URL, then bubble notifications are clickable and launch that URL. One use for this is using a URL like
          ``appid://com.ubuntu.developer.ralsina.hello/hello/current-user-version`` which will switch to the app or launch
          it if it's not running. See `URLDispatcher <https://wiki.ubuntu.com/URLDispatcher>`__ for more information.

:icon: An icon relating to the event being notified. Defaults to empty (no icon);
       a secondary icon relating to the application will be shown as well, regardless of this field.
:timestamp: Seconds since the unix epoch, only used for persist (for now). If zero or unset, defaults to current timestamp.
:persist: Whether to show in notification centre; defaults to false
:popup: Whether to show in a bubble. Users can disable this, and can easily miss them, so don't rely on it exclusively. Defaults to false.

.. note:: Keep in mind that the precise way in which each field is presented to the user depends on factors such as
          whether it's shown as a bubble or in the notification centre, or even the version of Ubuntu Touch the user
          has on their device.

The notification can contain a **sound** field. This is either a boolean (play a predetermined sound) or the path to a sound file. The user can disable it, so don't rely on it exclusively.
Defaults to empty (no sound). The path is relative, and will be looked up in (a) the application's .local/share/<pkgname>, and (b)
standard xdg dirs.

The notification can contain a **vibrate** field, causing haptic feedback, which can be either a boolean (if true, vibrate a predetermined way) or an object that has the following content:

:pattern: a list of integers describing a vibration pattern (duration of alternating vibration/no vibration times, in milliseconds).
:repeat: number of times the pattern has to be repeated (defaults to 1, 0 is the same as 1).

The notification can contain a **emblem-counter** field, with the following content:

:count: a number to be displayed over the application's icon in the launcher.
:visible: set to true to show the counter, or false to hide it.

.. note:: Unlike other notifications, emblem-counter needs to be cleaned by the app itself.
          Please see `the persistent notification management section. <#persistent-notification-management>`__

.. FIXME crosslink to hello example app on each method

Security
~~~~~~~~

To use the push API, applications need to request permission in their security profile, using something like this:

    {
        "policy_groups": [
            "networking",
            "push-notification-client"
        ],
        "policy_version": 16.04
    }


Push Server API
---------------

The Push server is located at https://push.ubports.com and has a single endpoint: ``/notify``.
To notify a user, your application has to do a POST with ``Content-type: application/json``.

.. note:: The contents of the data field are arbitrary. They should be enough for your helper to build
          a notification using it, and decide whether it should be displayed or not. Keep in mind
          that this will be processed by more than one version of the helper, because the user may be using
          an older version of your app.

Here is an example of the POST body using all the fields::

    {
        "appid": "app.example_app",
        "expire_on": "2014-10-08T14:48:00.000Z",
        "token": "LeA4tRQG9hhEkuhngdouoA==",
        "clear_pending": true,
        "replace_tag": "tagname",
        "data": {
            "id": 43578,
            "timestamp": 1409583746,
            "serial": 1254,
            "sender": "Joe",
            "snippet": "Hi there!"
        }
    }


:appid: ID of the application that will receive the notification, as described in the client side documentation.
:expire_on: Expiration date/time for this message, in `ISO8601 Extendend format <http://en.wikipedia.org/wiki/ISO_8601>`__
:token: The token identifying the user+device to which the message is directed, as described in the client side documentation.
:clear_pending: Discards all previous pending notifications. Usually in response to getting a "too-many-pending" error.
:replace_tag: If there's a pending notification with the same tag, delete it before queuing this new one.
:data: A JSON object.

Limitations of the Server API
-----------------------------

The push notification infrastructure is meant to help ensuring timely
delivery of application notifications if the device is online or
timely informing the device user about application notifications that
were pending when the device comes back online. This in the face of
applications not being allowed to be running all the time, and
avoiding the resource cost of many applications all polling different services
frequently.

The push notification infrastructure is architected to guarantee at
least best-effort with respect to these goals and beyond it, on the
other end applications should not expect to be able to use and only
rely on the push notification infrastructure to store application
messages if they want ensure all their notification or messages are
delivered, the infrastructure is not intended to be the only long term
"inbox" storage for an application.

To preserve overall throughput the infrastructure imposes some limits
on applications:

 * message data payload is limited to 2K

 * when inserted all messages need to specify an expiration date after
   which they can be dropped and not delivered

 * an application is limited in the number of messages per token
   (application/user/device combination) that can be undelivered/pending at the
   same time (100 currently)

replace_tag can be used to implement notifications for which the newest
one replace the previous one if pending.

clear_pending can be used to be deal with a pending message limit
reached, possibly substituting the current undelivered messages with a
more generic one.

Applications using the push notification HTTP API should be robust
against receiving 503 errors, retrying after waiting with increasing
back-off. Later rate limits (signaled with the 429 status) may also come
into play.
