npm package: @omneo/shapes-sdk
Description: Omneo Shapes provide a seamless way to include Omneo profile data in a website with minimal effort
Author: Omneo
Latest version: 0.1.9

# Shapes SDK
A powerful little library for interacting with Omneo profile data and keeping UI elements in sync 🤖

## Features
* Make authenticated Omneo http requests from the browser
* Supports Promises
* Data state management with [Redux](https://github.com/reduxjs/redux)
* PubSub and DOM events for state updates
* Offline data caching with localStorage

## API Documentation
[Getting started | Shapes SDK by Omneo](https://shapes-sdk.omneo.io/guide/#the-shapessdk)

## Installing
Using npm:
```sh
$ npm install @omneo/shapes-sdk
```
Using yarn:
```sh
$ yarn add @omneo/shapes-sdk
```
Using CDN:
```html
<script type="text/javascript" src="https://cdn.omneo.io/shapes-sdk/shapes.sdk.js"></script>
```

When including ShapesSDK from the CDN, the library will be attached to the window and accessible at window.ShapesSDK. When including from npm/yarn ShapesSDK can be used via ES6 import or ES5 require:
```js
import ShapesSDK from '@omneo/shapes-sdk'
var ShapesSDK = require('@omneo/shapes-sdk')
```

## Initialising the client 
``` js
var shapesClient = ShapesSDK.init({
    url: 'https://api.[tenant-handle].getomneo.com/id', 
    token: '[omneo_id_token]',
    logging: true
})
```
>Some implementations may use a proxy url, please check with your Omneo implementation partner to confirm this. 
>Omneo ID token must first be generated by a secure server process. If you are using an Omneo eCommerce plugin, the token will be generated on login and added to a metafield

 
## Client Object
One initialised the `shapesClient` object exposes a number of methods for retrieving cached profile data, handling requests to Omneo and subscribing to state updates.
All methods interact with the client data state, which follows this structure:

```js
{
    "client_id": uuid,
    "loading": boolean,
    "profile": {
        "init": true,
        "error": false, 
        "loading": false,
        "data": {
            "id": "8e697f6b-533d-4837-8680-e7c6b3cde9f0",
            "first_name": "John",
            "last_name": "Smith",
            ...
        }
    }
}
```

### Methods
- [find](/guide/#shapesclient-find-path)
- [hydrate](/guide/#shapesclient-hydrate-path)
- [on](/guide/#shapesclient-on-event-name-handler)
- [off](/guide/#shapesclient-off-event-name-handler)

## .find()
Traverses and returns value from the shapes data state, using a path in dot `.` format.

#### Arguments
`path` - Dot notation path to data in state 

#### Returns

Value from existing `shapesClient` state. If no value is found, `null` is returned.
> This method is not responsible with fetching new data. When `shapesClient` loads, it will pull all cached data and a fresh set of top level `profile` data. If an endpoint such as `transactions` is required, register this with the `hydrate()` method below, which will then fetch from the Omneo API.

#### Example
```js
let transactions = shapesClient.find('transactions'); // Get all 
transactions
let combined_balance = shapesClient.find('balances.combined_balance_dollars') // Get first transaction by index
let loading = shapesClient.find('loading'); // Get loading state
let state = shapesClient.find(); // Get shapes state
```


## .hydrate()
Registers that `shapesClient` must retrieve the given endpoint. If the data has not already been fetched, `shapesClient` will get this data from Omneo and merge it into the client state. Once initialized, `shapesClient` will then emit the relevant `shapes.{endpoint}.ready` event.
#### Arguments
- `path` - Dot notation path to endpoint
#### Returns
`true` if there were no exceptions. All updates should be watched for through the `.on()` method 
#### Example
```js
shapesClient.hydrate('transactions')
shapesClient.hydrate('profile')
shapesClient.hydrate('lists')
```

## .on()
Allows you to add handlers to a `shapesClient` event. You can register multiple handlers to the same event, by calling this function for each. Each time the event with `event_name` is triggered, all handlers for this name will be called with event data passed as an argument.

For available events, see [Events](/guide/#events)
#### Arguments
- `event_name` - Dot notation event name
- `handler` - Function with single `data` argument, to be executed on event
#### Returns
The value of that event's relevant endpoint. Note that list endpoints are transformed into keyed objects for ease of state management. 
#### Example
```js
let handler = function(data){
    console.log(data);
};

let profile = shapesClient.on('profile.update', handler);

// console.log
// {
//     "init": true,
//     "error": false, 
//     "loading": false,
//     "data": {
//         "id": "8e697f6b-533d-4837-8680-e7c6b3cde9f0",
//         "first_name": "John",
//         "last_name": "Smith",
//         ...
//     }
// }
```

## .off()
Allows you to remove a handler for a `shapesClient` event. 

For available events, see [Events](/guide/#events)
#### Arguments
- `event_name` - Dot notation event name
- `handler` - Function with single `data` argument, to be executed on event
#### Returns
`true` if event listener was unregistered correctly
#### Example
```js
let handler = function(data){
    // Do something with data
};

shapesClient.off('profile.update', handler);
```

## .dispatch()
Dispatches an action directly to the internal store to manually manage state. The internal store reducer handles a number of action types.

#### [resource].update
Will merge the payload into the top level resources. Special consideration is given to `data` so it will be merged and not overwrite the existing state. Ensure that for resources such as `transactions` the array of items has been converted to an object with `id` as key.
``` json
{
    "type": "profile.update", 
    "payload": {
        "error": false,
        "data": {
            "first_name": "John"
        }
    }
}
```

#### [resource].add
An alias of `update` that is used when adding new items to resource lists, such as `rewards` or `transactions`

#### [resource].remove
Removes a key from the `{resource}.data` object by specifying the key in the payload
``` json
{
    "type": "transaction.remove", 
    "payload": {
        "id": "5"
    }
}
```

#### [resource].set
Merges the payload against the whole state object. Special provisions are not made for `data` and anything included in the `set` payload will overwrite the existing state. This is especially useful if you are managing the whole state manually.
``` js
{
    "type": "lists.set", 
    "payload": {
        "init": true,
        "loading": false,
        "data": {
            "5": {...},
            "27": {...}
        }
    }
}
```

#### [resource].load
Sets the loading state of a resource. This does not merge any other payload keys. This is useful for setting `loading: true` before a http request etc.

``` js
{
    "type": "profile.load", 
    "payload": {
        "loading": true
    }
}
```

#### [resource].reset
Resets the resource back to the initial state of the reducer. Useful for clearing if cached data is malformed or if there has been an error.

``` json
{
    "type": "lists.reset", 
}
```


#### Arguments
Dispatch receives a single action object, with `type` and `payload` keys to update the state. 
- `type` - Action type, using `.` separator to designate the resource. eg. `profile.set`
- `payload` - Object containing key/values to update in state. Refer to the action types for examples and accepted keys for each `type`

#### Returns
`undefined`

#### Example
```js
shapesClient.dispatch({
    type: 'profile.update',
    payload: {
        data: {
            first_name: "John",
            last_name: "Smith",
            email: "john@example.com"
        }
    }
})

shapesClient.dispatch({
    type: 'transacitons.add',
    payload: {
        data: {
            "5":{
                id: 5,
                items: [...]
            }
        }
    }
})

shapesClient.dispatch({
    type: 'lists.remove',
    payload: {
        data: {
            id: 5
        }
    }
})
```

## Requests
Shapes provides methods for making authenticated requests to all profile endpoints in Omneo. In addition, the shapes requests methods allow for automatic updating of top level profile objects such as `transactions`
> Currently the Request methods do not support automatic state updates of deeply nested objects, such as items within a list eg. `lists.1.items.4`

#### Methods
``` js
// GET
shapesClient.get(path, params, updateState)

// POST
shapesClient.put(path, data, updateState)

// PUT
shapesClient.post(path, data, updateState)

// DELETE
shapesClient.delete(path, false, updateState)
```

#### Arguments
- `path` - Dot notation path to endpoint
- `data` - Body payload for PUT and POST requests
- `params` - GET parameters, used to filter and paginate browse/list requests
- `updateState` - A boolean flag automatically merge request response into the state and emit update events to all subscribers.

#### Returns
A JavaScript Promise object. Once resolved, this method will return the value of that endpoint. 
#### Example
```js
shapesClient.get('profile').then(response=>{
    // Do something with profile data
})
shapesClient.post('profile', { first_name: "James" }, true).then(response=>{
 // Profile object with updated "new_name" field
})
shapesClient.put('lists.6', { id: 2, title: 'New List Item' }, true).then(() => {
  // Added new item to a list with the id of 6
})
shapesClient.delete('list.6', true).then(response => {
  // Delete entire list with id of 6
  // response.status = 204 no content
})
```


## Events
As the `shapesClient` manages all state and requests internally, it exposes a number of events to keep subscribed components up to date. These events are emitted through 2 differend streams. The first is to `subscribers` of `shapesClient.on('{event_name}')` and the second is to the native browser js `Event()` handler.


**Note:** In addition to the methods below, the `shapesClient` also emits all events to the `document` as custom named events.

#### Event structure
Events are defined by an event type and payload. Shapes will emit general events for when the client is initialized and for any state update. It will also emit events for `profile.ready` and `profile.update` as well as `ready` and `update` events for all individual endpoints. Events will include the UUID for the `shapesClient` as well as a loading indicator to confirm if an `update` event represents a loading state for that state object.

#### Event types
- shapes.ready
- shapes.update
- shapes.{endpoint}.ready
- shapes.{endpoint}.load
- shapes.{endpoint}.update

#### shapes.{event_type}
``` js
{
    "client_id": uuid,
    "profile":{
        "init": true,
        "error": false, 
        "loading": false,
        "data": {...}
    },
    "transactions":{
        "init": true,
        "error": false, 
        "loading": false,
        "data": {...}
    },
    ...
}
```

#### shapes.{resource}.{event_type}
``` js
{
    "init": true,
    "error": false, 
    "loading": false,
    "data": {
        "id": "8e697f6b-533d-4837-8680-e7c6b3cde9f0",
        "first_name": "John",
        "last_name": "Smith",
        ...
    }
}
```