chore: move openapi validation and dereferencing to a worker (#4789)
This commit is contained in:
parent
c7a7ec3bb2
commit
3563e1eb16
3 changed files with 102 additions and 6 deletions
|
|
@ -27,13 +27,19 @@ import * as S from "fp-ts/string"
|
||||||
import * as O from "fp-ts/Option"
|
import * as O from "fp-ts/Option"
|
||||||
import * as TE from "fp-ts/TaskEither"
|
import * as TE from "fp-ts/TaskEither"
|
||||||
import * as RA from "fp-ts/ReadonlyArray"
|
import * as RA from "fp-ts/ReadonlyArray"
|
||||||
|
import * as E from "fp-ts/Either"
|
||||||
import { IMPORTER_INVALID_FILE_FORMAT } from "."
|
import { IMPORTER_INVALID_FILE_FORMAT } from "."
|
||||||
import { cloneDeep, isNumber } from "lodash-es"
|
import { cloneDeep, isNumber } from "lodash-es"
|
||||||
import { getStatusCodeReasonPhrase } from "~/helpers/utils/statusCodes"
|
import { getStatusCodeReasonPhrase } from "~/helpers/utils/statusCodes"
|
||||||
|
|
||||||
export const OPENAPI_DEREF_ERROR = "openapi/deref_error" as const
|
export const OPENAPI_DEREF_ERROR = "openapi/deref_error" as const
|
||||||
|
|
||||||
// TODO: URL Import Support
|
const worker = new Worker(
|
||||||
|
new URL("./workers/openapi-import-worker.ts", import.meta.url),
|
||||||
|
{
|
||||||
|
type: "module",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const safeParseJSON = (str: string) => O.tryCatch(() => JSON.parse(str))
|
const safeParseJSON = (str: string) => O.tryCatch(() => JSON.parse(str))
|
||||||
|
|
||||||
|
|
@ -927,10 +933,7 @@ export const hoppOpenAPIImporter = (fileContents: string[]) =>
|
||||||
throw new Error("INVALID_OPENAPI_SPEC")
|
throw new Error("INVALID_OPENAPI_SPEC")
|
||||||
}
|
}
|
||||||
|
|
||||||
const validatedDoc = await SwaggerParser.validate(docObj, {
|
const validatedDoc = await validateDocs(docObj)
|
||||||
// @ts-expect-error - this is a valid option, but seems like the types are not updated
|
|
||||||
continueOnError: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
resultDoc.push(validatedDoc)
|
resultDoc.push(validatedDoc)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -969,7 +972,8 @@ export const hoppOpenAPIImporter = (fileContents: string[]) =>
|
||||||
const resultDoc = []
|
const resultDoc = []
|
||||||
|
|
||||||
for (const docObj of docArr) {
|
for (const docObj of docArr) {
|
||||||
const validatedDoc = await SwaggerParser.dereference(docObj)
|
const validatedDoc = await dereferenceDocs(docObj)
|
||||||
|
|
||||||
resultDoc.push(validatedDoc)
|
resultDoc.push(validatedDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -981,3 +985,41 @@ export const hoppOpenAPIImporter = (fileContents: string[]) =>
|
||||||
),
|
),
|
||||||
TE.chainW(convertOpenApiDocsToHopp)
|
TE.chainW(convertOpenApiDocsToHopp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const validateDocs = (docs: any): Promise<OpenAPI.Document> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
worker.postMessage({
|
||||||
|
type: "validate",
|
||||||
|
docs,
|
||||||
|
})
|
||||||
|
|
||||||
|
worker.onmessage = (event) => {
|
||||||
|
if (event.data.type === "VALIDATION_RESULT") {
|
||||||
|
if (E.isLeft(event.data.data)) {
|
||||||
|
reject("COULD_NOT_VALIDATE")
|
||||||
|
} else {
|
||||||
|
resolve(event.data.data.right as OpenAPI.Document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dereferenceDocs = (docs: any): Promise<OpenAPI.Document> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
worker.postMessage({
|
||||||
|
type: "dereference",
|
||||||
|
docs,
|
||||||
|
})
|
||||||
|
|
||||||
|
worker.onmessage = (event) => {
|
||||||
|
if (event.data.type === "DEREFERENCE_RESULT") {
|
||||||
|
if (E.isLeft(event.data.data)) {
|
||||||
|
reject("COULD_NOT_DEREFERENCE")
|
||||||
|
} else {
|
||||||
|
resolve(event.data.data.right as OpenAPI.Document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { Buffer } from "buffer"
|
||||||
|
import process from "process"
|
||||||
|
|
||||||
|
// Set up global shims for the swagger-parser library
|
||||||
|
self.Buffer = Buffer
|
||||||
|
self.global = self
|
||||||
|
self.process = process
|
||||||
|
|
||||||
|
import SwaggerParser from "@apidevtools/swagger-parser"
|
||||||
|
import * as E from "fp-ts/Either"
|
||||||
|
|
||||||
|
const validateDocs = async (docs: any) => {
|
||||||
|
try {
|
||||||
|
const res = await SwaggerParser.validate(docs, {
|
||||||
|
// @ts-expect-error - this is a valid option, but seems like the types are not updated
|
||||||
|
continueOnError: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return E.right(res)
|
||||||
|
} catch (error) {
|
||||||
|
return E.left("COULD_NOT_VALIDATE" as const)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dereferenceDocs = async (docs: any) => {
|
||||||
|
try {
|
||||||
|
const res = await SwaggerParser.dereference(docs)
|
||||||
|
|
||||||
|
return E.right(res)
|
||||||
|
} catch (error) {
|
||||||
|
return E.left("COULD_NOT_DEREFERENCE" as const)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("message", async (event) => {
|
||||||
|
if (event.data.type === "validate") {
|
||||||
|
const res = await validateDocs(event.data.docs)
|
||||||
|
|
||||||
|
self.postMessage({
|
||||||
|
type: "VALIDATION_RESULT",
|
||||||
|
data: res,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data.type === "dereference") {
|
||||||
|
const res = await dereferenceDocs(event.data.docs)
|
||||||
|
|
||||||
|
self.postMessage({
|
||||||
|
type: "DEREFERENCE_RESULT",
|
||||||
|
data: res,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -27,6 +27,7 @@ export default defineConfig({
|
||||||
define: {
|
define: {
|
||||||
// For 'util' polyfill required by dep of '@apidevtools/swagger-parser'
|
// For 'util' polyfill required by dep of '@apidevtools/swagger-parser'
|
||||||
"process.env": {},
|
"process.env": {},
|
||||||
|
"process.platform": '"browser"',
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue