diff --git a/docs/classes/default.md b/docs/classes/default.md index 4367b731..6dd54202 100644 --- a/docs/classes/default.md +++ b/docs/classes/default.md @@ -33,14 +33,13 @@ ### constructor -• **new default**(`apiKey?`, `endpoint?`) +• **new default**(`apiKeyOrOptions?`) #### Parameters | Name | Type | | :------ | :------ | -| `apiKey?` | `string` | -| `endpoint` | `string` | +| `apiKeyOrOptions?` | `string` \| [`SeamClientOptions`](../interfaces/SeamClientOptions.md) | #### Overrides @@ -48,7 +47,7 @@ Routes.constructor #### Defined in -[src/index.ts:9](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L9) +[src/index.ts:38](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L38) ## Properties @@ -100,7 +99,7 @@ ___ #### Defined in -[src/index.ts:7](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L7) +[src/index.ts:36](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L36) ___ @@ -239,4 +238,4 @@ Routes.makeRequest #### Defined in -[src/index.ts:33](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L33) +[src/index.ts:68](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L68) diff --git a/docs/interfaces/SeamClientOptions.md b/docs/interfaces/SeamClientOptions.md new file mode 100644 index 00000000..7f093a70 --- /dev/null +++ b/docs/interfaces/SeamClientOptions.md @@ -0,0 +1,46 @@ +[seamapi](../README.md) / [Exports](../modules.md) / SeamClientOptions + +# Interface: SeamClientOptions + +## Table of contents + +### Properties + +- [apiKey](SeamClientOptions.md#apikey) +- [endpoint](SeamClientOptions.md#endpoint) +- [workspaceId](SeamClientOptions.md#workspaceid) + +## Properties + +### apiKey + +• `Optional` **apiKey**: `string` + +#### Defined in + +[src/index.ts:8](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L8) + +___ + +### endpoint + +• `Optional` **endpoint**: `string` + +Seam Endpoint to use, defaults to https://connect.getseam.com + +#### Defined in + +[src/index.ts:12](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L12) + +___ + +### workspaceId + +• `Optional` **workspaceId**: `string` + +Workspace if using session authentication, defaults to SEAM_WORKSPACE_ID +or undefined + +#### Defined in + +[src/index.ts:17](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L17) diff --git a/docs/modules.md b/docs/modules.md index d5595d0a..4e0473fb 100644 --- a/docs/modules.md +++ b/docs/modules.md @@ -43,6 +43,7 @@ - [OngoingAccessCode](interfaces/OngoingAccessCode.md) - [PendingActionAttempt](interfaces/PendingActionAttempt.md) - [SeamAPIErrorMetadata](interfaces/SeamAPIErrorMetadata.md) +- [SeamClientOptions](interfaces/SeamClientOptions.md) - [SuccessfulActionAttempt](interfaces/SuccessfulActionAttempt.md) - [TimeBoundAccessCode](interfaces/TimeBoundAccessCode.md) - [Workspace](interfaces/Workspace.md) @@ -66,6 +67,10 @@ - [NoiseDetectionDeviceType](modules.md#noisedetectiondevicetype) - [SuccessfulAPIResponse](modules.md#successfulapiresponse) +### Functions + +- [getSeamClientOptionsWithDefaults](modules.md#getseamclientoptionswithdefaults) + ## Type aliases ### APIResponse @@ -220,3 +225,23 @@ ___ #### Defined in [src/types/globals.ts:7](https://github.com/seamapi/seamapi-javascript/blob/main/src/types/globals.ts#L7) + +## Functions + +### getSeamClientOptionsWithDefaults + +▸ **getSeamClientOptionsWithDefaults**(`apiKeyOrOptions?`): [`SeamClientOptions`](interfaces/SeamClientOptions.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `apiKeyOrOptions?` | `string` \| [`SeamClientOptions`](interfaces/SeamClientOptions.md) | + +#### Returns + +[`SeamClientOptions`](interfaces/SeamClientOptions.md) + +#### Defined in + +[src/index.ts:20](https://github.com/seamapi/seamapi-javascript/blob/main/src/index.ts#L20) diff --git a/package.json b/package.json index e1073a9e..9fac8066 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "axios": "0.25.0", "change-case": "4.1.2", "eventemitter3": "4.0.7", + "lodash": "^4.17.21", "node-inspect-extracted": "1.1.0", "ora": "5.4.1", "p-retry": "4.6.1", @@ -50,6 +51,7 @@ "@semantic-release/npm": "9.0.0", "@semantic-release/release-notes-generator": "10.0.3", "@swc/core": "1.2.133", + "@types/lodash": "^4.14.179", "@types/node": "17.0.10", "@types/yargs": "17.0.8", "ajv": "8.9.0", diff --git a/src/cli/browser.ts b/src/cli/browser.ts index b1bb0f02..78d62a6c 100644 --- a/src/cli/browser.ts +++ b/src/cli/browser.ts @@ -4,6 +4,7 @@ import type Y from "yargs" import { Yargs } from "https://unpkg.com/yargs@16.0.0-alpha.3/browser.mjs" import EventEmitter from "eventemitter3" import TypedEmitter from "typed-emitter" +import { getSeamClientOptionsWithDefaults, SeamClientOptions } from "../" const yargsInstance = Yargs() as ReturnType @@ -13,12 +14,14 @@ type CLIEvents = { class BrowserCLI extends (EventEmitter as unknown as new () => TypedEmitter) { private instance = getCLI(yargsInstance).scriptName("seam") + private seamClientOptions: SeamClientOptions /** * Use the Seam CLI in the browser! */ - constructor(private apiKey?: string) { + constructor(apiKeyOrOptions?: string | SeamClientOptions) { super() + this.seamClientOptions = getSeamClientOptionsWithDefaults(apiKeyOrOptions) this.setUpShims() } @@ -34,15 +37,21 @@ class BrowserCLI extends (EventEmitter as unknown as new () => TypedEmitter((resolve, reject) => { // .parseAsync isn't available in v16, so we listen for the ending newline instead @@ -55,7 +64,7 @@ class BrowserCLI extends (EventEmitter as unknown as new () => TypedEmitter { if (error) { this.removeListener("data", onData) diff --git a/src/cli/lib/execute-command.ts b/src/cli/lib/execute-command.ts index c7a4d931..da06f4f0 100644 --- a/src/cli/lib/execute-command.ts +++ b/src/cli/lib/execute-command.ts @@ -3,6 +3,7 @@ import { Get } from "type-fest" import { paramCase } from "change-case" import Seam, { SeamAPIError } from "../.." import { GlobalOptions } from "./global-options" +import _ from "lodash" type ParametersByPath = Parameters< Exclude, Seam> @@ -32,7 +33,16 @@ const executeCommand = async ( .start() } - const seam = new Seam(executeArgs["api-key"]) + const seam = new Seam( + _.omitBy( + { + apiKey: executeArgs["api-key"], + endpoint: executeArgs["endpoint"], + workspaceId: executeArgs["workspace-id"], + }, + _.isUndefined + ) + ) let method: any = seam for (const path of methodName.split(".")) { diff --git a/src/cli/lib/global-options.ts b/src/cli/lib/global-options.ts index 3eaa76b3..a6d38ca5 100644 --- a/src/cli/lib/global-options.ts +++ b/src/cli/lib/global-options.ts @@ -15,7 +15,29 @@ export const getParserWithOptions = (yargsInstance: Argv) => describe: "Output JSON", type: "boolean", }) - .group(["api-key", "quiet", "json", "help", "version"], "Global Options:") + .option("endpoint", { + describe: "Seam API Endpoint (defaults to https://connect.getseam.com)", + hidden: true, + type: "string", + }) + .option("workspace-id", { + describe: + "Workspace Id to perform action against (only if using session key auth)", + hidden: true, + type: "string", + }) + .group( + [ + "api-key", + "endpoint", + "workspace-id", + "quiet", + "json", + "help", + "version", + ], + "Global Options:" + ) export type YargsWithGlobalOptions = ReturnType type ExtractGeneric = T extends Argv ? X : never diff --git a/src/index.ts b/src/index.ts index 0cb00856..b26762ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,18 +3,50 @@ import { SeamAPIError } from "./lib/api-error" import { Routes } from "./routes" import { ErroredAPIResponse, SuccessfulAPIResponse } from "./types/globals" +export interface SeamClientOptions { + /* Seam API Key */ + apiKey?: string + /** + * Seam Endpoint to use, defaults to https://connect.getseam.com + **/ + endpoint?: string + /** + * Workspace if using session authentication, defaults to SEAM_WORKSPACE_ID + * or undefined + **/ + workspaceId?: string +} + +export const getSeamClientOptionsWithDefaults = ( + apiKeyOrOptions?: string | SeamClientOptions +): SeamClientOptions => { + const seamClientDefaults: SeamClientOptions = { + apiKey: process?.env?.SEAM_API_KEY, + endpoint: process?.env?.SEAM_API_URL || "https://connect.getseam.com", + workspaceId: process?.env?.SEAM_WORKSPACE_ID, + } + if (typeof apiKeyOrOptions === "string") { + return { ...seamClientDefaults, apiKey: apiKeyOrOptions } + } else { + return { ...seamClientDefaults, ...apiKeyOrOptions } + } +} + class Seam extends Routes { private client: AxiosInstance - constructor( - apiKey?: string, - endpoint = process?.env?.SEAM_API_URL || "https://connect.getseam.com" - ) { + constructor(apiKeyOrOptions?: string | SeamClientOptions) { super() - if (!apiKey) { - apiKey = process.env.SEAM_API_KEY - } + const options = getSeamClientOptionsWithDefaults(apiKeyOrOptions) + const { apiKey, endpoint, workspaceId } = options + + const isRegularAPIKey = apiKey?.startsWith("seam_") + + if (isRegularAPIKey && 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." + ) if (!apiKey) { throw new Error( @@ -26,6 +58,9 @@ class Seam extends Routes { baseURL: endpoint, headers: { Authorization: `Bearer ${apiKey}`, + + // only needed for session key authentication + ...(!workspaceId ? {} : { "Seam-Workspace": workspaceId }), }, }) } diff --git a/tests b/tests index 2f23a39d..70adac60 160000 --- a/tests +++ b/tests @@ -1 +1 @@ -Subproject commit 2f23a39d9b8f8fa0fcd525bbbf720f6f03833078 +Subproject commit 70adac60f4fbe4172cc90bed5589dcc51689b0d8 diff --git a/yarn.lock b/yarn.lock index b1d77aed..939e74d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -417,6 +417,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== +"@types/lodash@^4.14.179": + version "4.14.179" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5" + integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w== + "@types/minimatch@*": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"