Skip to content

Commit

Permalink
docs-util: fixes for circular references, PAK in examples, namespaces (
Browse files Browse the repository at this point in the history
…#9091)

- Fix circular references in generated OAS comments by cloning objects
- Add publishable API key in the header of store cURL examples + change the admin authentication header to use bearer tokens.
- Add plugin to generate namespaces from the `@customNamespace` tag in TSDocs, this was available before but was removed.
- Other fixes related to re-using schemas, overwritten descriptions, and smaller fixes
  • Loading branch information
shahednasser authored Sep 16, 2024
1 parent 05c8826 commit cb79a5d
Show file tree
Hide file tree
Showing 10 changed files with 448 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,15 @@ class OasExamplesGenerator {
]

if (isAdminAuthenticated) {
exampleArr.push(`-H 'x-medusa-access-token: {api_token}'`)
exampleArr.push(`-H 'Authorization: Bearer {access_token}'`)
} else if (isStoreAuthenticated) {
exampleArr.push(`-H 'Authorization: Bearer {access_token}'`)
}

if (path.startsWith("/store")) {
exampleArr.push(`-H 'x-publishable-api-key: {your_publishable_api_key}'`)
}

if (requestSchema) {
const requestData = this.getSchemaRequiredData(requestSchema)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ class OasSchemaHelper {
})
}

this.schemas.set(schema["x-schemaName"], schema)
if (this.canAddSchema(schema)) {
this.schemas.set(schema["x-schemaName"], schema)
}

