Skip to content

Commit fa9cc21

Browse files
fix: Do not generate undocumented routes and endpoints (#315)
* Update response key prop * Do not generate undocumented routes and endpoints * ci: Generate code * Account for namespace routes such as /acs * ci: Generate code --------- Co-authored-by: Seam Bot <[email protected]>
1 parent 5cc36c3 commit fa9cc21

14 files changed

+73
-1404
lines changed

generate-routes.ts

+73-12
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ async function main(): Promise<void> {
3131
])
3232
}
3333

34-
const openapiResponseKeyProp = 'x-fern-sdk-return-value'
34+
const openapiResponseKeyProp = 'x-response-key'
3535

3636
const routePaths = [
3737
'/access_codes',
@@ -124,9 +124,9 @@ interface ClassMeta {
124124
}
125125

126126
const createRoutes = (): Route[] => {
127-
const paths = Object.keys(openapi.paths)
127+
const allOpenapiPaths = Object.keys(openapi.paths)
128128

129-
const unmatchedEndpointPaths = paths
129+
const unmatchedEndpointPaths = allOpenapiPaths
130130
.filter(
131131
(path) =>
132132
!routePaths.some((routePath) => isEndpointUnderRoute(path, routePath)),
@@ -141,19 +141,42 @@ const createRoutes = (): Route[] => {
141141
)
142142
}
143143

144-
return routePaths.map(createRoute)
145-
}
144+
const routesToGenerate = routePaths.filter(
145+
(routePath) =>
146+
hasAtLeastOneDocumentedEndpoint(routePath) || isNamespaceRoute(routePath),
147+
)
146148

147-
const createRoute = (routePath: (typeof routePaths)[number]): Route => {
148-
const endpointPaths = Object.keys(openapi.paths).filter((path) =>
149-
isEndpointUnderRoute(path, routePath),
149+
return routesToGenerate.map((routePath) =>
150+
createRoute(routePath, routesToGenerate),
150151
)
152+
}
153+
154+
const createRoute = (
155+
routePath: (typeof routePaths)[number],
156+
documentedRoutePaths: ReadonlyArray<(typeof routePaths)[number]>,
157+
): Route => {
158+
const endpointPaths = Object.entries(openapi.paths)
159+
.filter(([path, pathSchema]) => {
160+
if (!isEndpointUnderRoute(path, routePath)) {
161+
return false
162+
}
163+
164+
return 'post' in pathSchema && !('x-undocumented' in pathSchema.post)
165+
})
166+
.map(([path]) => path)
151167

152168
const namespace = routePath.split('/').join('_').slice(1)
153169

170+
const subresources = (routePathSubresources[routePath] ?? []).filter(
171+
(subresource) => {
172+
const subresourcePath = `${routePath}/${subresource}`
173+
return documentedRoutePaths.some((path) => path === subresourcePath)
174+
},
175+
)
176+
154177
return {
155178
namespace,
156-
subresources: routePathSubresources[routePath] ?? [],
179+
subresources,
157180
endpoints: endpointPaths.map((endpointPath) =>
158181
createEndpoint(namespace, routePath, endpointPath),
159182
),
@@ -245,6 +268,46 @@ const isEndpointUnderRoute = (
245268
endpointPath.startsWith(routePath) &&
246269
endpointPath.split('/').length - 1 === routePath.split('/').length
247270

271+
const hasAtLeastOneDocumentedEndpoint = (
272+
routePath: (typeof routePaths)[number],
273+
): boolean => {
274+
const endpointsUnderRoute = Object.keys(openapi.paths).filter((path) =>
275+
isEndpointUnderRoute(path, routePath),
276+
)
277+
278+
if (endpointsUnderRoute.length === 0) {
279+
return false
280+
}
281+
282+
return endpointsUnderRoute.some((path) => {
283+
if (!isOpenapiPath(path)) return false
284+
285+
const pathSchema = openapi.paths[path]
286+
if (!('post' in pathSchema)) return false
287+
288+
return !('x-undocumented' in pathSchema.post)
289+
})
290+
}
291+
292+
/**
293+
* Determines if a route is a namespace route by checking if it has defined subresources
294+
* and if any of those subresources have corresponding route paths.
295+
* (e.g., "/acs" which contains "/acs/users", "/acs/systems", etc.)
296+
* These routes should be generated even if they don't have direct endpoints themselves.
297+
*/
298+
function isNamespaceRoute(routePath: (typeof routePaths)[number]): boolean {
299+
const subresources = routePathSubresources[routePath] ?? []
300+
if (subresources.length === 0) {
301+
return false
302+
}
303+
304+
return subresources.some((subresource) => {
305+
const subresourcePath =
306+
`${routePath}/${subresource}` as (typeof routePaths)[number]
307+
return routePaths.includes(subresourcePath)
308+
})
309+
}
310+
248311
const renderRoute = (route: Route, { constructors }: ClassMeta): string => `
249312
/*
250313
* Automatically generated by generate-routes.ts.
@@ -386,9 +449,7 @@ const renderClassMethodOptions = ({
386449
resource,
387450
}: Pick<Endpoint, 'resource'>): string => {
388451
if (resource === 'action_attempt') {
389-
return `options: ${renderClassMethodOptionsTypeDef({
390-
resource,
391-
})} = {},`
452+
return `options: ${renderClassMethodOptionsTypeDef({ resource })} = {},`
392453
}
393454
return ''
394455
}

src/lib/seam/connect/routes/acs-access-groups-unmanaged.ts

-209
This file was deleted.

src/lib/seam/connect/routes/acs-access-groups.ts

-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)