Skip to content

Commit 4c9f082

Browse files
feat: Dynamically determine endpoint resources (#87)
* Use x-fern-sdk-return-value to dynamically determine endpoint resources * ci: Format code * ci: Format code * Move the declaration of openapiResponseKeyProp * Update seamapi/types * ci: Generate code * Add /access_codes/simulate and /noise_sensors/simulate to routePathSubresources * ci: Generate code * Remove deriveResourceFromSchema * ci: Generate code * Use resource deriving func only for GET response schemas --------- Co-authored-by: Seam Bot <[email protected]>
1 parent 31673b7 commit 4c9f082

11 files changed

+412
-75
lines changed

generate-routes.ts

Lines changed: 23 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ async function main(): Promise<void> {
3131
])
3232
}
3333

34+
const openapiResponseKeyProp = 'x-fern-sdk-return-value'
35+
3436
const routePaths = [
3537
'/access_codes',
38+
'/access_codes/simulate',
3639
'/access_codes/unmanaged',
3740
'/acs',
3841
'/acs/access_groups',
@@ -54,6 +57,7 @@ const routePaths = [
5457
'/networks',
5558
'/noise_sensors',
5659
'/noise_sensors/noise_thresholds',
60+
'/noise_sensors/simulate',
5761
'/phones',
5862
'/phones/simulate',
5963
'/thermostats',
@@ -67,7 +71,7 @@ const routePaths = [
6771
const routePathSubresources: Partial<
6872
Record<(typeof routePaths)[number], string[]>
6973
> = {
70-
'/access_codes': ['unmanaged'],
74+
'/access_codes': ['unmanaged', 'simulate'],
7175
'/acs': [
7276
'access_groups',
7377
'credential_pools',
@@ -79,41 +83,11 @@ const routePathSubresources: Partial<
7983
],
8084
'/phones': ['simulate'],
8185
'/devices': ['unmanaged', 'simulate'],
82-
'/noise_sensors': ['noise_thresholds'],
86+
'/noise_sensors': ['noise_thresholds', 'simulate'],
8387
'/thermostats': ['climate_setting_schedules'],
8488
'/user_identities': ['enrollment_automations'],
8589
}
8690

87-
const ignoredEndpointPaths = [
88-
'/access_codes/simulate/create_unmanaged_access_code',
89-
'/connect_webviews/view',
90-
'/health',
91-
'/health/get_health',
92-
'/health/get_service_health',
93-
'/health/service/[service_name]',
94-
'/noise_sensors/simulate/trigger_noise_threshold',
95-
'/workspaces/reset_sandbox',
96-
] as const
97-
98-
const endpointResources: Partial<
99-
Record<
100-
keyof typeof openapi.paths,
101-
null | 'action_attempt' | 'noise_threshold'
102-
>
103-
> = {
104-
// Set all ignored endpoints null to simplify code generation.
105-
...ignoredEndpointPaths.reduce((acc, cur) => ({ ...acc, [cur]: null }), {}),
106-
107-
// These endpoints return a deprecated action attempt or resource.
108-
'/access_codes/delete': null,
109-
'/access_codes/unmanaged/delete': null,
110-
'/access_codes/update': null,
111-
'/noise_sensors/noise_thresholds/create': 'noise_threshold',
112-
'/noise_sensors/noise_thresholds/delete': null,
113-
'/noise_sensors/noise_thresholds/update': null,
114-
'/thermostats/climate_setting_schedules/update': null,
115-
} as const
116-
11791
interface Route {
11892
namespace: string
11993
endpoints: Endpoint[]
@@ -139,14 +113,10 @@ interface ClassMeta {
139113
const createRoutes = (): Route[] => {
140114
const paths = Object.keys(openapi.paths)
141115

142-
const unmatchedEndpointPaths = paths
143-
.filter(
144-
(path) =>
145-
!routePaths.some((routePath) => isEndpointUnderRoute(path, routePath)),
146-
)
147-
.filter(
148-
(path) => !(ignoredEndpointPaths as unknown as string[]).includes(path),
149-
)
116+
const unmatchedEndpointPaths = paths.filter(
117+
(path) =>
118+
!routePaths.some((routePath) => isEndpointUnderRoute(path, routePath)),
119+
)
150120

151121
if (unmatchedEndpointPaths.length > 0) {
152122
throw new Error(
@@ -206,28 +176,28 @@ const deriveResource = (
206176
endpointPath: string,
207177
method: Method,
208178
): string | null => {
209-
if (isEndpointResource(endpointPath)) {
210-
return endpointResources[endpointPath] ?? null
211-
}
212-
213179
if (isOpenapiPath(endpointPath)) {
214180
const spec = openapi.paths[endpointPath]
215181
const methodKey = method.toLowerCase()
216182

217183
if (methodKey === 'post' && 'post' in spec) {
218-
const response = spec.post.responses[200]
219-
if (!('content' in response)) return null
220-
return deriveResourceFromSchema(
221-
response.content['application/json']?.schema?.properties ?? {},
222-
)
184+
const postSpec = spec.post
185+
const openapiEndpointResource =
186+
openapiResponseKeyProp in postSpec
187+
? postSpec[openapiResponseKeyProp]
188+
: null
189+
190+
return openapiEndpointResource
223191
}
224192

225193
if (methodKey === 'get' && 'get' in spec) {
226194
const response = spec.get.responses[200]
195+
227196
if (!('content' in response)) {
228197
throw new Error(`Missing resource for ${method} ${endpointPath}`)
229198
}
230-
return deriveResourceFromSchema(
199+
200+
return deriveResourceFromSchemaForGetRequest(
231201
response.content['application/json']?.schema?.properties ?? {},
232202
)
233203
}
@@ -236,7 +206,9 @@ const deriveResource = (
236206
throw new Error(`Could not derive resource for ${method} ${endpointPath}`)
237207
}
238208

239-
const deriveResourceFromSchema = (properties: object): string | null =>
209+
const deriveResourceFromSchemaForGetRequest = (
210+
properties: object,
211+
): string | null =>
240212
Object.keys(properties).filter((key) => key !== 'ok')[0] ?? null
241213

242214
const deriveSemanticMethod = (methods: string[]): Method => {
@@ -248,10 +220,6 @@ const deriveSemanticMethod = (methods: string[]): Method => {
248220
throw new Error(`Could not find valid method in ${methods.join(', ')}`)
249221
}
250222

251-
const isEndpointResource = (
252-
key: string,
253-
): key is keyof typeof endpointResources => key in endpointResources
254-
255223
const isOpenapiPath = (key: string): key is keyof typeof openapi.paths =>
256224
key in openapi.paths
257225

package-lock.json

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

src/lib/seam/connect/routes/access-codes-simulate.ts

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

src/lib/seam/connect/routes/access-codes.ts

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

0 commit comments

Comments
 (0)