.. Author: Hossein Safavi

.. STATUS: draft

.. role:: red

.. _api_ui:

#########################
Internal UI API Reference
#########################

This API is meant to be **privately** accessible. Calls are authenticated with personal API tokens (currently ``uuid``)
which is checked against a token table in the database.

.. attention:: There are few **known issues** with the current token authentication mechanism:

    1. There is currently no enforced **expiration** for tokens.
    2. There is currently no **throttling** for attempts at token generation.
    3. **Tokens** should be cached in a ``key-value`` store such as Redis.
    4. There is no **early rejection** through signed authorization tokens



***********
Access Flow
***********

Each user must have a token for each experiment. The token contains a warrant indicating the permissions for that user.
There will be separate tokens generated for the UI apps, at the moment, the token has the name "UI".

Step 1
    Retrieve a list of available UI tokens given the username/password by using the ``/experiment/list/`` endpoint. If
    no tokens exist, at your choice, you can call the endpoint ``/tokens/create`` in order to generate/fetch tokens.
Step 2
    From that point onwards, use the token when accessing any resources or actions that are exposed within the UI.
Step 3
    If desired, experiment switching can be implemented by changing the token used for authorization.

In order to run against this API, we recommend the following Python packages be installed: ``requests`` ``passlib`` ``json``


.. http:get:: /experiment/list/?email=(email)&password=(password)


   Fetch a list of the available tokens for the given user. Each token will correspond to an authorization warrant that
   explains the rights of the user with regards to that experiment. At this time, this warrant is stored in the cloud
   inside the database which make authenticated API calls relatively expensive.

   :param email: The email address of the user requesting the authentication token.
   :type email: string
   :param password: The unhashed password of the user requesting the authentication token (as enccoded by `passlib.sha256` .
   :type password: string


   :resheader Content-Type: application/json
   :statuscode 200: no error
   :statuscode 401: incorrect user email, password hash, or experiment_id
   :statuscode 404: no tokens found for the given user

   **Example response**:
    .. sourcecode:: http

      HTTP/1.1 200 OK
      Vary: Accept
      Content-Type: application/json

    .. sourcecode:: javascript

        [
            {
                "status": {
                    "text": "OK",
                    "code": 200
                },
                "content": {
                    "privileges": [
                        {
                            "token": {
                                "token": "1e24717caa1c40e28f420af4947ddbbf",
                                "experiment": 1,
                                "grant_time": {
                                    "_type": "Timestamp",
                                    "value": "2017-03-08 10:02:21.934000"
                                },
                                "name": "UI",
                                "user": 1
                                },
                                "experiment": {
                                "protocol": null,
                                "name": "SeizeIT",
                                "creator": 1,
                                "utc_offset_minutes": 60,
                                "closed": false,
                                "owner": 1,
                                "id": 1
                            }
                        }
                    ],
                    "user": {
                        "id": 1,
                        "email": "hossein.safavi@byteflies.com"
                    }
                }
            }
        ]


.. http:get:: /tokens/create/?email=(email)&password=(password)


   This calls serves as a method to update the tokens and warrants for a user. It is primarily meant to be called each
   time a user is added to an experiment so that the associated token/warrant can be generated for UI access. The
   generated tokens are then returned to the caller.

   :param email: The email address of the user requesting the authentication token.
   :type email: string
   :param password: The unhashed password of the user requesting the authentication token (as enccoded by `passlib.sha256` .
   :type password: string


   :resheader Content-Type: application/json
   :statuscode 200: no error
   :statuscode 401: incorrect user email, password hash, or experiment_id
   :statuscode 404: no tokens could be generated because no experiments were found for this user

   **Example response**:
    .. sourcecode:: http

      HTTP/1.1 200 OK
      Vary: Accept
      Content-Type: application/json

    .. sourcecode:: javascript

      {
        "status": {
            "text": "OK",
            "code": 200
        },
        "content": [
            {
                "token": "1e24717caa1c40e28f420af4947ddbbf",
                "experiment": 1,
                "role": {
                    "_type": "<enum 'RoleEnum'>",
                    "value": "BF_ADMIN"
                }
                },
                {
                "token": "8a5070a690634e4d9c93daf639918bb1",
                "experiment": 2,
                "role": {
                    "_type": "<enum 'RoleEnum'>",
                    "value": "BF_ADMIN"
                }
            }
        ]
      }


.. http:get:: /box/overview/list/?access_token=(access_token)&experiment_id=(experiment_id)


   Fetches a summary overview of all the boxes within the experiment including status information on the boxes, a list
   of the latest recordings and other information that is needed for overview display.

   :access_token: The access token (uuid) which corresponds to the permission warrant for the desired experiment
   :type access_token: string
   :param experiment_id: The id of the experiment from which an overview is desired.
   :type experiment_id: int


   :resheader Content-Type: application/json
   :statuscode 200: no error
   :statuscode 401: incorrect user email, password hash, or experiment_id
   :statuscode 404: no boxes were found in the selected experiment

   **Example response**:
    .. sourcecode:: http

      HTTP/1.1 200 OK
      Vary: Accept
      Content-Type: application/json


.. http:get:: `/box/reassign/?access_token=(access_token)&box_allocation_id=(box_allocation_id)&company_specific_id=(company_specific_id)&start_time=(start_time)`


   The re-assign function allows a user to re-assign a box and the associated sensors to a new user. At the moment, we
   enforce that the box being re-assigned must have a current ongoing assignment with a open ending (i.e.
   no ``end_time`` specified). This is required due the poor way in which re-assignment is handled at this time. A
   series of errors can be generated in case the requested re-assignment cannot be completed due to data integrity
   issues. The re-assignment process happen inside an atomic transaction so if it fails, the database has not been modified
   in any way.

   :access_token: The access token (uuid) which corresponds to the permission warrant for the desired experiment
   :type access_token: string
   :box_allocation_id: The id of the experiment from which an overview is desired.
   :type box_allocation_id: int
   :param company_specific_id: The id of the experiment from which an overview is desired.
   :type company_specific_id: string
   :param start_time: The starting time of the next assignment (in **UTC**). This time will also be marked as the ending time of the previous assignment that is being terminated.
   :type start_time: datetime
   :param end_time: The ending time of the next assignment


   :resheader Content-Type: application/json
   :statuscode 200: no error
   :statuscode 401: incorrect user email, password hash, or experiment_id
   :statuscode 404: no boxes were found in the selected experiment
   :statuscode 409: internal data conflict

   **Example response**:
    .. sourcecode:: http

      HTTP/1.1 200 OK
      Vary: Accept
      Content-Type: application/json