diff --git a/src/lib/blueprint.ts b/src/lib/blueprint.ts index d717b57..14d7301 100644 --- a/src/lib/blueprint.ts +++ b/src/lib/blueprint.ts @@ -306,14 +306,15 @@ export const createBlueprint = async ( formatCode, } - const resources = createResources(openapi.components.schemas) + const routes = await createRoutes(openapi.paths, context) + const resources = createResources(openapi.components.schemas, routes) return { title: openapi.info.title, - routes: await createRoutes(openapi.paths, context), + routes, resources, - events: createEvents(openapi.components.schemas, resources), - actionAttempts: createActionAttempts(openapi.components.schemas), + events: createEvents(openapi.components.schemas, resources, routes), + actionAttempts: createActionAttempts(openapi.components.schemas, routes), } } @@ -788,6 +789,7 @@ const createParameter = ( export const createResources = ( schemas: Openapi['components']['schemas'], + routes: Route[], ): Record => { return Object.entries(schemas).reduce>( (resources, [schemaName, schema]) => { @@ -798,12 +800,13 @@ export const createResources = ( parsedEvent.oneOf, ) const eventSchema: OpenapiSchema = { + 'x-route-path': parsedEvent['x-route-path'], properties: commonProperties, type: 'object', } return { ...resources, - [schemaName]: createResource(schemaName, eventSchema), + [schemaName]: createResource(schemaName, eventSchema, routes), } } @@ -812,7 +815,7 @@ export const createResources = ( if (isValidResourceSchema) { return { ...resources, - [schemaName]: createResource(schemaName, schema), + [schemaName]: createResource(schemaName, schema, routes), } } @@ -825,13 +828,20 @@ export const createResources = ( const createResource = ( schemaName: string, schema: OpenapiSchema, + routes: Route[], ): Resource => { + const routePath = validateRoutePath( + schemaName, + schema['x-route-path'], + routes, + ) + return { resourceType: schemaName, properties: createProperties(schema.properties ?? {}, [schemaName]), description: schema.description ?? '', isDeprecated: schema.deprecated ?? false, - routePath: schema['x-route-path'] ?? '', + routePath, deprecationMessage: schema['x-deprecated'] ?? '', isUndocumented: (schema['x-undocumented'] ?? '').length > 0, undocumentedMessage: schema['x-undocumented'] ?? '', @@ -840,6 +850,21 @@ const createResource = ( } } +const validateRoutePath = ( + resourceName: string, + routePath: string | undefined, + routes: Route[], +): string => { + if (routePath == null || routePath.length === 0) { + throw new Error(`Resource ${resourceName} is missing a route path`) + } + if (!routes.some((r) => r.path === routePath)) { + throw new Error(`Route path ${routePath} not found in routes`) + } + + return routePath +} + const createResponse = ( operation: OpenapiOperation, path: string, @@ -1089,6 +1114,7 @@ export const getPreferredMethod = ( const createEvents = ( schemas: Openapi['components']['schemas'], resources: Record, + routes: Route[], ): EventResource[] => { const eventSchema = schemas['event'] if ( @@ -1115,7 +1141,7 @@ const createEvents = ( ) return { - ...createResource('event', schema as OpenapiSchema), + ...createResource('event', schema as OpenapiSchema, routes), eventType, targetResourceType: targetResourceType ?? null, } @@ -1125,6 +1151,7 @@ const createEvents = ( const createActionAttempts = ( schemas: Openapi['components']['schemas'], + routes: Route[], ): ActionAttempt[] => { const actionAttemptSchema = schemas['action_attempt'] if ( @@ -1155,6 +1182,7 @@ const createActionAttempts = ( processedActionTypes.add(actionType) const schemaWithStandardStatus: OpenapiSchema = { + 'x-route-path': actionAttemptSchema['x-route-path'], ...schema, properties: { ...schema.properties, @@ -1169,6 +1197,7 @@ const createActionAttempts = ( const resource = createResource( 'action_attempt', schemaWithStandardStatus, + routes, ) return { diff --git a/src/lib/openapi/schemas.ts b/src/lib/openapi/schemas.ts index c78fd3b..330c5d5 100644 --- a/src/lib/openapi/schemas.ts +++ b/src/lib/openapi/schemas.ts @@ -115,6 +115,7 @@ export const ResourceSchema = z.object({ }) export const EventResourceSchema = z.object({ + 'x-route-path': z.string().default(''), discriminator: z.object({ propertyName: z.string() }), oneOf: z.array(ResourceSchema), }) diff --git a/test/fixtures/types/openapi.ts b/test/fixtures/types/openapi.ts index 23692b3..ef6fbd8 100644 --- a/test/fixtures/types/openapi.ts +++ b/test/fixtures/types/openapi.ts @@ -84,7 +84,7 @@ export default { }, }, required: ['plane_id', 'name'], - 'x-route-path': '/planes', + 'x-route-path': '/transport/air/planes', }, deprecated_resource: { type: 'object', @@ -99,7 +99,7 @@ export default { required: ['deprecated_resource_id'], deprecated: true, 'x-deprecated': 'This resource is deprecated', - 'x-route-path': '/deprecated/resources', + 'x-route-path': '/deprecated/undocumented', }, draft_resource: { type: 'object', @@ -113,7 +113,7 @@ export default { }, required: ['draft_resource_id'], 'x-draft': 'This resource is draft', - 'x-route-path': '/draft/resources', + 'x-route-path': '/draft', }, undocumented_resource: { type: 'object', @@ -127,9 +127,10 @@ export default { }, required: ['undocumented_resource_id'], 'x-undocumented': 'This resource is undocumented', - 'x-route-path': '/undocumented/resources', + 'x-route-path': '/deprecated/undocumented', }, event: { + 'x-route-path': '/events', oneOf: [ { type: 'object', @@ -162,6 +163,7 @@ export default { ], }, action_attempt: { + 'x-route-path': '/action_attempts', oneOf: [ { type: 'object', @@ -474,5 +476,40 @@ export default { 'x-title': 'Draft endpoint', }, }, + '/action_attempts/get': { + post: { + operationId: 'actionAttemptsGetPost', + responses: { + 200: { + content: { + 'application/json': { + schema: { + properties: { + ok: { type: 'boolean' }, + action_attempt: { + $ref: '#/components/schemas/action_attempt', + }, + }, + required: ['action_attempt', 'ok'], + type: 'object', + }, + }, + }, + description: 'Get an action attempt.', + }, + 400: { description: 'Bad Request' }, + 401: { description: 'Unauthorized' }, + }, + security: [ + { + api_key: [], + }, + ], + summary: '/action_attempts/get', + tags: ['/action_attempts'], + 'x-response-key': 'action_attempt', + 'x-title': 'Get an action attempt', + }, + }, }, } diff --git a/test/snapshots/blueprint.test.ts.md b/test/snapshots/blueprint.test.ts.md index 48f3959..5e09ba9 100644 --- a/test/snapshots/blueprint.test.ts.md +++ b/test/snapshots/blueprint.test.ts.md @@ -101,7 +101,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, ], @@ -1211,7 +1211,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, ], diff --git a/test/snapshots/blueprint.test.ts.snap b/test/snapshots/blueprint.test.ts.snap index bc70860..ff78c23 100644 Binary files a/test/snapshots/blueprint.test.ts.snap and b/test/snapshots/blueprint.test.ts.snap differ diff --git a/test/snapshots/seam-blueprint.test.ts.md b/test/snapshots/seam-blueprint.test.ts.md index b65cb00..231dd39 100644 --- a/test/snapshots/seam-blueprint.test.ts.md +++ b/test/snapshots/seam-blueprint.test.ts.md @@ -101,7 +101,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -195,7 +195,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -289,7 +289,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -383,7 +383,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -477,7 +477,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -571,7 +571,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -665,7 +665,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -759,7 +759,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -853,7 +853,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -947,7 +947,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -1041,7 +1041,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -1135,7 +1135,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -1229,7 +1229,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -1323,7 +1323,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, { @@ -1417,7 +1417,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'action_attempt', - routePath: '', + routePath: '/action_attempts', undocumentedMessage: '', }, ], @@ -16076,7 +16076,7 @@ Generated by [AVA](https://avajs.dev). }, ], resourceType: 'event', - routePath: '', + routePath: '/events', undocumentedMessage: '', }, network: { diff --git a/test/snapshots/seam-blueprint.test.ts.snap b/test/snapshots/seam-blueprint.test.ts.snap index 480d238..f588a09 100644 Binary files a/test/snapshots/seam-blueprint.test.ts.snap and b/test/snapshots/seam-blueprint.test.ts.snap differ