docs-util: improvements and fixes to OAS generation (#8855)

- Add to knowledge base summaries for delete-related properties
- Fix long curl examples overflowing the line
- Fix singular / plural usage in some knowledge base descriptions / summaries
- Fix some workflows not picked up
- Remove query params for routes that don't use them.
This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-08-29 08:40:55 +00:00
1 parent 16d8fb1d20
commit 25134d2307
3 files changed
+93 -59

No files matched your search

@@ -523,6 +523,29 @@ class KnowledgeBaseFactory {
template: template:
"Pass additional custom data to the API route. This data is passed to the underlying workflow under the `additional_data` parameter.", "Pass additional custom data to the API route. This data is passed to the underlying workflow under the `additional_data` parameter.",
}, },
{
exact: "object",
template: (_, options) => {
if (!options?.rawParentName?.includes("DeleteResponse")) {
return
}
return "The name of the deleted object."
},
},
{
exact: "deleted",
template: (_, options) => {
if (
!options?.rawParentName?.includes("DeleteResponse") ||
!options.parentName
) {
return
}
return `Whether the ${singular(options.parentName)} was deleted.`
},
},
{ {
pattern: /.*/, pattern: /.*/,
template(str, options) { template(str, options) {
@@ -799,6 +822,7 @@ class KnowledgeBaseFactory {
: kebabToTitle(splitOasPath[splitOasPath.length - 1]) : kebabToTitle(splitOasPath[splitOasPath.length - 1])
// retrieve different formatted versions of the entity name for the summary/description // retrieve different formatted versions of the entity name for the summary/description
const pluralEndingEntityName = pluralize.plural(endingEntityName) const pluralEndingEntityName = pluralize.plural(endingEntityName)
const singularEndingEntityName = pluralize.singular(endingEntityName)
const lowerEndingEntityName = pluralEndingEntityName.toLowerCase() const lowerEndingEntityName = pluralEndingEntityName.toLowerCase()
const singularLowerEndingEntityName = const singularLowerEndingEntityName =
pluralize.singular(endingEntityName) pluralize.singular(endingEntityName)
@@ -808,11 +832,19 @@ class KnowledgeBaseFactory {
result.summary = `List ${pluralEndingEntityName}` result.summary = `List ${pluralEndingEntityName}`
result.description = `Retrieve a list of ${lowerEndingEntityName} in a ${singularLowerTag}. The ${lowerEndingEntityName} can be filtered by fields like FILTER FIELDS. The ${lowerEndingEntityName} can also be paginated.` result.description = `Retrieve a list of ${lowerEndingEntityName} in a ${singularLowerTag}. The ${lowerEndingEntityName} can be filtered by fields like FILTER FIELDS. The ${lowerEndingEntityName} can also be paginated.`
} else if (httpMethod === "post") { } else if (httpMethod === "post") {
result.summary = `Add ${pluralEndingEntityName} to ${singularTag}` result.summary = `Add ${
result.description = `Add a list of ${lowerEndingEntityName} to a ${singularLowerTag}.` isBulk ? pluralEndingEntityName : singularEndingEntityName
} to ${singularTag}`
result.description = isBulk
? `Add a list of ${lowerEndingEntityName} to a ${singularLowerTag}.`
: `Add a ${singularLowerEndingEntityName} to a ${singularLowerTag}`
} else { } else {
result.summary = `Remove ${pluralEndingEntityName} from ${singularTag}` result.summary = `Remove ${
result.description = `Remove a list of ${lowerEndingEntityName} from a ${singularLowerTag}. This doesn't delete the ${singularLowerEndingEntityName}, only the association between the ${singularLowerEndingEntityName} and the ${singularLowerTag}.` isBulk ? pluralEndingEntityName : singularEndingEntityName
} from ${singularTag}`
result.description = isBulk
? `Remove a list of ${lowerEndingEntityName} from a ${singularLowerTag}.`
: `Remove a ${singularLowerEndingEntityName} from a ${singularLowerTag}.`
} }
} else { } else {
// the OAS operation is applied on a single entity that is the main entity (denoted by the tag). // the OAS operation is applied on a single entity that is the main entity (denoted by the tag).
@@ -35,6 +35,7 @@ type SchemaDescriptionOptions = {
nodeType?: ts.Type nodeType?: ts.Type
typeStr: string typeStr: string
parentName?: string parentName?: string
rawParentName?: string
} }
export type OasArea = "admin" | "store" export type OasArea = "admin" | "store"
@@ -954,13 +955,15 @@ class OasKindGenerator extends FunctionKindGenerator {
// TODO for now I'll use the type for validatedQuery until // TODO for now I'll use the type for validatedQuery until
// we have an actual approach to infer query types // we have an actual approach to infer query types
const querySymbol = requestType.getProperty("validatedQuery") const querySymbol = requestType.getProperty("validatedQuery")
if (querySymbol) { if (querySymbol && this.shouldAddQueryParams(node)) {
const queryType = this.checker.getTypeOfSymbol(querySymbol) const queryType = this.checker.getTypeOfSymbol(querySymbol)
const queryTypeName = this.checker.typeToString(queryType)
queryType.getProperties().forEach((property) => { queryType.getProperties().forEach((property) => {
const propertyType = this.checker.getTypeOfSymbol(property) const propertyType = this.checker.getTypeOfSymbol(property)
const descriptionOptions: SchemaDescriptionOptions = { const descriptionOptions: SchemaDescriptionOptions = {
typeStr: property.getName(), typeStr: property.getName(),
parentName: tagName, parentName: tagName,
rawParentName: queryTypeName,
node: property.valueDeclaration, node: property.valueDeclaration,
symbol: property, symbol: property,
nodeType: propertyType, nodeType: propertyType,
@@ -993,6 +996,7 @@ class OasKindGenerator extends FunctionKindGenerator {
itemType: requestTypeArguments[0], itemType: requestTypeArguments[0],
descriptionOptions: { descriptionOptions: {
parentName: tagName, parentName: tagName,
rawParentName: this.checker.typeToString(requestTypeArguments[0]),
}, },
zodObjectTypeName: zodObjectTypeName, zodObjectTypeName: zodObjectTypeName,
}) })
@@ -1095,6 +1099,7 @@ class OasKindGenerator extends FunctionKindGenerator {
itemType: responseTypeArguments[0], itemType: responseTypeArguments[0],
descriptionOptions: { descriptionOptions: {
parentName: tagName, parentName: tagName,
rawParentName: this.checker.typeToString(responseTypeArguments[0]),
}, },
zodObjectTypeName: getCorrectZodTypeName({ zodObjectTypeName: getCorrectZodTypeName({
typeReferenceNode: node.parameters[1].type, typeReferenceNode: node.parameters[1].type,
@@ -1390,8 +1395,9 @@ class OasKindGenerator extends FunctionKindGenerator {
if (this.isRequired(property)) { if (this.isRequired(property)) {
requiredProperties.push(property.name) requiredProperties.push(property.name)
} }
const propertyType = this.checker.getTypeOfSymbol(property)
properties[property.name] = this.typeToSchema({ properties[property.name] = this.typeToSchema({
itemType: this.checker.getTypeOfSymbol(property), itemType: propertyType,
level: level + 1, level: level + 1,
title: property.name, title: property.name,
descriptionOptions: { descriptionOptions: {
@@ -1453,7 +1459,7 @@ class OasKindGenerator extends FunctionKindGenerator {
node, node,
nodeType, nodeType,
typeStr, typeStr,
parentName, ...templateOptions
}: SchemaDescriptionOptions): string { }: SchemaDescriptionOptions): string {
if (!symbol && !node && !nodeType) { if (!symbol && !node && !nodeType) {
// if none of the associated symbol, node, or type are provided, // if none of the associated symbol, node, or type are provided,
@@ -1462,9 +1468,7 @@ class OasKindGenerator extends FunctionKindGenerator {
return ( return (
this.knowledgeBaseFactory.tryToGetOasSchemaDescription({ this.knowledgeBaseFactory.tryToGetOasSchemaDescription({
str: typeStr, str: typeStr,
templateOptions: { templateOptions,
parentName,
},
}) || SUMMARY_PLACEHOLDER }) || SUMMARY_PLACEHOLDER
) )
} }
@@ -1882,66 +1886,54 @@ class OasKindGenerator extends FunctionKindGenerator {
getAssociatedWorkflow(node: FunctionNode): string | undefined { getAssociatedWorkflow(node: FunctionNode): string | undefined {
let workflow: string | undefined let workflow: string | undefined
if ( if (!ts.isFunctionDeclaration(node) && !ts.isArrowFunction(node)) {
(!ts.isFunctionDeclaration(node) && !ts.isArrowFunction(node)) ||
!node.body ||
!ts.isBlock(node.body)
) {
return return
} }
const sourceFile = node.getSourceFile() const sourceFile = node.getSourceFile()
const fileLocalSymbols: Map<string, ts.Symbol> =
"locals" in sourceFile
? (sourceFile.locals as Map<string, ts.Symbol>)
: new Map()
if (!fileLocalSymbols.size) { // if a source file doesn't have imports, then it's assumed that it's not using workflows.
if (!("imports" in sourceFile) || !Array.isArray(sourceFile.imports)) {
return return
} }
node.body.statements.some((statement) => { // retrieve workflows imported from `@medusajs/core-flows`
let awaitExpression: ts.AwaitExpression | undefined // since there could be multiple import statements from the
// same package, put them in an array
const coreFlowsImports: ts.ImportClause[] = []
;(sourceFile.imports as ts.Node[]).forEach((importNode) => {
if ( if (
ts.isVariableStatement(statement) && !importNode.parent ||
statement.declarationList.declarations[0].initializer && !ts.isImportDeclaration(importNode.parent) ||
ts.isAwaitExpression( !importNode.parent.importClause?.namedBindings ||
statement.declarationList.declarations[0].initializer importNode.getText() !== `"@medusajs/core-flows"`
)
) { ) {
awaitExpression = statement.declarationList.declarations[0].initializer return
} else if (ts.isAwaitExpression(statement)) {
awaitExpression = statement
} }
if ( coreFlowsImports.push(importNode.parent.importClause)
!awaitExpression || })
!ts.isCallExpression(awaitExpression.expression) ||
!ts.isPropertyAccessExpression(awaitExpression.expression.expression) ||
!awaitExpression.expression.expression.name.getText().startsWith("run")
) {
return false
}
const fullExpressionText = awaitExpression.expression.getText() // if no imports found from `@medusajs/core-flows`, return
const parenIndex = fullExpressionText.indexOf("(") if (!coreFlowsImports.length) {
const dotIndex = fullExpressionText.indexOf(".") return
const cutOffIndex = }
parenIndex !== -1 && dotIndex === -1
? parenIndex
: parenIndex === -1 && dotIndex !== -1
? dotIndex
: parenIndex === -1 && dotIndex === -1
? fullExpressionText.length
: Math.min(parenIndex, dotIndex)
const expressionName = fullExpressionText.substring(0, cutOffIndex)
if (!fileLocalSymbols.has(expressionName)) { // check if any of the imported vars are used in the function node
return false // const fileWorkflowImports: Map<string, ts.Identifier> = new Map()
} const fnText = node.getText()
coreFlowsImports.forEach((coreFlowsImport) => {
coreFlowsImport.namedBindings?.forEachChild((childImport) => {
if (!ts.isImportSpecifier(childImport) || workflow) {
return
}
workflow = expressionName const workflowName = childImport.name.getText()
return true
if (fnText.includes(workflowName)) {
workflow = workflowName
}
})
}) })
return workflow return workflow
@@ -2031,6 +2023,17 @@ class OasKindGenerator extends FunctionKindGenerator {
writeFileSync(areaYamlPath, stringify(areaYaml)) writeFileSync(areaYamlPath, stringify(areaYaml))
} }
} }
shouldAddQueryParams(node: FunctionNode): boolean {
const queryParamsUsageIndicators = [
`req.filterableFields`,
`req.remoteQueryConfig`,
]
const fnText = node.getText()
return queryParamsUsageIndicators.some((indicator) =>
fnText.includes(indicator)
)
}
} }
export default OasKindGenerator export default OasKindGenerator
@@ -13,8 +13,7 @@ export default function formatOas(
oas: OpenApiOperation | OpenApiSchema, oas: OpenApiOperation | OpenApiSchema,
oasPrefix: string oasPrefix: string
) { ) {
return `* ${oasPrefix}${DOCBLOCK_NEW_LINE}${stringify(oas).replaceAll( return `* ${oasPrefix}${DOCBLOCK_NEW_LINE}${stringify(oas, {
"\n", lineWidth: 200,
DOCBLOCK_NEW_LINE }).replaceAll("\n", DOCBLOCK_NEW_LINE)}${DOCBLOCK_END_LINE}`
)}${DOCBLOCK_END_LINE}`
} }