Skip to content

Commit 58edcfa

Browse files
authored
Merge pull request #355 from seamapi/evan/cx-411-generate-seamhttpwithoutworkspaceendpoints
Add SeamHttpEndpointsWithoutWorkspace
2 parents 3949d88 + fef5205 commit 58edcfa

22 files changed

+477
-187
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -397,10 +397,10 @@ const pages = seam.createPaginator(
397397
const devices = await pages.flattenToArray()
398398
```
399399

400-
### Interacting with Multiple Workspaces
400+
### Requests without a Workspace in scope
401401

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

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

411411
```ts
412412
// Set the `SEAM_PERSONAL_ACCESS_TOKEN` environment variable
413-
const seam = new SeamHttpMultiWorkspace()
413+
const seam = new SeamHttpWithoutWorkspace()
414414

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

420420
// Use the factory method
421-
const seam = SeamHttpMultiWorkspace.fromPersonalAccessToken(
421+
const seam = SeamHttpWithoutWorkspace.fromPersonalAccessToken(
422422
'some-console-session-token',
423423
)
424424

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

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

440440
// Use the factory method
441-
const seam = SeamHttpMultiWorkspace.fromConsoleSessionToken(
441+
const seam = SeamHttpWithoutWorkspace.fromConsoleSessionToken(
442442
'some-console-session-token',
443443
)
444444

codegen/layouts/endpoints.hbs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
{{> route-imports }}
7+
{{> route-imports-without-workspace }}
78

89
{{#each routeImports}}
910
import {
@@ -14,8 +15,12 @@ import {
1415
} from './{{fileName}}'
1516
{{/each}}
1617

17-
export class SeamHttpEndpoints {
18+
export class {{className}} {
19+
{{#if withoutWorkspace}}
20+
{{> route-class-methods-without-workspace }}
21+
{{else}}
1822
{{> route-class-methods }}
23+
{{/if}}
1924

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

39-
export type SeamHttpEndpointQueryPaths = {{#each endpointReadPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
44+
{{#if endpointReadPaths.length}}
45+
export type {{typeNamePrefix}}QueryPaths = {{#each endpointReadPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
46+
{{/if}}
4047

41-
export type SeamHttpEndpointPaginatedQueryPaths = {{#each endpointPaginatedPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
48+
{{#if endpointPaginatedPaths.length}}
49+
export type {{typeNamePrefix}}PaginatedQueryPaths = {{#each endpointPaginatedPaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
50+
{{/if}}
4251

43-
export type SeamHttpEndpointMutationPaths = {{#each endpointWritePaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
52+
{{#if endpointWritePaths.length}}
53+
export type {{typeNamePrefix}}MutationPaths = {{#each endpointWritePaths}}'{{.}}' {{#unless @last}} | {{/unless}}{{/each}}
54+
{{/if}}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
client: Client
2+
readonly defaults: Required<SeamHttpRequestOptions>
3+
readonly ltsVersion = seamApiLtsVersion
4+
static ltsVersion = seamApiLtsVersion
5+
6+
constructor(options: SeamHttpWithoutWorkspaceOptions = {}) {
7+
const opts = parseOptions(options)
8+
this.client = 'client' in opts ? opts.client : createClient(opts)
9+
this.defaults = limitToSeamHttpRequestOptions(opts)
10+
}
11+
12+
static fromClient(
13+
client: SeamHttpWithoutWorkspaceOptionsWithClient['client'],
14+
options: Omit<SeamHttpWithoutWorkspaceOptionsWithClient, 'client'> = {},
15+
): {{className}}{
16+
const constructorOptions = { ...options, client }
17+
if (!isSeamHttpWithoutWorkspaceOptionsWithClient(constructorOptions)) {
18+
throw new SeamHttpWithoutWorkspaceInvalidOptionsError('Missing client')
19+
}
20+
return new {{className}}(constructorOptions)
21+
}
22+
23+
static fromConsoleSessionToken(
24+
consoleSessionToken: SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken['consoleSessionToken'],
25+
options: Omit<
26+
SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
27+
'consoleSessionToken'
28+
> = {},
29+
): {{className}}{
30+
const constructorOptions = { ...options, consoleSessionToken }
31+
if (
32+
!isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken(
33+
constructorOptions,
34+
)
35+
) {
36+
throw new SeamHttpWithoutWorkspaceInvalidOptionsError(
37+
'Missing consoleSessionToken',
38+
)
39+
}
40+
return new {{className}}(constructorOptions)
41+
}
42+
43+
static fromPersonalAccessToken(
44+
personalAccessToken: SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken['personalAccessToken'],
45+
options: Omit<
46+
SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
47+
'personalAccessToken'
48+
> = {},
49+
): {{className}}{
50+
const constructorOptions = { ...options, personalAccessToken }
51+
if (
52+
!isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken(
53+
constructorOptions,
54+
)
55+
) {
56+
throw new SeamHttpWithoutWorkspaceInvalidOptionsError(
57+
'Missing personalAccessToken',
58+
)
59+
}
60+
return new {{className}}(constructorOptions)
61+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { seamApiLtsVersion } from 'lib/lts-version.js'
2+
import { type Client, createClient } from 'lib/seam/connect/client.js'
3+
import {
4+
isSeamHttpWithoutWorkspaceOptionsWithClient,
5+
isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
6+
isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
7+
type SeamHttpRequestOptions,
8+
SeamHttpWithoutWorkspaceInvalidOptionsError,
9+
type SeamHttpWithoutWorkspaceOptions,
10+
type SeamHttpWithoutWorkspaceOptionsWithClient,
11+
type SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
12+
type SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
13+
} from 'lib/seam/connect/options.js'
14+
import { limitToSeamHttpRequestOptions, parseOptions } from 'lib/seam/connect/parse-options.js'

codegen/layouts/without-workspace.hbs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Automatically generated by codegen/smith.ts.
3+
* Do not edit this file or add other files to this directory.
4+
*/
5+
6+
{{> route-imports-without-workspace }}
7+
8+
import { SeamHttpWorkspaces } from 'lib/seam/connect/routes/workspaces/index.js'
9+
10+
export class SeamHttpWithoutWorkspace {
11+
{{> route-class-methods-without-workspace }}
12+
13+
get workspaces(): Pick<SeamHttpWorkspaces, 'create' | 'list'> {
14+
return SeamHttpWorkspaces.fromClient(this.client, this.defaults)
15+
}
16+
}
17+
18+
/**
19+
* @deprecated Use SeamHttpWithoutWorkspace instead.
20+
*/
21+
export const SeamHttpMultiWorkspace = SeamHttpWithoutWorkspace

codegen/lib/connect.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ export const connect = (
7272
setEndpointsLayoutContext(endpointFile, routes)
7373
routeIndexes['']?.add('seam-http-endpoints.js')
7474

75+
const withoutWorkspaceKey = `${rootPath}/seam-http-without-workspace.ts`
76+
files[withoutWorkspaceKey] = { contents: Buffer.from('\n') }
77+
const withoutWorkspaceFile = files[withoutWorkspaceKey] as unknown as File
78+
withoutWorkspaceFile.layout = 'without-workspace.hbs'
79+
withoutWorkspaceFile.className = 'SeamHttpWithoutWorkspace'
80+
routeIndexes['']?.add('seam-http-without-workspace.js')
81+
82+
const endpointsWithoutWorkspaceKey = `${rootPath}/seam-http-endpoints-without-workspace.ts`
83+
files[endpointsWithoutWorkspaceKey] = { contents: Buffer.from('\n') }
84+
const endpointWithoutWorkspaceFile = files[
85+
endpointsWithoutWorkspaceKey
86+
] as unknown as File
87+
endpointWithoutWorkspaceFile.layout = 'endpoints.hbs'
88+
setEndpointsLayoutContext(endpointWithoutWorkspaceFile, routes, {
89+
withoutWorkspace: true,
90+
})
91+
routeIndexes['']?.add('seam-http-endpoints-without-workspace.js')
92+
7593
for (const node of nodes) {
7694
const path = toFilePath(node.path)
7795
const name = kebabCase(node.name)

codegen/lib/layouts/endpoints.ts

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Route } from '@seamapi/blueprint'
1+
import type { Endpoint, Route } from '@seamapi/blueprint'
22

33
import {
44
type EndpointLayoutContext,
@@ -9,6 +9,8 @@ import {
99

1010
export interface EndpointsLayoutContext {
1111
className: string
12+
typeNamePrefix: string
13+
withoutWorkspace: boolean
1214
endpoints: EndpointLayoutContext[]
1315
endpointReadPaths: string[]
1416
endpointPaginatedPaths: string[]
@@ -26,21 +28,33 @@ interface RouteImportLayoutContext {
2628
export const setEndpointsLayoutContext = (
2729
file: Partial<EndpointsLayoutContext>,
2830
routes: Route[],
31+
{ withoutWorkspace = false }: { withoutWorkspace?: boolean } = {},
2932
): void => {
30-
file.className = getClassName('Endpoints')
33+
const endpointFilter = (endpoint: Endpoint): boolean =>
34+
withoutWorkspace ? endpoint.workspaceScope !== 'required' : true
35+
36+
file.withoutWorkspace = withoutWorkspace
37+
file.className = getClassName(
38+
`Endpoints${withoutWorkspace ? 'WithoutWorkspace' : ''}`,
39+
)
40+
file.typeNamePrefix = getClassName(
41+
`Endpoint${withoutWorkspace ? 'WithoutWorkspace' : ''}`,
42+
)
3143
file.skipClientSessionImport = true
3244
file.endpoints = routes.flatMap((route) =>
33-
route.endpoints.map((endpoint) =>
34-
getEndpointLayoutContext(endpoint, route),
35-
),
45+
route.endpoints
46+
.filter(endpointFilter)
47+
.map((endpoint) => getEndpointLayoutContext(endpoint, route)),
3648
)
3749
file.endpointReadPaths = routes.flatMap((route) =>
3850
route.endpoints
51+
.filter(endpointFilter)
3952
.filter(({ request }) => request.semanticMethod === 'GET')
4053
.map(({ path }) => path),
4154
)
4255
file.endpointPaginatedPaths = routes.flatMap((route) =>
4356
route.endpoints
57+
.filter(endpointFilter)
4458
.filter(
4559
({ request, hasPagination }) =>
4660
request.semanticMethod === 'GET' && hasPagination,
@@ -49,21 +63,25 @@ export const setEndpointsLayoutContext = (
4963
)
5064
file.endpointWritePaths = routes.flatMap((route) =>
5165
route.endpoints
66+
.filter(endpointFilter)
5267
.filter(({ request }) => request.semanticMethod !== 'GET')
5368
.map(({ path }) => path),
5469
)
55-
file.routeImports = routes.map((route) => {
56-
const endpoints = route.endpoints.map((endpoint) =>
57-
getEndpointLayoutContext(endpoint, route),
58-
)
59-
return {
60-
className: getClassName(route.path),
61-
fileName: `${toFilePath(route.path)}/index.js`,
62-
typeNames: endpoints.flatMap((endpoint) => [
63-
endpoint.parametersTypeName,
64-
endpoint.optionsTypeName,
65-
endpoint.requestTypeName,
66-
]),
67-
}
68-
})
70+
file.routeImports = routes
71+
.filter((route) => route.endpoints.some(endpointFilter))
72+
.map((route) => {
73+
const endpoints = route.endpoints
74+
.filter(endpointFilter)
75+
.map((endpoint) => getEndpointLayoutContext(endpoint, route))
76+
77+
return {
78+
className: getClassName(route.path),
79+
fileName: `${toFilePath(route.path)}/index.js`,
80+
typeNames: endpoints.flatMap((endpoint) => [
81+
endpoint.parametersTypeName,
82+
endpoint.optionsTypeName,
83+
endpoint.requestTypeName,
84+
]),
85+
}
86+
})
6987
}

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
"axios-retry": "^4.4.2"
9999
},
100100
"devDependencies": {
101-
"@seamapi/blueprint": "^0.51.0",
101+
"@seamapi/blueprint": "^0.51.1",
102102
"@seamapi/fake-seam-connect": "^1.77.0",
103103
"@seamapi/smith": "^0.4.4",
104104
"@seamapi/types": "1.420.2",

src/lib/seam/connect/auth.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import {
2-
isSeamHttpMultiWorkspaceOptionsWithConsoleSessionToken,
3-
isSeamHttpMultiWorkspaceOptionsWithPersonalAccessToken,
42
isSeamHttpOptionsWithApiKey,
53
isSeamHttpOptionsWithClientSessionToken,
64
isSeamHttpOptionsWithConsoleSessionToken,
75
isSeamHttpOptionsWithPersonalAccessToken,
6+
isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
7+
isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
88
SeamHttpInvalidOptionsError,
9-
type SeamHttpMultiWorkspaceOptionsWithConsoleSessionToken,
10-
type SeamHttpMultiWorkspaceOptionsWithPersonalAccessToken,
119
type SeamHttpOptionsWithApiKey,
1210
type SeamHttpOptionsWithClientSessionToken,
1311
type SeamHttpOptionsWithConsoleSessionToken,
1412
type SeamHttpOptionsWithPersonalAccessToken,
13+
type SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken,
14+
type SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken,
1515
} from './options.js'
1616
import type { Options } from './parse-options.js'
1717
import {
@@ -43,14 +43,14 @@ export const getAuthHeaders = (options: Options): Headers => {
4343
}
4444

4545
if (
46-
isSeamHttpMultiWorkspaceOptionsWithConsoleSessionToken(options) ||
46+
isSeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken(options) ||
4747
isSeamHttpOptionsWithConsoleSessionToken(options)
4848
) {
4949
return getAuthHeadersForConsoleSessionToken(options)
5050
}
5151

5252
if (
53-
isSeamHttpMultiWorkspaceOptionsWithPersonalAccessToken(options) ||
53+
isSeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken(options) ||
5454
isSeamHttpOptionsWithPersonalAccessToken(options)
5555
) {
5656
return getAuthHeadersForPersonalAccessToken(options)
@@ -142,7 +142,7 @@ const getAuthHeadersForConsoleSessionToken = ({
142142
consoleSessionToken,
143143
...options
144144
}:
145-
| SeamHttpMultiWorkspaceOptionsWithConsoleSessionToken
145+
| SeamHttpWithoutWorkspaceOptionsWithConsoleSessionToken
146146
| SeamHttpOptionsWithConsoleSessionToken): Headers => {
147147
const workspaceId = 'workspaceId' in options ? options.workspaceId : undefined
148148

@@ -180,7 +180,7 @@ const getAuthHeadersForPersonalAccessToken = ({
180180
personalAccessToken,
181181
...options
182182
}:
183-
| SeamHttpMultiWorkspaceOptionsWithPersonalAccessToken
183+
| SeamHttpWithoutWorkspaceOptionsWithPersonalAccessToken
184184
| SeamHttpOptionsWithPersonalAccessToken): Headers => {
185185
const workspaceId = 'workspaceId' in options ? options.workspaceId : undefined
186186

0 commit comments

Comments
 (0)