return {
$ref: this.constructSchemaReference(schema["x-schemaName"]),
Expand Down Expand Up @@ -181,6 +183,36 @@ class OasSchemaHelper {
return clonedSchema
}

isSchemaEmpty(schema: OpenApiSchema): boolean {
switch (schema.type) {
case "object":
return (
schema.properties === undefined ||
Object.keys(schema.properties).length === 0
)
case "array":
return (
!this.isRefObject(schema.items) && this.isSchemaEmpty(schema.items)
)
default:
return false
}
}

canAddSchema(schema: OpenApiSchema): boolean {
if (!schema["x-schemaName"]) {
return false
}

const existingSchema = this.schemas.get(schema["x-schemaName"])

if (!existingSchema) {
return true
}

return this.isSchemaEmpty(existingSchema) && !this.isSchemaEmpty(schema)
}

/**
* Retrieve the expected file name of the schema.
*
Expand Down Expand Up @@ -212,7 +244,7 @@ class OasSchemaHelper {
// check if it already exists in the schemas map
if (this.schemas.has(schemaName)) {
return {
schema: this.schemas.get(schemaName)!,
schema: JSON.parse(JSON.stringify(this.schemas.get(schemaName)!)),
schemaPrefix: `@schema ${schemaName}`,
}
}
Expand Down Expand Up @@ -272,7 +304,7 @@ class OasSchemaHelper {
return name
.replace("DTO", "")
.replace(this.schemaRefPrefix, "")
.replace(/(?<!AdminProduct)Type$/, "")
.replace(/(?<!(AdminProduct|CreateProduct))Type$/, "")
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class SchemaFactory {
schema = Object.assign(schema, {
...additionalData,
// keep the description
description: schema.description || additionalData.description
description: schema.description || additionalData.description,
})
}

Expand Down
115 changes: 95 additions & 20 deletions www/utils/packages/docs-generator/src/classes/kinds/oas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ class OasKindGenerator extends FunctionKindGenerator {
node,
tagName,
methodName,
forUpdate: true,
})

// update query parameters
Expand Down Expand Up @@ -511,6 +512,7 @@ class OasKindGenerator extends FunctionKindGenerator {
const newResponseSchema = this.getResponseSchema({
node,
tagName,
forUpdate: true,
})
let updatedResponseSchema: OpenApiSchema | undefined

Expand Down Expand Up @@ -968,6 +970,7 @@ class OasKindGenerator extends FunctionKindGenerator {
node,
tagName,
methodName,
forUpdate = false,
}: {
/**
* The node to retrieve its request parameters.
Expand All @@ -981,6 +984,10 @@ class OasKindGenerator extends FunctionKindGenerator {
* The tag's name.
*/
tagName?: string
/**
* Whether the request parameters are retrieved for update purposes only.
*/
forUpdate?: boolean
}): {
/**
* The query parameters.
Expand Down Expand Up @@ -1041,6 +1048,7 @@ class OasKindGenerator extends FunctionKindGenerator {
title: propertyName,
descriptionOptions,
context: "request",
saveSchema: !forUpdate,
}),
})
)
Expand All @@ -1063,6 +1071,7 @@ class OasKindGenerator extends FunctionKindGenerator {
},
zodObjectTypeName: zodObjectTypeName,
context: "request",
saveSchema: !forUpdate,
})

// If function is a GET function, add the type parameter to the
Expand Down Expand Up @@ -1135,6 +1144,7 @@ class OasKindGenerator extends FunctionKindGenerator {
getResponseSchema({
node,
tagName,
forUpdate = false,
}: {
/**
* The node to retrieve its response schema.
Expand All @@ -1144,6 +1154,10 @@ class OasKindGenerator extends FunctionKindGenerator {
* The tag's name.
*/
tagName?: string
/**
* Whether the response schema is retrieved for update only.
*/
forUpdate?: boolean
}): OpenApiSchema | undefined {
let responseSchema: OpenApiSchema | undefined

Expand All @@ -1170,6 +1184,7 @@ class OasKindGenerator extends FunctionKindGenerator {
itemType: responseTypeArguments[0],
}),
context: "response",
saveSchema: !forUpdate,
})
}
}
Expand All @@ -1191,6 +1206,7 @@ class OasKindGenerator extends FunctionKindGenerator {
allowedChildren,
disallowedChildren,
zodObjectTypeName,
saveSchema = true,
...rest
}: {
/**
Expand Down Expand Up @@ -1229,6 +1245,10 @@ class OasKindGenerator extends FunctionKindGenerator {
* Whether the type is in a request / response
*/
context?: "request" | "response"
/**
* Whether to save object schemas. Useful when only getting schemas to update.
*/
saveSchema?: boolean
}): OpenApiSchema {
if (level > this.MAX_LEVEL) {
return {}
Expand Down Expand Up @@ -1354,6 +1374,7 @@ class OasKindGenerator extends FunctionKindGenerator {
parentName: title || descriptionOptions?.parentName,
}
: undefined,
saveSchema,
...rest,
}),
}
Expand Down Expand Up @@ -1384,6 +1405,7 @@ class OasKindGenerator extends FunctionKindGenerator {
level,
title,
descriptionOptions,
saveSchema,
...rest,
})
)
Expand All @@ -1407,6 +1429,7 @@ class OasKindGenerator extends FunctionKindGenerator {
level,
title,
descriptionOptions,
saveSchema,
...rest,
})
})
Expand Down Expand Up @@ -1437,6 +1460,7 @@ class OasKindGenerator extends FunctionKindGenerator {
level,
descriptionOptions,
allowedChildren: pickedProperties,
saveSchema,
...rest,
})
case typeAsString.startsWith("Omit"):
Expand All @@ -1458,6 +1482,7 @@ class OasKindGenerator extends FunctionKindGenerator {
level,
descriptionOptions,
disallowedChildren: omitProperties,
saveSchema,
...rest,
})
case typeAsString.startsWith("Partial"):
Expand All @@ -1475,6 +1500,7 @@ class OasKindGenerator extends FunctionKindGenerator {
descriptionOptions,
disallowedChildren,
allowedChildren,
saveSchema,
...rest,
})

