feat(oas): new medusa-oas docs for Redocly and circular references (#3745)
## What New `medusa-oas docs` to improve OAS compatibility with for Redocly API documentation viewer and handle deep circular references. ## Why We wish to share the tooling we have been using to generate our public API documentation. ## How * Move `/docs-utils/redocly` under `medusa-oas-cli` package. * Move some of the operations from `oas-github-ci/scripts/build-openapi.js` under the `medusa-oas docs` command. * Improve DevX when dealing with circular references by outputting precise troubleshooting recommendations in the error message. * Extract some common operations in utility methods. ## Tests ### Step 1 * Run `yarn install` * Run `yarn build` * Run `yarn openapi:generate --dry-run` * Expect same behaviour as before where OAS is validated and circular references are checked. ### Step 2 * Run `yarn openapi:generate` * Expect same behaviour as before where API documentation is generated in `/docs` ### Step 3 * Move to the `packages/oas/medusa-oas-cli` * Run `yarn medusa-oas oas --type store --out-dir ~/tmp/oas` to generate the raw OAS file. * Run `yarn medusa-oas docs --src-file ~/tmp/oas/store.oas.json --preview` * Open url from the console output in a browser * Expect a preview of the API documentation using Redocly. ### Step 4 * Run `yarn medusa-oas docs --src-file ~/tmp/oas/store.oas.json --out-dir ~/tmp/docs/store --clean --split` * Expect a similiar output as `yarn openapi:generate` ### Step 5 * Run `yarn medusa-oas docs --src-file ~/tmp/oas/store.oas.json --out-dir ~/tmp/docs/store --clean --html` * Expect `index.html` to have been created. * Run `npx http-server ~/tmp/docs/store -p 8000` * Open http://127.0.0.1:8000 in a browser. * Expect a zero-dependency static rendering of the API documentation using Redocly. ### Step 6 * To emulate an unhandled circular reference, edit [packages/oas/medusa-oas-cli/redocly/redocly-config.yaml](https://github.com/medusajs/medusa/blob/d180f47e1609e74f46f72cdeef6265a654da3e7e/packages/oas/medusa-oas-cli/redocly/redocly-config.yaml#L9-L10) and comment out "Address: - Customer" * Run `yarn medusa-oas docs --src-file ~/tmp/oas/store.oas.json --dry-run` * Expect an error message with a hint on how to resolve the issue. * Create a file `~/tmp/redocly-config.yaml` and paste in the recommendation from the error message. * Run `yarn medusa-oas docs --src-file ~/tmp/oas/store.oas.json --dry-run --config ~/tmp/redocly-config.yaml` * Expect Dry run to succeed.
This commit is contained in:
@@ -1,157 +0,0 @@
|
||||
plugins:
|
||||
- "./plugins/plugin.js"
|
||||
|
||||
# Allows to replace a $ref with `type: object` in order to avoid infinite loops
|
||||
# when Redocly attempts to render circular references.
|
||||
decorators:
|
||||
plugin/circular-patch:
|
||||
verbose: false
|
||||
schemas:
|
||||
Address:
|
||||
- Customer
|
||||
Cart:
|
||||
- Customer
|
||||
- Order
|
||||
- Payment
|
||||
- PaymentSession
|
||||
ClaimImage:
|
||||
- ClaimItem
|
||||
ClaimItem:
|
||||
- ClaimOrder
|
||||
ClaimOrder:
|
||||
- Fulfillment
|
||||
- Order
|
||||
- Return
|
||||
Country:
|
||||
- Region
|
||||
Customer:
|
||||
- Order
|
||||
CustomerGroup:
|
||||
- Customer
|
||||
- PriceList
|
||||
Discount:
|
||||
- Discount
|
||||
DiscountRule:
|
||||
- DiscountCondition
|
||||
DraftOrder:
|
||||
- Cart
|
||||
- Order
|
||||
Fulfillment:
|
||||
- ClaimOrder
|
||||
- Order
|
||||
- Swap
|
||||
FulfillmentItem:
|
||||
- Fulfillment
|
||||
GiftCard:
|
||||
- Order
|
||||
GiftCardTransaction:
|
||||
- GiftCard
|
||||
- Order
|
||||
LineItem:
|
||||
- Cart
|
||||
- ClaimOrder
|
||||
- Order
|
||||
- OrderEdit
|
||||
- Swap
|
||||
LineItemAdjustment:
|
||||
- LineItem
|
||||
LineItemTaxLine:
|
||||
- LineItem
|
||||
MoneyAmount:
|
||||
- PriceList
|
||||
- ProductVariant
|
||||
- Region
|
||||
Notification:
|
||||
- Notification
|
||||
Order:
|
||||
- Cart
|
||||
- ClaimOrder
|
||||
- Customer
|
||||
- DraftOrder
|
||||
- Fulfillment
|
||||
- OrderEdit
|
||||
- Payment
|
||||
- Refund
|
||||
- Return
|
||||
- Swap
|
||||
OrderEdit:
|
||||
- Order
|
||||
OrderItemChange:
|
||||
- OrderEdit
|
||||
Payment:
|
||||
- Cart
|
||||
- Order
|
||||
- Swap
|
||||
ProductCategory:
|
||||
- ProductCategory
|
||||
- Product
|
||||
ProductCollection:
|
||||
- Product
|
||||
ProductOption:
|
||||
- Product
|
||||
ProductOptionValue:
|
||||
- ProductOption
|
||||
- ProductVariant
|
||||
ProductVariant:
|
||||
- Product
|
||||
ProductVariantInventoryItem:
|
||||
- ProductVariant
|
||||
Refund:
|
||||
- Order
|
||||
- Payment
|
||||
Return:
|
||||
- ClaimOrder
|
||||
- Order
|
||||
- Swap
|
||||
ReturnItem:
|
||||
- Return
|
||||
ReturnReason:
|
||||
- ReturnReason
|
||||
SalesChannelLocation:
|
||||
- SalesChannel
|
||||
ShippingMethod:
|
||||
- Cart
|
||||
- ClaimOrder
|
||||
- Order
|
||||
- Payment
|
||||
- Return
|
||||
- Swap
|
||||
ShippingMethodTaxLine:
|
||||
- ShippingMethod
|
||||
ShippingOption:
|
||||
- Region
|
||||
ShippingOptionRequirement:
|
||||
- ShippingOption
|
||||
ShippingProfile:
|
||||
- Product
|
||||
- ShippingOption
|
||||
Swap:
|
||||
- Cart
|
||||
- Fulfillment
|
||||
- Order
|
||||
- Payment
|
||||
- Return
|
||||
TaxRate:
|
||||
- Region
|
||||
TrackingLink:
|
||||
- Fulfillment
|
||||
|
||||
# Similar config to /www/docs/docusaurus.config.js > redocusaurus
|
||||
# Allows to emulate rendering of API public documentation when using `yarn redocly preview-docs openapi.yaml`
|
||||
theme:
|
||||
openapi:
|
||||
theme:
|
||||
colors:
|
||||
primary:
|
||||
dart: "#242526"
|
||||
sidebar:
|
||||
width: "250px"
|
||||
disableSearch: true
|
||||
expandResponses: "200,204"
|
||||
generatedPayloadSamplesMaxDepth: 4
|
||||
hideDownloadButton: true
|
||||
hideRequestPayloadSample: true
|
||||
nativeScrollbars: true
|
||||
requiredPropsFirst: true
|
||||
showObjectSchemaExamples: true
|
||||
sortTagsAlphabetically: true
|
||||
@@ -1,87 +0,0 @@
|
||||
module.exports = CircularPatch
|
||||
|
||||
/**
|
||||
* Since ref() is triggered upon reaching a $ref OAS line (leaf),
|
||||
* we want to inverse `schemas` instructions in order to optimize iterators.
|
||||
*/
|
||||
function preparePatches(schemas) {
|
||||
const patches = []
|
||||
const patchesObj = {}
|
||||
for (const schemaToPatch in schemas) {
|
||||
for (const schemaName of schemas[schemaToPatch]) {
|
||||
if (!patchesObj[schemaName]) {
|
||||
patchesObj[schemaName] = {
|
||||
schemaName,
|
||||
schemaPointer: `#/components/schemas/${schemaName}`,
|
||||
schemaToPatchPointers: [],
|
||||
}
|
||||
}
|
||||
const schemaToPatchPointer = `#/components/schemas/${schemaToPatch}`
|
||||
if (
|
||||
!patchesObj[schemaName].schemaToPatchPointers.includes(
|
||||
schemaToPatchPointer
|
||||
)
|
||||
) {
|
||||
patchesObj[schemaName].schemaToPatchPointers.push(schemaToPatchPointer)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const key in patchesObj) {
|
||||
patches.push(patchesObj[key])
|
||||
}
|
||||
return patches
|
||||
}
|
||||
|
||||
function applyPatch(node, schemaName) {
|
||||
delete node["$ref"]
|
||||
node.type = "object"
|
||||
if (!node.description && schemaName) {
|
||||
node.description = `${schemaName} object.`
|
||||
}
|
||||
}
|
||||
|
||||
function CircularPatch({ schemas = {}, verbose = false }) {
|
||||
const logs = []
|
||||
const patches = preparePatches(schemas)
|
||||
const refPathPrefix = "#/components/schemas/"
|
||||
const refPathPrefixLength = refPathPrefix.length
|
||||
const refPathPrefixRegex = /#\/components\/schemas\/\w+/
|
||||
return {
|
||||
ref(ref, ctx, resolved) {
|
||||
if (
|
||||
ctx.type.name.toLowerCase() !== "schema" ||
|
||||
!resolved?.location?.pointer
|
||||
) {
|
||||
return
|
||||
}
|
||||
for (const patch of patches) {
|
||||
if (resolved.location.pointer !== patch.schemaPointer) {
|
||||
continue
|
||||
}
|
||||
const ctxSchemaPointer =
|
||||
ctx.location.pointer.match(refPathPrefixRegex)[0]
|
||||
if (!patch.schemaToPatchPointers.includes(ctxSchemaPointer)) {
|
||||
continue
|
||||
}
|
||||
applyPatch(ref)
|
||||
if (verbose) {
|
||||
logs.push(
|
||||
`${ctxSchemaPointer.substring(refPathPrefixLength)} patch $ref to ${
|
||||
patch.schemaName
|
||||
}`
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
Root: {
|
||||
leave() {
|
||||
if (verbose) {
|
||||
logs.sort()
|
||||
for (const log of logs) {
|
||||
console.debug(log)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
const CircularPatch = require("./decorators/circular-patch")
|
||||
|
||||
const id = "plugin"
|
||||
|
||||
const decorators = {
|
||||
oas3: {
|
||||
"circular-patch": CircularPatch,
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
id,
|
||||
decorators,
|
||||
}
|
||||
Reference in New Issue
Block a user