cognite-dash-components 0.2.5
Plotly Components for Cognite
License: MIT

# Cognite Dash Components

Cogntie Dash Components contains Python components that help Plotly Dash users to work with CDF. The current supported components are:

1. CogniteClientProvider
1. CogniteTenantSelector

## Installation

The package can be installed using pip 

```bash
pip install cognite-dash-components
```

## Usage

Import the package to your Plotly Dash Application


```python
import cognite_dash_components as cdc
```

### Cognite Client Provider
There should be only one `CogniteClientProvider`. This component provides the login functionality. It has the following properties:
* id: Unique component id to use in Dash
* project: The project to authenticate against
* authenticated: Set when a user is authenticated to `True`
* user: The authenticated users email address
* validTenants: A list of valid tenant dictionaries

The `validTenants` property is a list of customer projects that your app will allow people to login from. The term __tenant__, __project__ and __company name__ are used interchangeably. The `validTenants` list must be formatted in the following way:
```python
validTenants = [
    {'tenant':'publicdata','baseUrl':'https://api.cognitedata.com'},
]
```
Note each `tenant` should have the correct `baseUrl` supplied.

When the component recieves a valid `project` the user is redirected to the CDF login page. On successfully signing in they are redirected back to the application from which they have come, with their __project__ in the pathname and the __id_token__ and __access_token__ in the search parameters.

The __accessToken__ is saved as a cookie called `_csrf_token`. This is then used by __Dash__ to inject the token into each requests header under the key `X-Csrftoken`. This can be easily retrieved by creating a function like the one below:

```python
from flask import request

def getToken():
    return request.headers.get("X-Csrftoken")
```

This can then be used in `app.callback` to register authenticate a `CogniteClient` on the backend.

### Cognite Tenant Selector
The `CogniteTenantSelector` is used to let the user enter the project they want to authenticate against. This is useful if you are developing a Dash application for multiple projects. It has the following properties:
* id: Unique component id to use in Dash
* title: Text to show in the tenant selector
* project: The valid tenant entered by the user
* validTenants: Same as for the Client Provider
* hidden: A boolean that can hide the component from the user

When a user enters a valid tenant the components `project` property is updated. This can then be passed to the `CogniteClientProvider` to trigger authentication against the project. When a user is authenticated you can hide the tenant selector by setting the `hidden` property to `True`. 

If a user goes directly to a url containing a valid project the authentication flow will be triggered. If they still have a valid token then this will be used to authenticate them with CDF and provide the component with the users details.

## Basic Usage

Below is an example on how to use the components.
Note: Authentication relies on SSL. To achieve this locally create a dummy certificate/key pair by running:

`openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365`


```python
import cognite_dash_components as cdc
import dash
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
import dash_html_components as html
from flask import request
from cognite.client import CogniteClient
import dash_core_components as dcc


app = dash.Dash(__name__)

app.title = "CDC Test"

# List of Valid Tenants to provide access to the app
validTenants = [{"tenant": "publicdata", "baseUrl": "https://api.cognitedata.com"}]

# Visual Layout Components
app.layout = html.Div(
    [
        cdc.CogniteClientProvider(id="cognite", validTenants=validTenants,),
        cdc.CogniteTenantSelector(
            id="tenant-selector",
            title="Example App",
            hidden=True,
            validTenants=validTenants,
        ),
        html.Div(dcc.Loading(html.Div(id="user")), id="secure-content", hidden=True),
    ]
)

# Helper functions for calling CDF
def getToken():
    return request.headers.get("X-Csrftoken")


def getCogniteClient(project):
    base_url = next(item for item in validTenants if item["tenant"] == project)[
        "baseUrl"
    ]
    return CogniteClient(
        token=getToken(), project=project, base_url=base_url, client_name="Plotly Test"
    )

# Show the secure content container if authenticated, else show the tenant selector
@app.callback(
    [Output("tenant-selector", "hidden"), Output("secure-content", "hidden")],
    [Input("cognite", "authenticated")],
)
def toggle_secure_content(authenticated):
    if authenticated:
        return True, False
    else:
        return False, True
    raise PreventUpdate


# Initiate login when valid project set in tenant selector
@app.callback(
    Output("cognite", "project"), [Input("tenant-selector", "project")],
)
def set_cognite_project(project):
    if project:
        return project
    raise PreventUpdate

# Load some secure content when user is authenticated
@app.callback(
    Output("user", "children"),
    [
        Input("cognite", "project"),
        Input("cognite", "authenticated"),
        Input("cognite", "user"),
    ],
)
def update_user_div(project, authenticated, user):

    if project and authenticated and user:

        client = getCogniteClient(project)
        assets = client.assets.list()

        return f"Hi {user}, you are authenticated to the {project} project. There are {len(assets)} assets in this project"
    raise PreventUpdate


if __name__ == "__main__":
    app.run_server(debug=True, ssl_context=("cert.pem", "key.pem"))

```