Skip to content

Commit d4a007d

Browse files
razor-xseambot
andauthored
feat: Throw if anything under /seam is not undocumented (#265)
* Throw if anything under /seam is not undocumented * Update @seamapi/types * ci: Generate code --------- Co-authored-by: Seam Bot <seambot@getseam.com>
1 parent 72f1c7c commit d4a007d

File tree

6 files changed

+807
-162
lines changed

6 files changed

+807
-162
lines changed

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
@@ -74,7 +74,7 @@
7474
"zod": "^3.23.8"
7575
},
7676
"devDependencies": {
77-
"@seamapi/types": "1.764.0",
77+
"@seamapi/types": "^1.768.0",
7878
"@swc/core": "^1.11.29",
7979
"@types/node": "^24.10.9",
8080
"ava": "^6.0.1",

src/lib/blueprint.ts

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,18 +500,83 @@ export const createBlueprint = async (
500500
routes,
501501
context,
502502
)
503+
const events = await createEvents(openapiSchemas, routes, context)
504+
505+
assertSeamPathsAreUndocumented({
506+
routes,
507+
namespaces,
508+
resources,
509+
events,
510+
actionAttempts,
511+
})
503512

504513
return {
505514
title: openapi.info.title,
506515
routes,
507516
namespaces,
508517
resources,
509518
pagination: createPagination(pagination, openapiSchemas),
510-
events: await createEvents(openapiSchemas, routes, context),
519+
events,
511520
actionAttempts,
512521
}
513522
}
514523

524+
const isSeamPath = (path: string): boolean =>
525+
path === '/seam' || path.startsWith('/seam/')
526+
527+
const assertSeamPathsAreUndocumented = ({
528+
routes,
529+
namespaces,
530+
resources,
531+
events,
532+
actionAttempts,
533+
}: Pick<
534+
Blueprint,
535+
'routes' | 'namespaces' | 'resources' | 'events' | 'actionAttempts'
536+
>): void => {
537+
const offenders = [
538+
...routes.flatMap((route) => {
539+
const routeOffenders =
540+
isSeamPath(route.path) && !route.isUndocumented
541+
? [`route ${route.path}`]
542+
: []
543+
const endpointOffenders = route.endpoints.flatMap((endpoint) =>
544+
isSeamPath(endpoint.path) && !endpoint.isUndocumented
545+
? [`endpoint ${endpoint.path}`]
546+
: [],
547+
)
548+
549+
return [...routeOffenders, ...endpointOffenders]
550+
}),
551+
...namespaces.flatMap((namespace) =>
552+
isSeamPath(namespace.path) && !namespace.isUndocumented
553+
? [`namespace ${namespace.path}`]
554+
: [],
555+
),
556+
...resources.flatMap((resource) =>
557+
isSeamPath(resource.routePath) && !resource.isUndocumented
558+
? [`resource ${resource.routePath}`]
559+
: [],
560+
),
561+
...events.flatMap((event) =>
562+
isSeamPath(event.routePath) && !event.isUndocumented
563+
? [`event ${event.routePath}`]
564+
: [],
565+
),
566+
...actionAttempts.flatMap((actionAttempt) =>
567+
isSeamPath(actionAttempt.routePath) && !actionAttempt.isUndocumented
568+
? [`action_attempt ${actionAttempt.routePath}`]
569+
: [],
570+
),
571+
]
572+
573+
if (offenders.length > 0) {
574+
throw new Error(
575+
`All /seam entries must be marked undocumented. Found: ${offenders.join(', ')}`,
576+
)
577+
}
578+
}
579+
515580
const extractValidActionAttemptTypes = (
516581
schemas: Openapi['components']['schemas'],
517582
): string[] => {
@@ -1946,9 +2011,9 @@ const createActionAttempts = async (
19462011
const allPropertyKeys = new Set<string>()
19472012
for (const schema of actionAttemptSchemas) {
19482013
if (schema.properties != null) {
1949-
Object.keys(schema.properties).forEach((key) =>
1950-
allPropertyKeys.add(key),
1951-
)
2014+
Object.keys(schema.properties).forEach((key) => {
2015+
allPropertyKeys.add(key)
2016+
})
19522017
}
19532018
}
19542019

test/blueprint.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,40 @@ test('createBlueprint: with formatCode', async (t) => {
1616
})
1717
t.snapshot(blueprint, 'blueprint')
1818
})
19+
20+
test('createBlueprint: throws when a /seam entry is documented', async (t) => {
21+
const typesModule = TypesModuleSchema.parse(types)
22+
const openapi = structuredClone(typesModule.openapi)
23+
24+
openapi.paths['/seam/widgets/get'] = {
25+
post: {
26+
operationId: 'seamWidgetsGetPost',
27+
responses: {
28+
200: {
29+
content: {
30+
'application/json': {
31+
schema: {
32+
properties: {
33+
ok: { type: 'boolean' },
34+
},
35+
required: ['ok'],
36+
type: 'object',
37+
},
38+
},
39+
},
40+
description: 'OK',
41+
},
42+
},
43+
security: [],
44+
summary: '/seam/widgets/get',
45+
tags: ['/seam/widgets'],
46+
'x-response-key': null,
47+
'x-title': 'Get seam widgets',
48+
},
49+
}
50+
51+
await t.throwsAsync(() => createBlueprint({ ...typesModule, openapi }), {
52+
message:
53+
/All \/seam entries must be marked undocumented\. Found: .*\/seam\/widgets\/get/,
54+
})
55+
})

0 commit comments

Comments
 (0)