Skip to content

Commit

Permalink
add store, historyStart, and client HTTP API docs for v1.3.0 release
Browse files Browse the repository at this point in the history
  • Loading branch information
bensie committed Oct 13, 2024
1 parent 90b6d34 commit eab5bec
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 52 deletions.
85 changes: 44 additions & 41 deletions docs/connections/claims.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ For wildcards that overlap, any explicit `false` is used as the value and cannot

Each object inside the channels object accepts [`messages`](#channels.messages) and [`subscribe`](#channels.subscribe) attributes.

### `historyStart` {#channels.historyStart}

`NumericDate` (optional) - Allows the connection to make HTTP API requests to load past messages stored on the channel prior to the time when the current connection's channel subscription was created, where the specified time is the oldest retrievable message. If supplied, the history start time must be expressed as a Unix timestamp - the number of seconds since the Unix epoch. By default, stored message retreval is limited to messages that were published during the lifetime of the active channel subscription.

The following allows retieving stored messages going back to `2024-10-11 00:00:00 UTC`, which is `1728604800` as a Unix timestamp.

```json
{
"channels": {
"mychannel": {
// highlight-next-line
"historyStart": 1728604800,
"subscribe": true
}
}
}
```

If you wanted to allow access to the entire message history of a channel without specifying a particular time, setting `historyStart` to `0` will do the trick.

### `messages` {#channels.messages}

`Object` (optional) - Manages the permissions and directives for client-initiated messages that are published directly to the WebSocket on the channel(s) for the specified events. Each object key is the name of an event and can include asterisks (\*) anywhere in the string to denote wildcards. Each object value is another object with the settings for that event or event wildcard.
Expand Down Expand Up @@ -134,16 +154,16 @@ In the following example, `chat` client event messages will trigger server-side

#### `publish` {#channels.messages.publish}

The following allows this connection to subscribe to the channel "mychannel" and publish client-initiated messages with the "is-typing" event name.
`Boolean` (optional) - The following allows this connection to subscribe to the channel "mychannel" and publish client-initiated messages with the "is-typing" event name.

```json
{
"channels": {
"mychannel": {
"subscribe": true,
// highlight-next-line
"messages": {
"is-typing": {
// highlight-next-line
"publish": true
}
}
Expand All @@ -159,9 +179,9 @@ Wildcards are supported in event names using asterisks (\*). The following allow
"channels": {
"mychannel": {
"subscribe": true,
// highlight-next-line
"messages": {
"*": {
// highlight-next-line
"publish": true
}
}
Expand Down Expand Up @@ -195,77 +215,60 @@ In the following example, since `chat.*` includes `chat.admin`, this connection

:::

### `subscribe` {#channels.subscribe}
#### `store` {#channels.messages.store}

`Boolean` (optional) - Whether or not to allow the connection to subscribe to the channel(s) specified by the object key. Default is `false`.
`Integer` (optional) - If [client-initiated message publishing](#channels.messages.publish) are permitted for this channel, the `store` attribute specifies the number of seconds that written messages from this connection are retained in the database after being sent. `0` is the default if unspecified, which does not store the message. `-1` keeps messages forever. The highest number you can specify here is `3155695200`, roughly 100 years.

The following sets `subscribe` to `true` on "mychannel".
The following allows sending ephemeral "is-typing" events and saved "chat" events to "mychannel", storing each "chat" message for about 1 year (365 days \* 24 hours \* 60 minutes \* 60 seconds) = 31,536,000 seconds.

```json
{
"channels": {
"mychannel": {
// highlight-next-line
"subscribe": true
"messages": {
"is-typing": {
"publish": true
},
"chat": {
"publish": true,
// highlight-next-line
"store": 31536000
}
}
}
}
}
```

You can also explicitly set `subscribe` to `false`, preventing subscriptions to this channel. An explicit `false` for a channel will always cause a deny, regardless of other permissions (such as wildcards) that may set this value to `true`.

```json
{
"channels": {
"mychannel": {
// highlight-next-line
"subscribe": false
}
}
}
```
### `subscribe` {#channels.subscribe}

<!--### `subscriptionsCountStat`
`Boolean` (optional) - Whether or not to allow the connection to subscribe to the channel(s) specified by the object key. Default is `false`.

`Boolean` (optional) - If set to `true`, this connection will receive a `hotsock.subscriptionsCountStat` message containing the number of members connected to this channel each time someone subscribes or unsubscribes. Default is `false`.
The following sets `subscribe` to `true` on "mychannel".

```json
{
"channels": {
"mychannel": {
// highlight-next-line
"subscriptionsCountStat": true
"subscribe": true
}
}
}
```

:::warning
Consider the cost of enabling this, particularly for channels with a large number of subscribers. Each subscriber who has this enabled receives a count stat update every time
:::-->

<!-- #### `store`

`Integer` (optional) - If [client-initiated messages](#messages) are permitted for this channel, the `store` attribute specifies the number of seconds that written messages from this connection are retained in the database after being sent. `0` is the default, which does not save message data. `-1` keeps messages forever.

The following allows sending ephemeral "is-typing" events and stored "message.markAsRead" events to "mychannel", storing each "message.markAsRead" message for 30 days (30 days \* 24 hours \* 60 minutes \* 60 seconds) = 2,592,000 seconds.
You can also explicitly set `subscribe` to `false`, preventing subscriptions to this channel. An explicit `false` for a channel will always cause a deny, regardless of other permissions (such as wildcards) that may set this value to `true`.

```json
{
"channels": {
"mychannel": {
"messages": {
"is-typing": {
"publish": true
},
"message.markAsRead": {
"store": 2592000
}
}
// highlight-next-line
"subscribe": false
}
}
}
```-->
```

## `connectionId` {#connectionId}

Expand Down
209 changes: 209 additions & 0 deletions docs/connections/client-http-api.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
---
sidebar_label: Client HTTP API
toc_max_heading_level: 4
---

# Client HTTP API

In addition to WebSocket interactions, connected clients can also make use of an HTTP API for publishing messages and listing message history for channels. All requests **_must_ be `POST` requests** and **_must_ have URL-encoded `connectionId` and `connectionSecret`** query parameters set.

## Determine your API URL

Your installation's HTTP API URL can be found in your CloudFormation stack outputs screen under [`HttpApiUrl`](../installation/initial-setup.mdx#HttpApiUrl). Example: `https://r6zcm2.lambda-url.us-east-1.on.aws/`

:::note
Custom Domains are not currently supported for HTTP API endpoints.
:::

## Authentication

In the `hotsock.connected` message received when a WebSocket connection is established, the `data` payload contains the `connectionId` and `connectionSecret`, which can be used to authenticate HTTP API requests.

```json
{
"event": "hotsock.connected",
"data": {
// highlight-next-line
"connectionId": "JCnTZd_KoAMCF-A=",
"connectionExpiresAt": "2024-10-13T19:09:23Z",
// highlight-next-line
"connectionSecret": "1FfpK4PHV1B7ZryUSCsN"
},
"meta": { "uid": null, "umd": null }
}
```

Using the `connectionId` and `connectionSecret` from this message, you can now construct authenticated API calls by setting these as query parameters to all HTTP API requests.

For example, the URL with required query parameters for publishing a message would be:

```
https://r6zcm2.lambda-url.us-east-1.on.aws/connection/publishMessage?connectionId=JCnTZd_KoAMCF-A%3d&connectionSecret=1FfpK4PHV1B7ZryUSCsN
```

:::tip
Notice the **%3d** in `JCnTZd_KoAMCF-A%3d`, replacing the **=** in the original connection ID `JCnTZd_KoAMCF-A=`. Connection IDs typically have an equals sign as their suffix, so this value must be URL encoded for the URL to be parsed correctly.
:::

## `connection/listMessages` {#listMessages}

List the message history for a channel. By default, a connection can only list messages for channels that they are subscribed to, limited to messages that were published during the timeframe of the active subscription on the current WebSocket connection. If you subscribed to a channel 2 minutes ago, you'll only be able to list message history on that channel for the past 2 minutes.

When issuing a connect or subscribe token, specify the [`historyStart`](./claims.mdx#channels.historyStart) claim to expand visibility to message history beyond the lifetime of the active subscription.

This endpoint will return up to 100 messages or up to 1MB of data, whichever comes first. Fetch subsequent pages using `before` or `after`, depending on your use case.

#### `after` {#listMessages.after}

String (optional) - Load messages after (newer than) the message with the ID specified here. Has no effect if `reverse` is `true`.

Message IDs are [ULIDs](https://github.com/ulid/spec), making them time-sortable by their value. If you receive a response containing 100 messages and have `reverse` set to `false` (default), you can get the next 100 messages by setting `after` to the last message ID you received.

Example: `01JA3HVNF6E89HDT1ABRFWJ8C8`

#### `before` {#listMessages.before}

String (optional) - Load messages before (older than) the message with the ID specified here. Has no effect if `reverse` is `false` (which is the default).

Message IDs are [ULIDs](https://github.com/ulid/spec), making them time-sortable by their value. If you receive a response containing 100 messages and have `reverse` set to `true`, you can get the next 100 older messages by setting `before` to the last message ID you received.

Example: `01JA3HVNF6E89HDT1ABRFWJ8C8`

#### `channel` {#listMessages.channel}

String (required) - The name of the channel to query message history.

#### `reverse` {#listMessages.reverse}

Boolean (optional) - If set to `true`, lists messages from newest to oldest. Default is `false`.

### Example

The following will load up to 100 of the most recent messages from the `my-channel` channel.

#### Request

```javascript
fetch(
"https://r6zcm2.lambda-url.us-east-1.on.aws/connection/listMessages?connectionId=fjlb_eHLIAMCKRg%3d&connectionSecret=SZy32Etv0KIbe4Jod6KH",
{
method: "POST",
body: JSON.stringify({ channel: "my-channel", reverse: true }),
}
)
```

#### Response

```json
{
"messages": [
{
"id": "01JA3S0TNEC2QBYMZRBMCEXGNW",
"event": "my-event",
"channel": "my-channel",
"data": "Nope.",
"meta": {
"uid": "Pam",
"umd": null
}
},
{
"id": "01JA3S0P7FB7WVYS67X316M32S",
"event": "my-event",
"channel": "my-channel",
"data": "Wow. Can we make it a different moment?",
"meta": {
"uid": "Jim",
"umd": null
}
},
{
"id": "01JA3S0GB6S3WNTV2S1RJ421TH",
"event": "my-event",
"channel": "my-channel",
"data": "Yup.",
"meta": {
"uid": "Pam",
"umd": null
}
},
{
"id": "01JA3S0BR0H7HD4CZ54KA99PAQ",
"event": "my-event",
"channel": "my-channel",
"data": "That was the moment that you liked me?",
"meta": {
"uid": "Jim",
"umd": null
}
},
{
"id": "01JA3S05K6024HXRJN2Q9W0YMB",
"event": "my-event",
"channel": "my-channel",
"data": "You came up to my desk and said, 'This may sound weird, and there's no reason for me to know this, but that mixed berry yogurt you're about to eat has expired.'",
"meta": {
"uid": "Pam",
"umd": null
}
}
]
}
```

## `connection/publishMessage` {#publishMessage}

Publish a message to a channel. For `publishMessage` requests to succeed, the connection must already be subscribed to the channel and must have [`publish`](./claims.mdx#channels.messages.publish) permissions granted to the client.

:::tip
You might wonder when it's appropriate to send a message [via the WebSocket](https://github.com/hotsock/hotsock-js?tab=readme-ov-file#publish-messages-to-a-channel) or via this HTTP API. Sending via the WebSocket is extremely low-overhead and fast because the connection is already established, but it's also fire-and-forget - neither receipt nor a response are guaranteed and unless [`echo`](./claims.mdx#channels.messages.echo) is enabled, you have no indication that the message was sent successfully.

HTTP API calls give you the control of a full HTTP request/response. If you receive a `202 Accepted` response, you can be certain that the message was received by the server.
:::

#### `channel`

`String` (required) - The name of the channel where this message will be published. This can be any string up to 128 characters, but must not contain any asterisk (`*`), number sign (`#`), comma (`,`), or whitespace/newline characters.

#### `data`

`JSON` (optional) - Up to 32KiB of custom data specific to your application. This can be anything that is valid JSON - object, array, string, number, boolean, or `null`. Binary data must be Base64-encoded to a string.

#### `eagerIdGeneration`

`Boolean` (optional) - If you need to store a copy of the message ID that is published to the channel for follow-up requests, set this to `true`. Default is `false`. [Learn more](../server-api/publish-messages.mdx#message-format.eagerIdGeneration)

#### `event`

`String` (required) - The name of the event to publish to the channel. This can be any string up to 128 characters, but must not begin with `hotsock.` and must not contain any asterisk (`*`) characters.

### Example

The following will publish a message to the `my-channel` channel.

#### Request

```javascript
fetch(
"https://r6zcm2.lambda-url.us-east-1.on.aws/connection/publishMessage?connectionId=fjlb_eHLIAMCKRg%3d&connectionSecret=SZy32Etv0KIbe4Jod6KH",
{
method: "POST",
body: JSON.stringify({
channel: "my-channel",
event: "my-event",
data: "Hi 👋",
}),
}
)
```

#### Response

```json
{
"id": null,
"channel": "my-channel",
"event": "my-event"
}
```
2 changes: 1 addition & 1 deletion docs/connections/connect-and-subscribe.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Set the token value in the `token` query parameter (note the `?token=eyJ...` por
```
wscat -c "wss://1ycut2oy9h.execute-api.us-east-1.amazonaws.com/v1?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTM5NjM5MDUsImNoYW5uZWxzIjp7ImdldC1zdGFydGVkLXdpdGgtaG90c29jayI6eyJzdWJzY3JpYmUiOnRydWV9fSwic2NvcGUiOiJjb25uZWN0In0.Ls62aNTexRTZGJfp5ei-hUO4XwhEn5hwgxBuUPD77k4"
Connected (press CTRL+C to quit)
< {"event":"hotsock.connected","data":{"connectionId":"JCnTZd_KoAMCF-A="}}
< {"event":"hotsock.connected","data":{"connectionId":"JCnTZd_KoAMCF-A=","connectionExpiresAt":"2024-10-13T19:09:23Z","connectionSecret":"1FfpK4PHV1B7ZryUSCsN"},"meta":{"uid":null,"umd":null}}
>
```

Expand Down
2 changes: 1 addition & 1 deletion docs/server-api/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ The data object type for this event is `channel`.

## `hotsock.messagePublished` {#hotsock.messagePublished}

This event is sent whenever a message is published to Hotsock with `emitPubSubEvent` enabled. For server-initiated messages, set [`emitPubSubEvent`](./publish-messages.mdx#message-format--emitPubSubEvent) when publishing. For client-initiated messages, set the [`emitPubSubEvent` claim](../connections/claims.mdx#channels.messages.emitPubSubEvent) to `true` for desired channel events.
This event is sent whenever a message is published to Hotsock with `emitPubSubEvent` enabled. For server-initiated messages, set [`emitPubSubEvent`](./publish-messages.mdx#message-format.emitPubSubEvent) when publishing. For client-initiated messages, set the [`emitPubSubEvent` claim](../connections/claims.mdx#channels.messages.emitPubSubEvent) to `true` for desired channel events.

### `type` {#hotsock.messagePublished--type}

Expand Down
Loading

0 comments on commit eab5bec

Please sign in to comment.