From 37cc03f8212cd942625329d381bbd6cc4bebe606 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 26 Apr 2023 16:23:33 -0700 Subject: [PATCH 1/6] Normalize getClientSessionToken options type --- docs/classes/Seam.md | 9 +++++++-- src/seam-connect/client.ts | 28 ++++++++++++---------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/classes/Seam.md b/docs/classes/Seam.md index beb87e71..f3142e72 100644 --- a/docs/classes/Seam.md +++ b/docs/classes/Seam.md @@ -330,13 +330,18 @@ ___ ### getClientSessionToken -▸ `Static` **getClientSessionToken**(`ops`): `Promise`<[`APIResponse`](../modules.md#apiresponse)<[`ClientSessionResponse`](../interfaces/ClientSessionResponse.md)\>\> +▸ `Static` **getClientSessionToken**(`options`): `Promise`<[`APIResponse`](../modules.md#apiresponse)<[`ClientSessionResponse`](../interfaces/ClientSessionResponse.md)\>\> #### Parameters | Name | Type | | :------ | :------ | -| `ops` | `CSTParams` | +| `options` | `Object` | +| `options.apiKey?` | `string` | +| `options.endpoint?` | `string` | +| `options.publishableKey?` | `string` | +| `options.userIdentifierKey` | `string` | +| `options.workspaceId?` | `string` | #### Returns diff --git a/src/seam-connect/client.ts b/src/seam-connect/client.ts index 23f030fb..a1adaf2b 100644 --- a/src/seam-connect/client.ts +++ b/src/seam-connect/client.ts @@ -159,24 +159,28 @@ export class Seam extends Routes { } } - static async getClientSessionToken( - ops: CSTParams - ): Promise> { + static async getClientSessionToken(options: { + publishableKey?: string + userIdentifierKey: string + endpoint?: string + workspaceId?: string + apiKey?: string + }): Promise> { const { apiKey, endpoint, axiosOptions } = - getSeamClientOptionsWithDefaults(ops) + getSeamClientOptionsWithDefaults(options) let headers: AxiosRequestHeaders = { ...axiosOptions?.headers, } - if (ops.publishableKey?.startsWith("seam_pk")) { + if (options.publishableKey?.startsWith("seam_pk")) { // frontend mode - headers["seam-publishable-key"] = ops.publishableKey + headers["seam-publishable-key"] = options.publishableKey } else if (apiKey?.startsWith("seam_")) { // backend mode headers["seam-api-key"] = apiKey } - if (ops.userIdentifierKey) { - headers["seam-user-identifier-key"] = ops.userIdentifierKey + if (options.userIdentifierKey) { + headers["seam-user-identifier-key"] = options.userIdentifierKey } else { throw new Error("userIdentifierKey is required") } @@ -204,11 +208,3 @@ export class Seam extends Routes { } } } - -type CSTParams = { - publishableKey?: string - userIdentifierKey: string - endpoint?: string - workspaceId?: string - apiKey?: string -} From fe72bad584d53323ffe26a2e10a38ae854189eaa Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 26 Apr 2023 16:35:52 -0700 Subject: [PATCH 2/6] feat: Add checks for keys on getClientSessionToken --- src/seam-connect/client.ts | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/seam-connect/client.ts b/src/seam-connect/client.ts index a1adaf2b..d3a96d36 100644 --- a/src/seam-connect/client.ts +++ b/src/seam-connect/client.ts @@ -168,22 +168,36 @@ export class Seam extends Routes { }): Promise> { const { apiKey, endpoint, axiosOptions } = getSeamClientOptionsWithDefaults(options) - let headers: AxiosRequestHeaders = { - ...axiosOptions?.headers, + + if (!options.userIdentifierKey) { + throw new Error("userIdentifierKey is required") } - if (options.publishableKey?.startsWith("seam_pk")) { - // frontend mode - headers["seam-publishable-key"] = options.publishableKey - } else if (apiKey?.startsWith("seam_")) { - // backend mode - headers["seam-api-key"] = apiKey + const getKeyHeaders = (): AxiosRequestHeaders => { + const { publishableKey } = options + if (publishableKey) { + if (!publishableKey.startsWith("seam_pk")) { + throw new Error("Invalid publishableKey") + } + return { "seam-publishable-key": publishableKey } + } + + if (apiKey) { + if (!apiKey?.startsWith("seam_")) { + throw new Error("Invalid apiKey") + } + return { "seam-api-key": apiKey } + } + + throw new Error("Must provide a publishableKey or apiKey") } - if (options.userIdentifierKey) { - headers["seam-user-identifier-key"] = options.userIdentifierKey - } else { - throw new Error("userIdentifierKey is required") + + const headers = { + "seam-user-identifier-key": options.userIdentifierKey, + ...getKeyHeaders(), + ...axiosOptions?.headers, } + const client = axios.create({ ...axiosOptions, baseURL: endpoint, From 2bd1a40882ae5755c92131f1c449990d99c358f5 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 26 Apr 2023 16:40:43 -0700 Subject: [PATCH 3/6] feat: Reuse makeRequest --- docs/classes/Seam.md | 2 +- src/seam-connect/client.ts | 61 +++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/docs/classes/Seam.md b/docs/classes/Seam.md index f3142e72..c02d71fe 100644 --- a/docs/classes/Seam.md +++ b/docs/classes/Seam.md @@ -349,4 +349,4 @@ ___ #### Defined in -[src/seam-connect/client.ts:162](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L162) +[src/seam-connect/client.ts:143](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L143) diff --git a/src/seam-connect/client.ts b/src/seam-connect/client.ts index d3a96d36..241cff25 100644 --- a/src/seam-connect/client.ts +++ b/src/seam-connect/client.ts @@ -1,8 +1,8 @@ import axios, { + Axios, AxiosInstance, AxiosRequestConfig, AxiosRequestHeaders, - AxiosResponse, } from "axios" import axiosRetry from "axios-retry" import { SeamAPIError, SeamMalformedInputError } from "../lib/api-error" @@ -13,7 +13,7 @@ import { SuccessfulAPIResponse, } from "../types/globals" import { version } from "../../package.json" -import { ClientSession, ClientSessionResponse } from "../types" +import { ClientSessionResponse } from "../types" export interface SeamClientOptions { /* Seam API Key */ @@ -137,26 +137,7 @@ export class Seam extends Routes { public async makeRequest( request: AxiosRequestConfig ): Promise> { - try { - const response = await this.client.request(request) - return response.data - } catch (error) { - if (axios.isAxiosError(error) && error.response) { - if (error.response.data.error?.type === "invalid_input") { - throw new SeamMalformedInputError( - error.response.data.error.validation_errors - ) - } - - throw new SeamAPIError( - error.response.status, - error.response.headers["seam-request-id"], - (error.response.data as ErroredAPIResponse).error - ) - } - - throw error - } + return makeRequest(this.client, request) } static async getClientSessionToken(options: { @@ -203,22 +184,36 @@ export class Seam extends Routes { baseURL: endpoint, headers, }) - try { - const response: AxiosResponse & { - data: { client_session: ClientSession } - } = await client.post( - "/internal/client_sessions/create", - {}, - // { headers: { "seam-sdk-version": version } } - {} - ) - return await response.data - } catch (error: any) { + + return makeRequest(client, { + method: "POST", + url: "/internal/client_sessions/create", + }) + } +} + +const makeRequest = async ( + client: Axios, + request: AxiosRequestConfig +): Promise> => { + try { + const response = await client.request(request) + return response.data + } catch (error) { + if (axios.isAxiosError(error) && error.response) { + if (error.response.data.error?.type === "invalid_input") { + throw new SeamMalformedInputError( + error.response.data.error.validation_errors + ) + } + throw new SeamAPIError( error.response.status, error.response.headers["seam-request-id"], (error.response.data as ErroredAPIResponse).error ) } + + throw error } } From cb8c00e526dbdb15e1c334c65ec6e7d776250141 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 26 Apr 2023 16:41:29 -0700 Subject: [PATCH 4/6] Move getAuthHeaders down --- docs/classes/Seam.md | 8 ++-- src/seam-connect/client.ts | 76 +++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/classes/Seam.md b/docs/classes/Seam.md index c02d71fe..9893d9f0 100644 --- a/docs/classes/Seam.md +++ b/docs/classes/Seam.md @@ -51,7 +51,7 @@ Routes.constructor #### Defined in -[src/seam-connect/client.ts:102](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L102) +[src/seam-connect/client.ts:64](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L64) ## Properties @@ -111,7 +111,7 @@ ___ #### Defined in -[src/seam-connect/client.ts:100](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L100) +[src/seam-connect/client.ts:62](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L62) ___ @@ -324,7 +324,7 @@ Routes.makeRequest #### Defined in -[src/seam-connect/client.ts:137](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L137) +[src/seam-connect/client.ts:99](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L99) ___ @@ -349,4 +349,4 @@ ___ #### Defined in -[src/seam-connect/client.ts:143](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L143) +[src/seam-connect/client.ts:105](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L105) diff --git a/src/seam-connect/client.ts b/src/seam-connect/client.ts index 241cff25..3cd7d6eb 100644 --- a/src/seam-connect/client.ts +++ b/src/seam-connect/client.ts @@ -58,44 +58,6 @@ export const getSeamClientOptionsWithDefaults = ( } } -const getAuthHeaders = ({ - clientSessionToken, - apiKey, - workspaceId, -}: { - clientSessionToken?: string - apiKey?: string - workspaceId?: string -}): Record => { - if (apiKey && clientSessionToken) { - throw new Error("You can't use clientSessionToken AND specify apiKey.") - } - - if (clientSessionToken) { - if (!clientSessionToken.startsWith("seam_cst")) { - throw new Error("clientSessionToken must start with seam_cst") - } - return { "client-session-token": clientSessionToken } - } - - if (apiKey) { - if (apiKey.startsWith("seam_cst")) { - console.warn( - "Using API Key as Client Session Token is deprecated. Please use the clientSessionToken option instead." - ) - return { "client-session-token": apiKey } - } - if (!apiKey.startsWith("seam_at") && workspaceId) - throw new Error( - "You can't use API Key Authentication AND specify a workspace. Your API Key only works for the workspace it was created in. To use Session Key Authentication with multi-workspace support, contact Seam support." - ) - return { authorization: `Bearer ${apiKey}` } - } - throw new Error( - "Must provide either clientSessionToken or apiKey (API Key or Access Token with Workspace ID)." - ) -} - export class Seam extends Routes { public client: AxiosInstance @@ -217,3 +179,41 @@ const makeRequest = async ( throw error } } + +const getAuthHeaders = ({ + clientSessionToken, + apiKey, + workspaceId, +}: { + clientSessionToken?: string + apiKey?: string + workspaceId?: string +}): Record => { + if (apiKey && clientSessionToken) { + throw new Error("You can't use clientSessionToken AND specify apiKey.") + } + + if (clientSessionToken) { + if (!clientSessionToken.startsWith("seam_cst")) { + throw new Error("clientSessionToken must start with seam_cst") + } + return { "client-session-token": clientSessionToken } + } + + if (apiKey) { + if (apiKey.startsWith("seam_cst")) { + console.warn( + "Using API Key as Client Session Token is deprecated. Please use the clientSessionToken option instead." + ) + return { "client-session-token": apiKey } + } + if (!apiKey.startsWith("seam_at") && workspaceId) + throw new Error( + "You can't use API Key Authentication AND specify a workspace. Your API Key only works for the workspace it was created in. To use Session Key Authentication with multi-workspace support, contact Seam support." + ) + return { authorization: `Bearer ${apiKey}` } + } + throw new Error( + "Must provide either clientSessionToken or apiKey (API Key or Access Token with Workspace ID)." + ) +} From 783ef7d32d97bc1b6b281e3da7bb949f330e7bca Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Mon, 1 May 2023 21:49:02 -0700 Subject: [PATCH 5/6] fix: Use correct default endpoint in browser --- docs/classes/Seam.md | 8 ++++---- src/seam-connect/client.ts | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/classes/Seam.md b/docs/classes/Seam.md index 9893d9f0..919a2b20 100644 --- a/docs/classes/Seam.md +++ b/docs/classes/Seam.md @@ -51,7 +51,7 @@ Routes.constructor #### Defined in -[src/seam-connect/client.ts:64](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L64) +[src/seam-connect/client.ts:65](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L65) ## Properties @@ -111,7 +111,7 @@ ___ #### Defined in -[src/seam-connect/client.ts:62](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L62) +[src/seam-connect/client.ts:63](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L63) ___ @@ -324,7 +324,7 @@ Routes.makeRequest #### Defined in -[src/seam-connect/client.ts:99](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L99) +[src/seam-connect/client.ts:100](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L100) ___ @@ -349,4 +349,4 @@ ___ #### Defined in -[src/seam-connect/client.ts:105](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L105) +[src/seam-connect/client.ts:106](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L106) diff --git a/src/seam-connect/client.ts b/src/seam-connect/client.ts index 3cd7d6eb..2e8cb4c2 100644 --- a/src/seam-connect/client.ts +++ b/src/seam-connect/client.ts @@ -38,12 +38,13 @@ export interface SeamClientOptions { export const getSeamClientOptionsWithDefaults = ( apiKeyOrOptions?: string | SeamClientOptions ): SeamClientOptions => { - let seamClientDefaults: SeamClientOptions = {} + const defaultEndpoint = "https://connect.getseam.com" + let seamClientDefaults: SeamClientOptions = { endpoint: defaultEndpoint } try { // try to get defaults from environment (for server-side use) seamClientDefaults = { apiKey: process?.env?.SEAM_API_KEY, - endpoint: process?.env?.SEAM_API_URL || "https://connect.getseam.com", + endpoint: process?.env?.SEAM_API_URL ?? defaultEndpoint, workspaceId: process?.env?.SEAM_WORKSPACE_ID, } } catch (error) { From 8a26dd74d81cc30971fc47a3adabcc51a2184e6d Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Mon, 1 May 2023 21:52:02 -0700 Subject: [PATCH 6/6] Use globalThis to simplify default options --- docs/classes/Seam.md | 8 ++++---- src/seam-connect/client.ts | 17 +++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/docs/classes/Seam.md b/docs/classes/Seam.md index 919a2b20..dd64de6e 100644 --- a/docs/classes/Seam.md +++ b/docs/classes/Seam.md @@ -51,7 +51,7 @@ Routes.constructor #### Defined in -[src/seam-connect/client.ts:65](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L65) +[src/seam-connect/client.ts:58](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L58) ## Properties @@ -111,7 +111,7 @@ ___ #### Defined in -[src/seam-connect/client.ts:63](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L63) +[src/seam-connect/client.ts:56](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L56) ___ @@ -324,7 +324,7 @@ Routes.makeRequest #### Defined in -[src/seam-connect/client.ts:100](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L100) +[src/seam-connect/client.ts:93](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L93) ___ @@ -349,4 +349,4 @@ ___ #### Defined in -[src/seam-connect/client.ts:106](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L106) +[src/seam-connect/client.ts:99](https://github.com/seamapi/javascript/blob/main/src/seam-connect/client.ts#L99) diff --git a/src/seam-connect/client.ts b/src/seam-connect/client.ts index 2e8cb4c2..ac5e6087 100644 --- a/src/seam-connect/client.ts +++ b/src/seam-connect/client.ts @@ -38,18 +38,11 @@ export interface SeamClientOptions { export const getSeamClientOptionsWithDefaults = ( apiKeyOrOptions?: string | SeamClientOptions ): SeamClientOptions => { - const defaultEndpoint = "https://connect.getseam.com" - let seamClientDefaults: SeamClientOptions = { endpoint: defaultEndpoint } - try { - // try to get defaults from environment (for server-side use) - seamClientDefaults = { - apiKey: process?.env?.SEAM_API_KEY, - endpoint: process?.env?.SEAM_API_URL ?? defaultEndpoint, - workspaceId: process?.env?.SEAM_WORKSPACE_ID, - } - } catch (error) { - // we are in a browser, so use the apiKeyOrOptions - // do nothing + const seamClientDefaults = { + apiKey: globalThis?.process?.env?.SEAM_API_KEY ?? undefined, + endpoint: + globalThis?.process?.env?.SEAM_API_URL ?? "https://connect.getseam.com", + workspaceId: globalThis?.process?.env?.SEAM_WORKSPACE_ID ?? undefined, } if (typeof apiKeyOrOptions === "string") { // for both browser and server, if apiKeyOrOptions is a string, use it as the apiKey, and merge with defaults