Skip to content

Commit

Permalink
fix: support local file refs
Browse files Browse the repository at this point in the history
  • Loading branch information
solufa committed Jun 7, 2020
1 parent b4c6bdc commit 7b69860
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 21 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"@types/jest": "^25.2.3",
"@types/js-yaml": "^3.12.4",
"@types/minimist": "^1.2.0",
"@typescript-eslint/eslint-plugin": "2.34.0",
"@typescript-eslint/parser": "^2.34.0",
Expand Down
51 changes: 30 additions & 21 deletions src/resolveExternalRefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import https from 'https'
import http from 'http'
import fs from 'fs'
import path from 'path'
import yaml from 'js-yaml'
import { OpenAPIV3 } from 'openapi-types'

const getText = (url: string) =>
Expand All @@ -24,31 +25,32 @@ const getText = (url: string) =>
})
})

type DocInfo = { url: string; doc: OpenAPIV3.Document }
type DocType = Record<string, any>
type DocInfo = { url: string; doc: DocType }
const hasExternalRegExp = /"\$ref":"[^#].+?"/g

const fetchExternalDocs = async (docs: OpenAPIV3.Document, inputDir: string) => {
const fetchExternalDocs = async (docs: DocType, inputDir: string) => {
const docList: DocInfo[] = []
const fetchingUrls: string[] = []

const fetchDocs = (d: OpenAPIV3.Document, input: string) =>
const fetchDocs = (d: DocType, input: string) =>
Promise.all(
(JSON.stringify(d).match(hasExternalRegExp) ?? []).map(async ref => {
const [, url] = ref.match(/"\$ref":"(.+?)#/)!
const [, url] = ref.match(/"\$ref":"(.+?)[#"]/)!

if (fetchingUrls.includes(url)) return

fetchingUrls.push(url)
const doc: OpenAPIV3.Document = JSON.parse(
await (url.startsWith('http')
? getText(url)
: input.startsWith('http')
? getText(path.join(input, url))
: fs.promises.readFile(path.join(input, url), 'utf8'))
)

const filePath = url.startsWith('http')
? url
: path.posix.join(input.split('/').slice(0, -1).join('/'), url)
const text = await (filePath.startsWith('http')
? getText(filePath)
: fs.promises.readFile(filePath, 'utf8'))
const doc: DocType = filePath.endsWith('.json') ? JSON.parse(text) : yaml.safeLoad(text)
docList[fetchingUrls.indexOf(url)] = { url, doc }

await fetchDocs(doc, url.startsWith('http') ? url : path.join(input, url))
await fetchDocs(doc, filePath)
})
)

Expand All @@ -58,21 +60,27 @@ const fetchExternalDocs = async (docs: OpenAPIV3.Document, inputDir: string) =>

const getComponentInfo = (docList: DocInfo[], url: string, prop: string) => {
const data = docList.find(d => d.url === url)!.doc
const target = prop.split('/').reduce((prev, current) => prev[current], data as any)
if (target.name) return { type: 'parameters' as const, data: target }
return { type: 'schemas' as const, data: target }
const target = prop ? prop.split('/').reduce((prev, current) => prev[current], data) : data

if (target.name) return { type: 'parameters', data: target }
return { type: 'schemas', data: target }
}

const genExternalTypeName = (docList: DocInfo[], url: string, prop: string) =>
`External${docList.findIndex(d => d.url === url)}${prop ? `_${prop.split('/').pop()}` : ''}`

const resolveExternalDocs = async (docs: OpenAPIV3.Document, inputDir: string) => {
const externalDocs = await fetchExternalDocs(docs, inputDir)
const componentsInfoList: { url: string; prop: string; name: string }[] = []
const replacedExternalDocs = externalDocs.map((selfDoc: DocInfo) => {
let docsString = JSON.stringify(selfDoc.doc)
;(docsString.match(/"\$ref":".+?"/g) ?? []).forEach(refs => {
const targetText = refs.replace('"$ref":"', '').slice(0, -1)
const [, url = selfDoc.url, prop] = targetText.match(/(.+?)?#\/(.+)/)!
const [urlBase, propBase = '/'] = targetText.split('#')
const url = urlBase || selfDoc.url
const prop = propBase.slice(1)
const info = getComponentInfo(externalDocs, url, prop)
const name = `External${externalDocs.findIndex(d => d.url === url)}_${prop.split('/').pop()}`
const name = genExternalTypeName(externalDocs, url, prop)
docsString = docsString.replace(targetText, `#/components/${info.type}/${name}`)
componentsInfoList.push({ url, prop, name })
})
Expand All @@ -85,7 +93,7 @@ const resolveExternalDocs = async (docs: OpenAPIV3.Document, inputDir: string) =
components: componentsInfoList.reduce((prev, { url, prop, name }) => {
const info = getComponentInfo(replacedExternalDocs, url, prop)
return { ...prev, [info.type]: { ...prev[info.type], [name]: info.data } }
}, {} as Record<string, any>)
}, {} as DocType)
}
}

Expand All @@ -95,11 +103,12 @@ export default async (docs: OpenAPIV3.Document, inputDir: string): Promise<OpenA
let docsString = JSON.stringify(docs)
;(docsString.match(hasExternalRegExp) ?? []).forEach(refs => {
const targetText = refs.replace('"$ref":"', '').slice(0, -1)
const [, url, prop] = targetText.match(/(.+?)#\/(.+)/)!
const [url, propBase = '/'] = targetText.split('#')
const prop = propBase.slice(1)

const info = getComponentInfo(externalDocs, url, prop)
components[info.type] = components[info.type] || {}
const name = `External${externalDocs.findIndex(d => d.url === url)}_${prop.split('/').pop()}`
const name = genExternalTypeName(externalDocs, url, prop)
Object.assign(components[info.type], { [name]: info.data })
docsString = docsString.replace(targetText, `#/components/${info.type}/${name}`)
})
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,11 @@
jest-diff "^25.2.1"
pretty-format "^25.2.1"

"@types/js-yaml@^3.12.4":
version "3.12.4"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.4.tgz#7d3b534ec35a0585128e2d332db1403ebe057e25"
integrity sha512-fYMgzN+9e28R81weVN49inn/u798ruU91En1ZnGvSZzCRc5jXx9B2EDhlRaWmcO1RIxFHL8AajRXzxDuJu93+A==

"@types/json-schema@^7.0.3":
version "7.0.4"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
Expand Down

0 comments on commit 7b69860

Please sign in to comment.