Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 39 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ or create a new empty client session.

A Personal Access Token is scoped to a Seam Console user.
Obtain one from the Seam Console.
A workspace id must be provided when using this method
A workspace ID must be provided when using this method
and all requests will be scoped to that workspace.

```ts
Expand All @@ -192,7 +192,7 @@ const seam = SeamHttp.fromPersonalAccessToken(

A Console Session Token is used by the Seam Console.
This authentication method is only used by internal Seam applications.
A workspace id must be provided when using this method
A workspace ID must be provided when using this method
and all requests will be scoped to that workspace.

```ts
Expand All @@ -212,53 +212,62 @@ const seam = SeamHttp.fromConsoleSessionToken(
### Action Attempts

Some asynchronous operations, e.g., unlocking a door, return an [action attempt].
Seam tracks the progress of requested operation and updates the action attempt.
Seam tracks the progress of the requested operation and updates the action attempt
when it succeeds or fails.

To make working with action attempts more convenient for applications,
this library provides the `waitForActionAttempt` option.
this library provides the `waitForActionAttempt` option and enables it by default.

Pass the option per-request,
When the `waitForActionAttempt` option is enabled, the SDK:

- Polls the action attempt up to the `timeout`
at the `pollingInterval` (both in milliseconds).
- Resolves with a fresh copy of the successful action attempt.
- Rejects with a `SeamActionAttemptFailedError` if the action attempt is unsuccessful.
- Rejects with a `SeamActionAttemptTimeoutError` if the action attempt is still pending when the `timeout` is reached.
- Both errors expose an `actionAttempt` property.

If you already have an action attempt ID
and want to wait for it to resolve, simply use

```ts
await seam.locks.unlockDoor(
{ device_id },
await seam.actionAttempts.get({ action_attempt_id })
```

Or, to get the current state of an action attempt by ID without waiting,

```ts
await seam.actionAttempts.get(
{ action_attempt_id },
{
waitForActionAttempt: true,
waitForActionAttempt: false,
},
)
```

or set the default option for the client:
To disable this behavior, set the default option for the client,

```ts
const seam = new SeamHttp({
apiKey: 'your-api-key',
waitForActionAttempt: true,
waitForActionAttempt: false,
})

await seam.locks.unlockDoor({ device_id })
```

If you have already have an action attempt id
and want to wait for it to resolve, simply use
or the behavior may be configured per-request,

```ts
await seam.actionAttempts.get(
{ action_attempt_id },
await seam.locks.unlockDoor(
{ device_id },
{
waitForActionAttempt: true,
waitForActionAttempt: false,
},
)
```

Using the `waitForActionAttempt` option:

- Polls the action attempt up to the `timeout`
at the `pollingInterval` (both in milliseconds).
- Resolves with a fresh copy of the successful action attempt.
- Rejects with a `SeamActionAttemptFailedError` if the action attempt is unsuccessful.
- Rejects with a `SeamActionAttemptTimeoutError` if the action attempt is still pending when the `timeout` is reached.
- Both errors expose an `actionAttempt` property.
The `pollingInterval` and `timeout` may be configured for the client or per-request, for example

```ts
import {
Expand All @@ -267,22 +276,19 @@ import {
isSeamActionAttemptTimeoutError,
} from '@seamapi/http/connect'

const seam = new SeamHttp('your-api-key')
const seam = new SeamHttp('your-api-key', {
waitForActionAttempt: {
pollingInterval: 1000,
timeout: 5000,
},
})

const [lock] = await seam.locks.list()

if (lock == null) throw new Error('No locks in this workspace')

try {
await seam.locks.unlockDoor(
{ device_id: lock.device_id },
{
waitForActionAttempt: {
pollingInterval: 1000,
timeout: 5000,
},
},
)
await seam.locks.unlockDoor({ device_id: lock.device_id })
console.log('Door unlocked')
} catch (err: unknown) {
if (isSeamActionAttemptFailedError(err)) {
Expand Down
7 changes: 1 addition & 6 deletions examples/unlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,7 @@ export const builder: Builder = {

export const handler: Handler<Options> = async ({ deviceId, seam, logger }) => {
try {
const actionAttempt = await seam.locks.unlockDoor(
{
device_id: deviceId,
},
{ waitForActionAttempt: true },
)
const actionAttempt = await seam.locks.unlockDoor({ device_id: deviceId })
logger.info({ actionAttempt }, 'unlocked')
} catch (err: unknown) {
if (isSeamActionAttemptFailedError<UnlockDoorActionAttempt>(err)) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/seam/connect/parse-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const getNormalizedOptions = (
: apiKeyOrOptions

const requestOptions = {
waitForActionAttempt: options.waitForActionAttempt ?? false,
waitForActionAttempt: options.waitForActionAttempt ?? true,
}

if (isSeamHttpOptionsWithClient(options)) {
Expand Down
14 changes: 10 additions & 4 deletions test/seam/connect/wait-for-action-attempt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ test('waitForActionAttempt: waits for pending action attempt', async (t) => {

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint,
waitForActionAttempt: false,
})

const actionAttempt = await seam.locks.unlockDoor({
Expand Down Expand Up @@ -48,6 +49,7 @@ test('waitForActionAttempt: returns successful action attempt', async (t) => {

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint,
waitForActionAttempt: false,
})

const actionAttempt = await seam.locks.unlockDoor({
Expand Down Expand Up @@ -87,6 +89,7 @@ test('waitForActionAttempt: times out while waiting for action attempt', async (

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint,
waitForActionAttempt: false,
})

const actionAttempt = await seam.locks.unlockDoor({
Expand Down Expand Up @@ -123,6 +126,7 @@ test('waitForActionAttempt: rejects when action attempt fails', async (t) => {

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint,
waitForActionAttempt: false,
})

const actionAttempt = await seam.locks.unlockDoor({
Expand Down Expand Up @@ -163,6 +167,7 @@ test('waitForActionAttempt: times out if waiting for polling interval', async (t

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint,
waitForActionAttempt: false,
})

const actionAttempt = await seam.locks.unlockDoor({
Expand Down Expand Up @@ -200,6 +205,7 @@ test('waitForActionAttempt: waits directly on returned action attempt', async (t

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint,
waitForActionAttempt: false,
})

const actionAttempt = await seam.locks.unlockDoor(
Expand All @@ -212,7 +218,7 @@ test('waitForActionAttempt: waits directly on returned action attempt', async (t
t.is(actionAttempt.status, 'success')
})

test('waitForActionAttempt: does not wait by default', async (t) => {
test('waitForActionAttempt: waits by default', async (t) => {
const { seed, endpoint } = await getTestServer(t)

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
Expand All @@ -223,22 +229,22 @@ test('waitForActionAttempt: does not wait by default', async (t) => {
device_id: seed.august_device_1,
})

t.is(actionAttempt.status, 'pending')
t.is(actionAttempt.status, 'success')
})

test('waitForActionAttempt: can set class default', async (t) => {
const { seed, endpoint } = await getTestServer(t)

const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint,
waitForActionAttempt: true,
waitForActionAttempt: false,
})

const actionAttempt = await seam.locks.unlockDoor({
device_id: seed.august_device_1,
})

t.is(actionAttempt.status, 'success')
t.is(actionAttempt.status, 'pending')
})

test('waitForActionAttempt: can set class default with object', async (t) => {
Expand Down