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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,10 @@ A workspace ID must be provided when using this method
and all requests will be scoped to that workspace.

```ts
// Pass as an option to the constructor
// Set the `SEAM_PERSONAL_ACCESS_TOKEN` and `SEAM_WORKSPACE_ID` environment variables
const seam = new SeamHttp()

// Pass as an option to the constructor
const seam = new SeamHttp({
personalAccessToken: 'your-personal-access-token',
workspaceId: 'your-workspace-id',
Expand Down Expand Up @@ -407,6 +409,9 @@ A Personal Access Token is scoped to a Seam Console user.
Obtain one from the Seam Console.

```ts
// Set the `SEAM_PERSONAL_ACCESS_TOKEN` environment variable
const seam = new SeamHttpMultiWorkspace()

// Pass as an option to the constructor
const seam = new SeamHttpMultiWorkspace({
personalAccessToken: 'your-personal-access-token',
Expand Down
4 changes: 4 additions & 0 deletions src/lib/seam/connect/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isSeamHttpRequestOption } from './parse-options.js'
import type { ResolveActionAttemptOptions } from './resolve-action-attempt.js'

export type SeamHttpMultiWorkspaceOptions =
| SeamHttpMultiWorkspaceOptionsFromEnv
| SeamHttpMultiWorkspaceOptionsWithClient
| SeamHttpMultiWorkspaceOptionsWithConsoleSessionToken
| SeamHttpMultiWorkspaceOptionsWithPersonalAccessToken
Expand All @@ -28,6 +29,9 @@ export interface SeamHttpFromPublishableKeyOptions

export interface SeamHttpOptionsFromEnv extends SeamHttpCommonOptions {}

export interface SeamHttpMultiWorkspaceOptionsFromEnv
extends SeamHttpCommonOptions {}

export interface SeamHttpMultiWorkspaceOptionsWithClient
extends SeamHttpRequestOptions {
client: Client
Expand Down
42 changes: 42 additions & 0 deletions src/lib/seam/connect/parse-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
isSeamHttpMultiWorkspaceOptionsWithClient,
isSeamHttpOptionsWithClient,
isSeamHttpOptionsWithClientSessionToken,
SeamHttpInvalidOptionsError,
type SeamHttpMultiWorkspaceOptions,
type SeamHttpOptions,
type SeamHttpRequestOptions,
Expand Down Expand Up @@ -76,9 +77,31 @@ const getNormalizedOptions = (
const apiKey =
'apiKey' in options ? options.apiKey : getApiKeyFromEnv(options)

const personalAccessToken =
'personalAccessToken' in options
? options.personalAccessToken
: getPersonalAccessTokenFromEnv(options)

const workspaceId =
'workspaceId' in options ? options.workspaceId : getWorkspaceIdFromEnv()

if (
apiKey != null &&
personalAccessToken != null &&
!('apiKey' in options) &&
!('personalAccessToken' in options)
) {
throw new SeamHttpInvalidOptionsError(
'Both SEAM_API_KEY and SEAM_PERSONAL_ACCESS_TOKEN environment variables are defined. Please use only one authentication method.',
)
}

return {
...options,
...(apiKey != null ? { apiKey } : {}),
...(personalAccessToken != null && workspaceId != null
? { personalAccessToken, workspaceId }
: {}),
...requestOptions,
}
}
Expand All @@ -98,6 +121,25 @@ const getApiKeyFromEnv = (
return globalThis.process?.env?.SEAM_API_KEY
}

const getPersonalAccessTokenFromEnv = (
options: SeamHttpOptions,
): string | null | undefined => {
if ('apiKey' in options && options.apiKey != null) {
return null
}
if ('clientSessionToken' in options && options.clientSessionToken != null) {
return null
}
if ('consoleSessionToken' in options && options.consoleSessionToken != null) {
return null
}
return globalThis.process?.env?.['SEAM_PERSONAL_ACCESS_TOKEN']
}

const getWorkspaceIdFromEnv = (): string | null | undefined => {
return globalThis.process?.env?.['SEAM_WORKSPACE_ID']
}

const getEndpointFromEnv = (): string | null | undefined => {
if (globalThis.process?.env?.SEAM_API_URL != null) {
// eslint-disable-next-line no-console
Expand Down
2 changes: 1 addition & 1 deletion src/lib/seam/connect/seam-http-multi-workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class SeamHttpMultiWorkspace {
client: Client
readonly defaults: Required<SeamHttpRequestOptions>

constructor(options: SeamHttpMultiWorkspaceOptions) {
constructor(options: SeamHttpMultiWorkspaceOptions = {}) {
const opts = parseOptions(options)
this.client = 'client' in opts ? opts.client : createClient(opts)
this.defaults = limitToSeamHttpRequestOptions(opts)
Expand Down
96 changes: 95 additions & 1 deletion test/seam/connect/env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import test from 'ava'
import { getTestServer } from 'fixtures/seam/connect/api.js'
import jwt from 'jsonwebtoken'

import { SeamHttp, SeamHttpInvalidOptionsError } from '@seamapi/http/connect'
import {
SeamHttp,
SeamHttpInvalidOptionsError,
SeamHttpMultiWorkspace,
} from '@seamapi/http/connect'

/*
* Tests in this file must run serially to ensure a clean environment for each test.
Expand Down Expand Up @@ -273,3 +277,93 @@ test.serial(
t.is(device.device_id, seed.august_device_1)
},
)

test.serial(
'SeamHttp: constructor uses SEAM_PERSONAL_ACCESS_TOKEN and SEAM_WORKSPACE_ID environment variables',
async (t) => {
const { seed, endpoint } = await getTestServer(t)
env['SEAM_PERSONAL_ACCESS_TOKEN'] = seed.seam_at1_token
env['SEAM_WORKSPACE_ID'] = seed.seed_workspace_1
const seam = new SeamHttp({ endpoint })
const device = await seam.devices.get({
device_id: seed.august_device_1,
})
t.is(device.workspace_id, seed.seed_workspace_1)
t.is(device.device_id, seed.august_device_1)
},
)

test.serial(
'SeamHttp: throws error when both SEAM_API_KEY and SEAM_PERSONAL_ACCESS_TOKEN are defined',
(t) => {
env.SEAM_API_KEY = 'some-api-key'
env['SEAM_PERSONAL_ACCESS_TOKEN'] = 'some-access-token'
env['SEAM_WORKSPACE_ID'] = 'some-workspace-id'
t.throws(() => new SeamHttp(), {
instanceOf: SeamHttpInvalidOptionsError,
message:
/Both SEAM_API_KEY and SEAM_PERSONAL_ACCESS_TOKEN environment variables are defined/,
})
},
)

test.serial(
'SeamHttp: personalAccessToken option overrides environment variables',
async (t) => {
const { seed, endpoint } = await getTestServer(t)
env['SEAM_PERSONAL_ACCESS_TOKEN'] = 'some-invalid-token'
env['SEAM_WORKSPACE_ID'] = seed.seed_workspace_1
const seam = new SeamHttp({
personalAccessToken: seed.seam_at1_token,
endpoint,
})
const device = await seam.devices.get({
device_id: seed.august_device_1,
})
t.is(device.workspace_id, seed.seed_workspace_1)
t.is(device.device_id, seed.august_device_1)
},
)

test.serial(
'SeamHttp: workspaceId option overrides environment variables',
async (t) => {
const { seed, endpoint } = await getTestServer(t)
env['SEAM_PERSONAL_ACCESS_TOKEN'] = seed.seam_at1_token
env['SEAM_WORKSPACE_ID'] = 'some-invalid-workspace'
const seam = new SeamHttp({
workspaceId: seed.seed_workspace_1,
endpoint,
})
const device = await seam.devices.get({
device_id: seed.august_device_1,
})
t.is(device.workspace_id, seed.seed_workspace_1)
t.is(device.device_id, seed.august_device_1)
},
)

test.serial(
'SeamHttpMultiWorkspace: constructor uses SEAM_PERSONAL_ACCESS_TOKEN environment variable',
async (t) => {
const { seed, endpoint } = await getTestServer(t)
env['SEAM_PERSONAL_ACCESS_TOKEN'] = seed.seam_at1_token
const multiWorkspace = new SeamHttpMultiWorkspace({ endpoint })
const workspaces = await multiWorkspace.workspaces.list()
t.true(workspaces.length > 0)
},
)

test.serial(
'SeamHttpMultiWorkspace: personalAccessToken option overrides environment variables',
async (t) => {
const { seed, endpoint } = await getTestServer(t)
env['SEAM_PERSONAL_ACCESS_TOKEN'] = 'some-invalid-token'
const multiWorkspace = new SeamHttpMultiWorkspace({
personalAccessToken: seed.seam_at1_token,
endpoint,
})
const workspaces = await multiWorkspace.workspaces.list()
t.true(workspaces.length > 0)
},
)