Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 47 additions & 4 deletions src/lib/blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ interface ResourceResponse extends BaseResponse {
responseType: 'resource'
responseKey: string
resourceType: string
actionAttemptType?: string
}

interface ResourceListResponse extends BaseResponse {
Expand Down Expand Up @@ -276,6 +277,7 @@ export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'

interface Context extends Required<BlueprintOptions> {
codeSampleDefinitions: CodeSampleDefinition[]
actionAttempts: ActionAttempt[]
}

export const TypesModuleSchema = z.object({
Expand All @@ -301,19 +303,21 @@ export const createBlueprint = async (
// TODO: Move openapi to TypesModuleSchema
const openapi = typesModule.openapi as Openapi

const resources = createResources(openapi.components.schemas)
const actionAttempts = createActionAttempts(openapi.components.schemas)

const context = {
codeSampleDefinitions,
formatCode,
actionAttempts,
}

const resources = createResources(openapi.components.schemas)

return {
title: openapi.info.title,
routes: await createRoutes(openapi.paths, context),
resources,
events: createEvents(openapi.components.schemas, resources),
actionAttempts: createActionAttempts(openapi.components.schemas),
actionAttempts,
}
}

Expand Down Expand Up @@ -523,7 +527,7 @@ const createEndpointFromOperation = async (
const draftMessage = parsedOperation['x-draft']

const request = createRequest(methods, operation, path)
const response = createResponse(operation, path)
const response = createResponse(operation, path, context)

const operationAuthMethods = parsedOperation.security.map(
(securitySchema) => {
Expand Down Expand Up @@ -843,6 +847,7 @@ const createResource = (
const createResponse = (
operation: OpenapiOperation,
path: string,
context: Context,
): Response => {
if (!('responses' in operation) || operation.responses == null) {
throw new Error(
Expand Down Expand Up @@ -920,6 +925,12 @@ const createResponse = (
)
}

const actionAttemptType = validateActionAttemptType(
parsedOperation['x-action-attempt-type'],
responseKey,
path,
context,
)
const refKey = responseKey

if (refKey != null && properties[refKey] != null) {
Expand All @@ -931,6 +942,7 @@ const createResponse = (
responseKey: refKey,
resourceType: refString?.split('/').at(-1) ?? 'unknown',
description,
...(actionAttemptType != null && { actionAttemptType }),
}
}
}
Expand All @@ -941,6 +953,37 @@ const createResponse = (
}
}

const validateActionAttemptType = (
actionAttemptType: string | undefined,
responseKey: string,
path: string,
context: Context,
): string | undefined => {
const excludedPaths = ['/action_attempts']
const isPathExcluded = excludedPaths.some((p) => path.startsWith(p))

if (
actionAttemptType == null &&
responseKey === 'action_attempt' &&
!isPathExcluded
) {
throw new Error(`Missing action_attempt_type for path ${path}`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also validate the action attempt type is a valid one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

if (
actionAttemptType != null &&
!context.actionAttempts.some(
(attempt) => attempt.actionAttemptType === actionAttemptType,
)
) {
throw new Error(
`Invalid action_attempt_type '${actionAttemptType}' for path ${path}`,
)
}

return actionAttemptType
}

export const createProperties = (
properties: Record<string, OpenapiSchema>,
parentPaths: string[],
Expand Down
1 change: 1 addition & 0 deletions src/lib/openapi/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export const OpenapiOperationSchema = z.object({
'x-undocumented': z.string().default(''),
'x-deprecated': z.string().default(''),
'x-draft': z.string().default(''),
'x-action-attempt-type': z.string().optional(),
})

export const EnumValueSchema = z.object({
Expand Down
32 changes: 32 additions & 0 deletions test/fixtures/types/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,38 @@ export default {
'x-title': 'Get a foo',
},
},
'/foos/create': {
post: {
operationId: 'foosCreatePost',
responses: {
200: {
content: {
'application/json': {
schema: {
properties: {
ok: { type: 'boolean' },
action_attempt: {
$ref: '#/components/schemas/action_attempt',
},
},
required: ['action_attempt', 'ok'],
type: 'object',
},
},
},
description: 'Create a foo.',
},
400: { description: 'Bad Request' },
401: { description: 'Unauthorized' },
},
security: [],
summary: '/foos/create',
tags: ['/foos'],
'x-response-key': 'action_attempt',
'x-action-attempt-type': 'CREATE_FOO',
'x-title': 'Create a foo',
},
},
'/foos/list': {
get: {
operationId: 'foosListGet',
Expand Down
60 changes: 60 additions & 0 deletions test/snapshots/blueprint.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,36 @@ Generated by [AVA](https://avajs.dev).
undocumentedMessage: '',
workspaceScope: 'required',
},
{
authMethods: [],
codeSamples: [],
deprecationMessage: '',
description: '',
draftMessage: '',
isDeprecated: false,
isDraft: false,
isUndocumented: false,
name: 'create',
path: '/foos/create',
request: {
methods: [
'POST',
],
parameters: [],
preferredMethod: 'POST',
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'CREATE_FOO',
description: 'Create a foo.',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
responseType: 'resource',
},
title: 'Create a foo',
undocumentedMessage: '',
workspaceScope: 'none',
},
{
authMethods: [
'api_key',
Expand Down Expand Up @@ -1727,6 +1757,36 @@ Generated by [AVA](https://avajs.dev).
undocumentedMessage: '',
workspaceScope: 'required',
},
{
authMethods: [],
codeSamples: [],
deprecationMessage: '',
description: '',
draftMessage: '',
isDeprecated: false,
isDraft: false,
isUndocumented: false,
name: 'create',
path: '/foos/create',
request: {
methods: [
'POST',
],
parameters: [],
preferredMethod: 'POST',
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'CREATE_FOO',
description: 'Create a foo.',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
responseType: 'resource',
},
title: 'Create a foo',
undocumentedMessage: '',
workspaceScope: 'none',
},
{
authMethods: [
'api_key',
Expand Down
Binary file modified test/snapshots/blueprint.test.ts.snap
Binary file not shown.
14 changes: 14 additions & 0 deletions test/snapshots/seam-blueprint.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -18871,6 +18871,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'CREATE_ACCESS_CODE',
description: 'OK',
resourceType: 'access_code',
responseKey: 'access_code',
Expand Down Expand Up @@ -22073,6 +22074,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'ENCODE_CREDENTIAL',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -22151,6 +22153,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'SCAN_CREDENTIAL',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -28454,6 +28457,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'LOCK_DOOR',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -28514,6 +28518,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'UNLOCK_DOOR',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -29301,6 +29306,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'CREATE_NOISE_THRESHOLD',
description: 'OK',
resourceType: 'noise_threshold',
responseKey: 'noise_threshold',
Expand Down Expand Up @@ -30106,6 +30112,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'ACTIVATE_CLIMATE_PRESET',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -30192,6 +30199,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'SET_HVAC_MODE',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -30630,6 +30638,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'SET_HVAC_MODE',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -30742,6 +30751,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'SET_HVAC_MODE',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -31354,6 +31364,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'SET_HVAC_MODE',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -31561,6 +31572,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'SET_FAN_MODE',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -31594,6 +31606,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'SET_HVAC_MODE',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down Expand Up @@ -33890,6 +33903,7 @@ Generated by [AVA](https://avajs.dev).
semanticMethod: 'POST',
},
response: {
actionAttemptType: 'RESET_SANDBOX_WORKSPACE',
description: 'OK',
resourceType: 'action_attempt',
responseKey: 'action_attempt',
Expand Down
Binary file modified test/snapshots/seam-blueprint.test.ts.snap
Binary file not shown.
Loading