feat(docs): OAS circular reference check shall fail openapi:generate (#3269)

### What

Unhandled OAS circular references shall fail `openapi:generate` until they have been patched in `docs-util/redocly/config.yaml`

### Why

Prevent developers to commit new OAS changes that would cause to API documentation to crash.

Our API documentation rendering library will crash and not load if our OAS contains circular references. We have an automated mechanism to patch offending references but they must be identified and configured by hand.

### How

Let the `openapi:generate --dry-run` command reach OAS sanitization and circular reference check operations. 

Fail the build script if unhandled circular references are detected. 

Output the offending references to the stdout in order to help the developers identify and configure the required patch.

Since some the tooling involved only work with files, use the host system's temporary directory mechanism when using the `--dry-run` flag.

### Test

* Introduce a bug by removing an entry in `docs-util/redocly/config.yaml`
* Run `yarn openapi:generate --dry-run`
* Expect the script to exit before completing
* Expect the logs to contain `🔴 Unhandled circular references.`
* Expect the logs to contain an array of the offending references
This commit is contained in:
Patrick authored and GitHub committed 2023-02-17 12:11:27 +00:00
1 parent aefe5aa133
commit 3e274edb9e
2 files changed
+28 -9

No files matched your search

+1
View File
@@ -2,6 +2,7 @@ name: OAS Comments Format Validation
on: on:
pull_request: pull_request:
paths: paths:
- packages/medusa/oas/**
- packages/medusa/src/api/** - packages/medusa/src/api/**
- packages/medusa/src/models/** - packages/medusa/src/models/**
- packages/medusa/src/types/** - packages/medusa/src/types/**
+27 -9
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
const fs = require("fs/promises") const fs = require("fs/promises")
const os = require("os")
const path = require("path") const path = require("path")
const execa = require("execa") const execa = require("execa")
const yaml = require("js-yaml") const yaml = require("js-yaml")
@@ -11,19 +12,20 @@ const basePath = path.resolve(__dirname, `../`)
const docsApiPath = path.resolve(basePath, "docs/api/") const docsApiPath = path.resolve(basePath, "docs/api/")
const run = async () => { const run = async () => {
await generateOASSources(docsApiPath, isDryRun) const outputPath = isDryRun ? await getTmpDirectory() : docsApiPath
if (isDryRun) {
return await generateOASSources(outputPath)
}
for (const apiType of ["store", "admin"]) { for (const apiType of ["store", "admin"]) {
const inputJsonFile = path.resolve(docsApiPath, `${apiType}.oas.json`) const inputJsonFile = path.resolve(outputPath, `${apiType}.oas.json`)
const outputYamlFile = path.resolve(docsApiPath, `${apiType}.oas.yaml`) const outputYamlFile = path.resolve(outputPath, `${apiType}.oas.yaml`)
await jsonFileToYamlFile(inputJsonFile, outputYamlFile) await jsonFileToYamlFile(inputJsonFile, outputYamlFile)
await sanitizeOAS(outputYamlFile) await sanitizeOAS(outputYamlFile)
await circularReferenceCheck(outputYamlFile) await circularReferenceCheck(outputYamlFile)
await generateReference(outputYamlFile, apiType) if (!isDryRun) {
await generateReference(outputYamlFile, apiType)
}
} }
} }
@@ -68,10 +70,13 @@ const circularReferenceCheck = async (srcFile) => {
}, },
}) })
if (parser.$refs.circular) { if (parser.$refs.circular) {
console.log(`🔴 Unhandled circular references - ${srcFile}`) const fileName = path.basename(srcFile)
const circularRefs = [...parser.$refs.circularRefs] const circularRefs = [...parser.$refs.circularRefs]
circularRefs.sort() circularRefs.sort()
console.log(circularRefs) console.log(circularRefs)
throw new Error(
`🔴 Unhandled circular references - ${fileName} - Please patch in docs-util/redocly/config.yaml`
)
} }
} }
@@ -86,6 +91,19 @@ const generateReference = async (srcFile, apiType) => {
console.log(logs) console.log(logs)
} }
const getTmpDirectory = async () => {
/**
* RUNNER_TEMP: GitHub action, the path to a temporary directory on the runner.
*/
const tmpDir = process.env["RUNNER_TEMP"] ?? os.tmpdir()
return await fs.mkdtemp(tmpDir)
}
void (async () => { void (async () => {
await run() try {
await run()
} catch (err) {
console.log(err)
process.exit(1)
}
})() })()