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
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,10 @@ const pages = seam.createPaginator(
const devices = await pages.flattenToArray()
```

### Interacting with Multiple Workspaces
### Requests without a Workspace in scope

Some Seam API endpoints interact with multiple workspaces.
The `SeamHttpMultiWorkspace` client is not bound to a specific workspace
Some Seam API endpoints do not require a workspace in scope.
The `SeamHttpWithoutWorkspace` client is not bound to a specific workspace
and may use those endpoints with an appropriate authentication method.

#### Personal Access Token
Expand All @@ -410,15 +410,15 @@ Obtain one from the Seam Console.

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

// Pass as an option to the constructor
const seam = new SeamHttpMultiWorkspace({
const seam = new SeamHttpWithoutWorkspace({
personalAccessToken: 'your-personal-access-token',
})

// Use the factory method
const seam = SeamHttpMultiWorkspace.fromPersonalAccessToken(
const seam = SeamHttpWithoutWorkspace.fromPersonalAccessToken(
'some-console-session-token',
)

Expand All @@ -433,12 +433,12 @@ This authentication method is only used by internal Seam applications.

```ts
// Pass as an option to the constructor
const seam = new SeamHttpMultiWorkspace({
const seam = new SeamHttpWithoutWorkspace({
consoleSessionToken: 'some-console-session-token',
})

// Use the factory method
const seam = SeamHttpMultiWorkspace.fromConsoleSessionToken(
const seam = SeamHttpWithoutWorkspace.fromConsoleSessionToken(
'some-console-session-token',
)

Expand Down
19 changes: 15 additions & 4 deletions codegen/layouts/endpoints.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

{{> route-imports }}
{{> route-imports-without-workspace }}

{{#each routeImports}}
import {
Expand All @@ -14,8 +15,12 @@ import {
} from './{{fileName}}'
{{/each}}

export class SeamHttpEndpoints {
export class {{className}} {
{{#if withoutWorkspace}}
{{> route-class-methods-without-workspace }}
{{else}}
{{> route-class-methods }}
{{/if}}

{{#each endpoints}}
get['{{path}}'](): {{> endpont-method-signature isFnType=true }}
Expand All @@ -36,8 +41,14 @@ export class SeamHttpEndpoints {
{{/each}}
}

export type SeamHttpEndpointQueryPaths = {{#each endpointReadPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
{{#if endpointReadPaths.length}}
export type {{typeNamePrefix}}QueryPaths = {{#each endpointReadPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
{{/if}}

export type SeamHttpEndpointPaginatedQueryPaths = {{#each endpointPaginatedPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
{{#if endpointPaginatedPaths.length}}
export type {{typeNamePrefix}}PaginatedQueryPaths = {{#each endpointPaginatedPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
{{/if}}

export type SeamHttpEndpointMutationPaths = {{#each endpointWritePaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
{{#if endpointWritePaths.length}}
export type {{typeNamePrefix}}MutationPaths = {{#each endpointWritePaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
client: Client
readonly defaults: Required<SeamHttpRequestOptions>
readonly ltsVersion = seamApiLtsVersion
static ltsVersion = seamApiLtsVersion

constructor(options: SeamHttpWithoutWorkspaceOptions = {}) {
const opts = parseOptions(options)
this.client = 'client' in opts ? opts.client : createClient(opts)
this.defaults = limitToSeamHttpRequestOptions(opts)
}

static fromClient(
client: SeamHttpWithoutWorkspaceOptionsWithClient['client'],
options: Omit<SeamHttpWithoutWorkspaceOptionsWithClient, 'client'> = {},
): {{className}}{
const constructorOptions = { ...options, client }
if (!isSeamHttpWithoutWorkspaceOptionsWithClient(constructorOptions)) {
throw new SeamHttpWithoutWorkspaceInvalidOptionsError('Missing client')
}
return new {{className}}(constructorOptions)
}

static fromConsoleSessionToken(
consoleSessionToken: SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken['consoleSessionToken'],
options: Omit<
SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
'consoleSessionToken'
> = {},
): {{className}}{
const constructorOptions = { ...options, consoleSessionToken }
if (
!isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken(
constructorOptions,
)
) {
throw new SeamHttpWithoutWorkspaceInvalidOptionsError(
'Missing consoleSessionToken',
)
}
return new {{className}}(constructorOptions)
}

static fromPersonalAccessToken(
personalAccessToken: SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken['personalAccessToken'],
options: Omit<
SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
'personalAccessToken'
> = {},
): {{className}}{
const constructorOptions = { ...options, personalAccessToken }
if (
!isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken(
constructorOptions,
)
) {
throw new SeamHttpWithoutWorkspaceInvalidOptionsError(
'Missing personalAccessToken',
)
}
return new {{className}}(constructorOptions)
}
14 changes: 14 additions & 0 deletions codegen/layouts/partials/route-imports-without-workspace.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { seamApiLtsVersion } from 'lib/lts-version.js'
import { type Client, createClient } from 'lib/seam/connect/client.js'
import {
isSeamHttpWithoutWorkspaceOptionsWithClient,
isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
type SeamHttpRequestOptions,
SeamHttpWithoutWorkspaceInvalidOptionsError,
type SeamHttpWithoutWorkspaceOptions,
type SeamHttpWithoutWorkspaceOptionsWithClient,
type SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
type SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
} from 'lib/seam/connect/options.js'
import { limitToSeamHttpRequestOptions, parseOptions } from 'lib/seam/connect/parse-options.js'
21 changes: 21 additions & 0 deletions codegen/layouts/without-workspace.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Automatically generated by codegen/smith.ts.
* Do not edit this file or add other files to this directory.
*/

{{> route-imports-without-workspace }}

import { SeamHttpWorkspaces } from 'lib/seam/connect/routes/workspaces/index.js'

export class SeamHttpWithoutWorkspace {
{{> route-class-methods-without-workspace }}

get workspaces(): Pick<SeamHttpWorkspaces, 'create' | 'list'> {
return SeamHttpWorkspaces.fromClient(this.client, this.defaults)
}
}

/**
* @deprecated Use SeamHttpWithoutWorkspace instead.
*/
export const SeamHttpMultiWorkspace = SeamHttpWithoutWorkspace
18 changes: 18 additions & 0 deletions codegen/lib/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ export const connect = (
setEndpointsLayoutContext(endpointFile, routes)
routeIndexes['']?.add('seam-http-endpoints.js')

const withoutWorkspaceKey = `${rootPath}/seam-http-without-workspace.ts`
files[withoutWorkspaceKey] = { contents: Buffer.from('\n') }
const withoutWorkspaceFile = files[withoutWorkspaceKey] as unknown as File
withoutWorkspaceFile.layout = 'without-workspace.hbs'
withoutWorkspaceFile.className = 'SeamHttpWithoutWorkspace'
routeIndexes['']?.add('seam-http-without-workspace.js')

const endpointsWithoutWorkspaceKey = `${rootPath}/seam-http-endpoints-without-workspace.ts`
files[endpointsWithoutWorkspaceKey] = { contents: Buffer.from('\n') }
const endpointWithoutWorkspaceFile = files[
endpointsWithoutWorkspaceKey
] as unknown as File
endpointWithoutWorkspaceFile.layout = 'endpoints.hbs'
setEndpointsLayoutContext(endpointWithoutWorkspaceFile, routes, {
withoutWorkspace: true,
})
routeIndexes['']?.add('seam-http-endpoints-without-workspace.js')

for (const node of nodes) {
const path = toFilePath(node.path)
const name = kebabCase(node.name)
Expand Down
56 changes: 37 additions & 19 deletions codegen/lib/layouts/endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Route } from '@seamapi/blueprint'
import type { Endpoint, Route } from '@seamapi/blueprint'

import {
type EndpointLayoutContext,
Expand All @@ -9,6 +9,8 @@ import {

export interface EndpointsLayoutContext {
className: string
typeNamePrefix: string
withoutWorkspace: boolean
endpoints: EndpointLayoutContext[]
endpointReadPaths: string[]
endpointPaginatedPaths: string[]
Expand All @@ -26,21 +28,33 @@ interface RouteImportLayoutContext {
export const setEndpointsLayoutContext = (
file: Partial<EndpointsLayoutContext>,
routes: Route[],
{ withoutWorkspace = false }: { withoutWorkspace?: boolean } = {},
): void => {
file.className = getClassName('Endpoints')
const endpointFilter = (endpoint: Endpoint): boolean =>
withoutWorkspace ? endpoint.workspaceScope !== 'required' : true

file.withoutWorkspace = withoutWorkspace
file.className = getClassName(
`Endpoints${withoutWorkspace ? 'WithoutWorkspace' : ''}`,
)
file.typeNamePrefix = getClassName(
`Endpoint${withoutWorkspace ? 'WithoutWorkspace' : ''}`,
)
file.skipClientSessionImport = true
file.endpoints = routes.flatMap((route) =>
route.endpoints.map((endpoint) =>
getEndpointLayoutContext(endpoint, route),
),
route.endpoints
.filter(endpointFilter)
.map((endpoint) => getEndpointLayoutContext(endpoint, route)),
)
file.endpointReadPaths = routes.flatMap((route) =>
route.endpoints
.filter(endpointFilter)
.filter(({ request }) => request.semanticMethod === 'GET')
.map(({ path }) => path),
)
file.endpointPaginatedPaths = routes.flatMap((route) =>
route.endpoints
.filter(endpointFilter)
.filter(
({ request, hasPagination }) =>
request.semanticMethod === 'GET' && hasPagination,
Expand All @@ -49,21 +63,25 @@ export const setEndpointsLayoutContext = (
)
file.endpointWritePaths = routes.flatMap((route) =>
route.endpoints
.filter(endpointFilter)
.filter(({ request }) => request.semanticMethod !== 'GET')
.map(({ path }) => path),
)
file.routeImports = routes.map((route) => {
const endpoints = route.endpoints.map((endpoint) =>
getEndpointLayoutContext(endpoint, route),
)
return {
className: getClassName(route.path),
fileName: `${toFilePath(route.path)}/index.js`,
typeNames: endpoints.flatMap((endpoint) => [
endpoint.parametersTypeName,
endpoint.optionsTypeName,
endpoint.requestTypeName,
]),
}
})
file.routeImports = routes
.filter((route) => route.endpoints.some(endpointFilter))
.map((route) => {
const endpoints = route.endpoints
.filter(endpointFilter)
.map((endpoint) => getEndpointLayoutContext(endpoint, route))

return {
className: getClassName(route.path),
fileName: `${toFilePath(route.path)}/index.js`,
typeNames: endpoints.flatMap((endpoint) => [
endpoint.parametersTypeName,
endpoint.optionsTypeName,
endpoint.requestTypeName,
]),
}
})
}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"axios-retry": "^4.4.2"
},
"devDependencies": {
"@seamapi/blueprint": "^0.51.0",
"@seamapi/blueprint": "^0.51.1",
"@seamapi/fake-seam-connect": "^1.77.0",
"@seamapi/smith": "^0.4.4",
"@seamapi/types": "1.420.2",
Expand Down
16 changes: 8 additions & 8 deletions src/lib/seam/connect/auth.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import {
isSeamHttpMultiWorkspaceOptionsWithConsoleSessionToken,
isSeamHttpMultiWorkspaceOptionsWithPersonalAccessToken,
isSeamHttpOptionsWithApiKey,
isSeamHttpOptionsWithClientSessionToken,
isSeamHttpOptionsWithConsoleSessionToken,
isSeamHttpOptionsWithPersonalAccessToken,
isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
SeamHttpInvalidOptionsError,
type SeamHttpMultiWorkspaceOptionsWithConsoleSessionToken,
type SeamHttpMultiWorkspaceOptionsWithPersonalAccessToken,
type SeamHttpOptionsWithApiKey,
type SeamHttpOptionsWithClientSessionToken,
type SeamHttpOptionsWithConsoleSessionToken,
type SeamHttpOptionsWithPersonalAccessToken,
type SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
type SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
} from './options.js'
import type { Options } from './parse-options.js'
import {
Expand Down Expand Up @@ -43,14 +43,14 @@ export const getAuthHeaders = (options: Options): Headers => {
}

if (
isSeamHttpMultiWorkspaceOptionsWithConsoleSessionToken(options) ||
isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken(options) ||
isSeamHttpOptionsWithConsoleSessionToken(options)
) {
return getAuthHeadersForConsoleSessionToken(options)
}

if (
isSeamHttpMultiWorkspaceOptionsWithPersonalAccessToken(options) ||
isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken(options) ||
isSeamHttpOptionsWithPersonalAccessToken(options)
) {
return getAuthHeadersForPersonalAccessToken(options)
Expand Down Expand Up @@ -142,7 +142,7 @@ const getAuthHeadersForConsoleSessionToken = ({
consoleSessionToken,
...options
}:
| SeamHttpMultiWorkspaceOptionsWithConsoleSessionToken
| SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken
| SeamHttpOptionsWithConsoleSessionToken): Headers => {
const workspaceId = 'workspaceId' in options ? options.workspaceId : undefined

Expand Down Expand Up @@ -180,7 +180,7 @@ const getAuthHeadersForPersonalAccessToken = ({
personalAccessToken,
...options
}:
| SeamHttpMultiWorkspaceOptionsWithPersonalAccessToken
| SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken
| SeamHttpOptionsWithPersonalAccessToken): Headers => {
const workspaceId = 'workspaceId' in options ? options.workspaceId : undefined

Expand Down
Loading