Expand All @@ -1485,13 +1511,29 @@ class OasKindGenerator extends FunctionKindGenerator {
case itemType.isClassOrInterface() ||
itemType.isTypeParameter() ||
(itemType as ts.Type).flags === ts.TypeFlags.Object:
const properties: Record<string, OpenApiSchema> = {}
const properties: Record<
string,
OpenApiSchema | OpenAPIV3.ReferenceObject
> = {}
const requiredProperties: string[] = []

const baseType = itemType.getBaseTypes()?.[0]
const isDeleteResponse =
baseType?.aliasSymbol?.getEscapedName() === "DeleteResponse"

const objSchema: OpenApiSchema = {
type: "object",
description,
"x-schemaName":
itemType.isClassOrInterface() ||
itemType.isTypeParameter() ||
(isZodObject(itemType) && zodObjectTypeName)
? this.oasSchemaHelper.normalizeSchemaName(typeAsString)
: undefined,
// this is changed later
required: undefined,
}

if (level + 1 <= this.MAX_LEVEL) {
itemType.getProperties().forEach((property) => {
if (
Expand All @@ -1504,6 +1546,41 @@ class OasKindGenerator extends FunctionKindGenerator {
requiredProperties.push(property.name)
}
const propertyType = this.checker.getTypeOfSymbol(property)

// if property's type is same as parent's property,
// create a reference to the parent
const arrHasParentType =
this.checker.isArrayType(propertyType) &&
this.areTypesEqual(
itemType,
this.checker.getTypeArguments(
propertyType as ts.TypeReference
)[0]
)
const isParentType = this.areTypesEqual(itemType, propertyType)

if (isParentType && objSchema["x-schemaName"]) {
properties[property.name] = {
$ref: this.oasSchemaHelper.constructSchemaReference(
objSchema["x-schemaName"]
),
}

return
} else if (arrHasParentType && objSchema["x-schemaName"]) {
properties[property.name] = {
type: "array",
description,
items: {
$ref: this.oasSchemaHelper.constructSchemaReference(
objSchema["x-schemaName"]
),
},
} as OpenAPIV3.ArraySchemaObject

return
}

properties[property.name] = this.typeToSchema({
itemType: propertyType,
level: level + 1,
Expand All @@ -1513,38 +1590,34 @@ class OasKindGenerator extends FunctionKindGenerator {
typeStr: property.name,
parentName: title || descriptionOptions?.parentName,
},
saveSchema,
...rest,
})

if (isDeleteResponse && property.name === "object") {
if (
isDeleteResponse &&
property.name === "object" &&
!this.oasSchemaHelper.isRefObject(properties[property.name])
) {
const schemaProperty = properties[property.name] as OpenApiSchema
// try to retrieve default from `DeleteResponse`'s type argument
const deleteTypeArg = baseType.aliasTypeArguments?.[0]
properties[property.name].default =
schemaProperty.default =
deleteTypeArg && "value" in deleteTypeArg
? (deleteTypeArg.value as string)
: properties[property.name].default
: schemaProperty.default
}
})
}

const objSchema: OpenApiSchema = {
type: "object",
description,
"x-schemaName":
itemType.isClassOrInterface() ||
itemType.isTypeParameter() ||
(isZodObject(itemType) && zodObjectTypeName)
? this.oasSchemaHelper.normalizeSchemaName(typeAsString)
: undefined,
required:
requiredProperties.length > 0 ? requiredProperties : undefined,
}

if (Object.values(properties).length) {
objSchema.properties = properties
}

if (objSchema["x-schemaName"]) {
objSchema.required =
requiredProperties.length > 0 ? requiredProperties : undefined

if (saveSchema && objSchema["x-schemaName"]) {
// add object to schemas to be created
// if necessary
this.oasSchemaHelper.namedSchemaToReference(objSchema)
Expand Down Expand Up @@ -1947,8 +2020,6 @@ class OasKindGenerator extends FunctionKindGenerator {
}) || oldSchemaObj.items
}

// update schema

if (
oldSchemaObj!.description !== newSchemaObj?.description &&
oldSchemaObj!.description === SUMMARY_PLACEHOLDER
Expand Down Expand Up @@ -2184,6 +2255,10 @@ class OasKindGenerator extends FunctionKindGenerator {
return true
})
}

private areTypesEqual(type1: ts.Type, type2: ts.Type): boolean {
return "id" in type1 && "id" in type2 && type1.id === type2.id
}
}

export default OasKindGenerator
Loading

0 comments on commit cb79a5d

Please sign in to comment.