Skip to content

Commit d4601a7

Browse files
committed
Generate route endpoint types
1 parent f00b1a6 commit d4601a7

4 files changed

Lines changed: 57 additions & 32 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type {{requestTypeName}} = RouteRequest{{requestFormatSuffix}}<'{{path}}'>
2+
3+
export type {{responseTypeName}} = SetNonNullable<
4+
Required<RouteResponse<'{{path}}'>>
5+
>
6+
7+
export type {{optionsTypeName}} = {{#if returnsActionAttempt}}Pick<SeamHttpRequestOptions, 'waitForActionAttempt'>{{else}}never{{/if}}

codegen/layouts/partials/route-imports.hbs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,10 @@ import {
3838
import { SeamHttpRequest } from 'lib/seam/connect/seam-http-request.js'
3939
import { SeamPaginator } from 'lib/seam/connect/seam-paginator.js'
4040

41-
{{#if isClientSessionRoute}}
41+
{{#unless isClientSessionRoute}}
4242
import { SeamHttpClientSessions } from './client-sessions.js'
43-
{{/if}}
44-
45-
{{#if needsActionAttempts}}
46-
import {
47-
resolveActionAttempt,
48-
} from 'lib/seam/connect/resolve-action-attempt.js'
49-
{{#unless isActionAttemptsRoute}}
50-
import { SeamHttpActionAttempts } from './action-attempts.js'
5143
{{/unless}}
52-
{{/if}}
5344

5445
{{#each subroutes}}
5546
import { {{className}} } from './{{fileName}}'
5647
{{/each}}
57-
58-
{{#each namespaces}}
59-
import { {{className}} } from './{{fileName}}'
60-
{{/each}}

codegen/layouts/route.hbs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{> route-imports }}
22

3-
class {{className}} {
3+
export class {{className}} {
44
{{> route-class-methods }}
55

66
{{#each subroutes}}
@@ -13,3 +13,8 @@ class {{className}} {
1313

1414
{{/each}}
1515
}
16+
17+
{{#each endpoints}}
18+
{{> route-class-endpoint-export }}
19+
20+
{{/each}}

codegen/lib/connect.ts

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Blueprint, Namespace, Route } from '@seamapi/blueprint'
1+
import type { Blueprint, Endpoint, Namespace, Route } from '@seamapi/blueprint'
22
import { camelCase, kebabCase, pascalCase } from 'change-case'
33
import type Metalsmith from 'metalsmith'
44

@@ -83,14 +83,17 @@ interface RouteLayoutContext {
8383
className: string
8484
endpoints: EndpointLayoutContext[]
8585
subroutes: SubrouteLayoutContext[]
86-
namespaces: SubrouteLayoutContext[]
8786
isClientSessionRoute: boolean
88-
isActionAttemptsRoute: boolean
89-
needsActionAttempts: boolean
9087
}
9188

9289
interface EndpointLayoutContext {
93-
routeName: string
90+
path: string
91+
requestFormat: 'params' | 'body'
92+
requestTypeName: string
93+
responseTypeName: string
94+
requestFormatSuffix: string
95+
optionsTypeName: string
96+
returnsActionAttempt: boolean
9497
}
9598

9699
interface SubrouteLayoutContext {
@@ -108,20 +111,17 @@ const setRouteLayoutContext = (
108111
blueprint: Blueprint,
109112
): void => {
110113
file.className = getClassName(node?.path ?? null)
111-
112-
file.needsActionAttempts =
113-
node != null &&
114-
'endpoints' in node &&
115-
node.endpoints.some(
116-
(e) =>
117-
e.response.responseType === 'resource' &&
118-
e.response.resourceType === 'action_attempt',
119-
)
120-
121114
file.isClientSessionRoute = node?.name === '/client_sessions'
122-
file.isActionAttemptsRoute = node?.name === '/action_attempts'
123115

124-
file.endpoints = [] // TODO
116+
file.endpoints = []
117+
if (node != null && 'endpoints' in node) {
118+
const endpoints = node.endpoints.filter(
119+
({ isUndocumented }) => !isUndocumented,
120+
)
121+
file.endpoints = endpoints.map((endpoint) =>
122+
getEndpointLayoutContext(endpoint, node),
123+
)
124+
}
125125

126126
file.subroutes = [...blueprint.routes, ...blueprint.namespaces]
127127
.sort((n1, n2) => n1.name.localeCompare(n2.name))
@@ -138,3 +138,29 @@ const getSubrouteLayoutContext = (
138138
className: getClassName(route.path),
139139
}
140140
}
141+
142+
const getEndpointLayoutContext = (
143+
endpoint: Endpoint,
144+
route: Pick<Route, 'name'>,
145+
): EndpointLayoutContext => {
146+
const prefix = `${pascalCase(route.name)}${pascalCase(endpoint.name)}`
147+
148+
const requestFormat = ['GET', 'DELETE'].includes(
149+
endpoint.request.semanticMethod,
150+
)
151+
? 'params'
152+
: 'body'
153+
154+
const requestFormatSuffix = pascalCase(requestFormat)
155+
return {
156+
path: endpoint.path,
157+
requestFormat,
158+
requestFormatSuffix,
159+
returnsActionAttempt:
160+
endpoint.response.responseType === 'resource' &&
161+
endpoint.response.resourceType === 'action_attempt',
162+
requestTypeName: `${prefix}${requestFormatSuffix}`,
163+
responseTypeName: `${prefix}Response`,
164+
optionsTypeName: `${prefix}Options`,
165+
}
166+
}

0 commit comments

Comments
 (0)