npm package: @hitchy/plugin-odem-socket.io
Description: exposing Hitchy's document-oriented database via websocket
Author: cepharum GmbH
Latest version: 0.5.0

# @hitchy/plugin-odem-socket.io

_exposing Hitchy's document-oriented database via websocket_

## License

[MIT](LICENSE)

## Installation

```bash
npm i @hitchy/plugin-odem-socket.io
```

This plugin relies on additional plugins to be installed as well:

```bash
npm i @hitchy/plugin-odem @hitchy/plugin-socket.io
```


## Breaking changes

### v0.2

* The parameter `uuidsOnly` of actions `<modelName>:list` and `<modelName>:find` has been replaced by `loadRecords` to match the semantics of server-side Odem API. You have to negate its value to adapt.

### v0.3

* This version is recognizing an existing authentication plugin and checks a user's authorization for accessing models and their properties based on authorization rules set on server.
* Previously, actions of control API required the provision of a session ID to read a requesting user from as first argument. This approach has been abandoned. All actions are automatically associated with the user which has been authenticated when the websocket connection has been established.
* Clients should drop and re-connect over websocket whenever authentication of a requesting user has changed.

## Usage

### Basics

This plugin is exposing an API via websocket for controlling and monitoring models of server-side document-oriented database. Relying on particular features of [socket.io](https://socket.io/), this API is exposed in [namespace](https://socket.io/docs/v4/namespaces/) `/hitchy/odem`. 

For every server-side model, request listeners and/or notification broadcasters are set up. Either model's name is converted into its kebab-case variant and used as colon-separated prefix in event names emitted either by the server or by a client. 

> In the following sections, the placeholder `<modelName>` is used instead of that kebab-case variant of a model's name.

### Control API

> **Attention!**
> 
> As of v0.1.0, the control API is disabled by default due to the lack of having authorization checks implemented on server-side. You have to enable it explicitly by setting runtime configuration parameter `config.socket.odem.crud` which defaults to `false`.

All action events listed in this section are to be sent by a client. The server is setting up listeners and directly responds to either received event. Thus, the common pattern on client side looks like this:

```javascript
const socket = io( "/hitchy/odem" );

socket.on( "connect", () => {
    socket.emit( actionName, arg1, arg2, arg3, response => {
        // process the response here
    } );
} );
```

> socket.io requires all arguments to be provided no matter what. Thus, you can not omit any of the arguments given in examples below but have to provide values assumed to be default, at least. 

The following code excerpts are focusing on the inner part of emitting an action event and processing the response.

#### &lt;modelName>:list

This request is the websocket variant of [Model.list()](https://odem.hitchy.org/api/model.html#model-list). Supported arguments are 

* [query-related options](https://odem.hitchy.org/api/model.html#query-related-options) as supported by `Model.list()` and
* a boolean controlling `loadRecords` of [result-related options](https://odem.hitchy.org/api/model.html#result-related-options) supported by `Model.list()`. 

  > This boolean must be `true` to actually get the properties of retrieved items. Otherwise, listed items are represented by their UUIDs, only.

```javascript
socket.emit( "<modelName>:list", { limit: 10 }, false, response => {
    // - check for `response.success` or `response.error`
    // - process total count of items in `response.count`
    // - process requested excerpt of items in `response.items`
} );
```

Last argument is a callback to be invoked with the resulting response from server-side. `response` is an object consisting of truthy property `success` on success or `error` message in case of a failure. On success, property `count` is providing total count of matches and `items` is the list of matching items' properties.

#### &lt;modelName>:find

This request is searching for instances of selected model matching some query. It is invoking [Model.find()](https://odem.hitchy.org/api/model.html#model-find). Arguments are

* the [query](https://odem.hitchy.org/api/model.html#model-find) describing items to find,
* [query-related options](https://odem.hitchy.org/api/model.html#query-related-options) as supported by `Model.find()` and
* a boolean controlling `loadRecords` of [result-related options](https://odem.hitchy.org/api/model.html#result-related-options) supported by `Model.find()`.

  > This boolean must be `true` to actually get the properties of retrieved items. Otherwise, listed items are represented by their UUIDs, only.

```javascript
socket.emit( "<modelName>:find", {}, { limit: 10 }, true, response => {
    // - check for `response.success` or `response.error`
    // - process total count of items in `response.count`
    // - process requested excerpt of items in `response.items`
} );
```

Last argument is a callback to be invoked with the resulting response from server-side. `response` is an object consisting of truthy property `success` on success or `error` message in case of a failure. On success, property `count` is providing total count of matches and `items` is the selected excerpt from that list of matching items' each given by its properties including its UUID.

#### &lt;modelName>:create

This request is creating another instance of selected model assigning provided properties as initial values. Those properties are provided as serializable object.

```javascript
socket.emit( "<modelName>:create", { title: "important things" }, response => {
    // - check for `response.success` or `response.error`
    // - process properties of eventually created instance in `response.properties`
    // - created instance's UUID is available as `response.properties.uuid`
} );
```

Last argument is a callback to be invoked with the resulting response from server-side. `response` is an object consisting of truthy property `success` on success or `error` message in case of a failure. On success, `properties` of created instance are returned. These may be different from provided ones due to server-side constraints. The created item's UUID is added to that set of properties as `uuid`.

#### &lt;modelName>:read

This request is fetching a single instance's properties.

```javascript
socket.emit( "<modelName>:read", "12345678-1234-1234-1234-123456789012", response => {
    // - check for `response.success` or `response.error`
    // - process properties of fetched instance in `response.properties`
} );
```

Last argument is a callback to be invoked with the resulting response from server-side. `response` is an object consisting of truthy property `success` on success or `error` message in case of a failure. On success, `properties` of selected instance are returned.

#### &lt;modelName>:update

This request is updating an existing instance of model. The UUID of item to update and the values per property to assign are provided as arguments.

```javascript
socket.emit( "<modelName>:update", "12345678-1234-1234-1234-123456789012", { prio: 1 }, response => {
    // - check for `response.success` or `response.error`
    // - properties of updated instance are provided in `response.properties`
} );
```

Last argument is a callback to be invoked with the resulting response from server-side. `response` is an object consisting of truthy property `success` on success or `error` message in case of a failure. On success, `properties` of updated instance are returned. These may be different from provided ones due to server-side constraints.

#### &lt;modelName>:delete

This request is eventually completing the CRUD-support of this API by deleting an existing instance of model selected by its UUID in first argument.

```javascript
socket.emit( "<modelName>:delete", "12345678-1234-1234-1234-123456789012", response => {
    // - check for `response.success` or `response.error`
    // - deleted instance's UUID is available as `response.properties.uuid`
} );
```

Last argument is a callback to be invoked with the resulting response from server-side. `response` is an object consisting of truthy property `success` on success or `error` message in case of a failure. On success, `properties` of updated instance are returned.


### Notifications

[notifications of the document-oriented database](https://odem.hitchy.org/api/model.html#model-notifications) are forwarded as broadcast events named `<modelName>:changed`. Either event consists of 

- a type of change and 
- the changed item's properties (limited to its UUID on deletion).

The type of change is

* `created` on notifications about having created another instance of model,
* `changed` on notifications about having adjusted one or more properties of an existing instance of model and
* `removed` on notifications about having removed an existing instance of model.

:::danger Breaking change
Notification types have changed in v0.5.0 from `updated` to `changed` and from `deleted` to `removed` to match names used on server side.
:::

In last case the provided set of properties is limited to the instance's UUID. The common pattern on client-side for listening to server-side notifications looks like this:

```javascript
const socket = io( "/hitchy/odem" );

socket.on( "<modelName>:changed", info => {
    // type of change is in `info.change`, e.g. "created", "changed" or "removed"
    // process properties of changed instance in `info.properties`
} );
```

> There may be server-side restriction applying per event or per instance of model, thus some or all notifications might be omitted.