diff --git a/packages/server/package.json b/packages/server/package.json index c73fe874ae..63669e5b2f 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -68,6 +68,7 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { + "@apidevtools/swagger-parser": "^10.0.3", "@budibase/auth": "^0.9.185-alpha.21", "@budibase/client": "^0.9.185-alpha.21", "@budibase/string-templates": "^0.9.185-alpha.21", @@ -77,7 +78,6 @@ "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", "@sentry/node": "^6.0.0", - "@types/swagger-schema-official": "^2.0.22", "airtable": "0.10.1", "arangojs": "7.2.0", "aws-sdk": "^2.767.0", @@ -109,6 +109,7 @@ "mysql2": "^2.3.1", "node-fetch": "2.6.0", "open": "7.3.0", + "openapi-types": "^9.3.1", "pg": "8.5.1", "pino-pretty": "4.0.0", "posthog-node": "^1.1.4", diff --git a/packages/server/src/api/controllers/query/import.ts b/packages/server/src/api/controllers/query/import.ts deleted file mode 100644 index 93f4b27ab1..0000000000 --- a/packages/server/src/api/controllers/query/import.ts +++ /dev/null @@ -1,334 +0,0 @@ -import CouchDB from "../../../db" -import { queryValidation } from "./validation" -import { generateQueryID } from "../../../db/utils" -import { Spec as Swagger2, Operation } from "swagger-schema-official" -const curlconverter = require("curlconverter") -import { URL } from "url" - -// { -// "_id": "query_datasource_d62738f2d72a466997ffbf46f4952404_e7258ad382cd4c37961b81730633ff2d", -// "_rev": "1-e702a18eaa96c7cb4be1b402c34eaa59", -// "datasourceId": "datasource_d62738f2d72a466997ffbf46f4952404", -// "parameters": [ -// { -// "name": "paramtest", -// "default": "defaultValue" -// } -// ], -// "fields": { -// "headers": { -// "headertest": "test" -// }, -// "queryString": "query=test", -// "path": "/path/test" -// }, -// "queryVerb": "read", -// "transformer": "return data.test", -// "schema": {}, -// "name": "name", -// "readable": true -// } - -// return joiValidator.body(Joi.object({ -// _id: Joi.string(), -// _rev: Joi.string(), -// name: Joi.string().required(), -// fields: Joi.object().required(), -// datasourceId: Joi.string().required(), -// readable: Joi.boolean(), -// parameters: Joi.array().items(Joi.object({ -// name: Joi.string(), -// default: Joi.string().allow(""), -// })), -// queryVerb: Joi.string().allow().required(), -// extra: Joi.object().optional(), -// schema: Joi.object({}).required().unknown(true), -// transformer: Joi.string().optional(), -// })) - -interface Parameter { - name: string - default: string -} - -interface Query { - _id?: string - datasourceId: string - name: string - parameters: Parameter[] - fields: { - headers: any - queryString: string - path: string - } - transformer: string | null - schema: any - readable: boolean - queryVerb: string -} - -enum Strategy { - SWAGGER2, - OPENAPI3, - CURL, -} - -enum MethodToVerb { - get = "read", - post = "create", - put = "update", - patch = "patch", - delete = "delete", -} - -interface ImportResult { - errorQueries: Query[] -} - -interface DatasourceInfo { - url: string - name: string - defaultHeaders: any[] -} - -const parseImportStrategy = (data: string): Strategy => { - try { - const json = JSON.parse(data) - if (json.swagger === "2.0") { - return Strategy.CURL - } else if (json.openapi?.includes("3.0")) { - return Strategy.OPENAPI3 - } - } catch (jsonError) { - try { - parseCurl(data) - return Strategy.CURL - } catch (curlError) { - // do nothing - } - } - - throw new Error(`The import data could not be processed`) -} - -const processPath = (path: string): string => { - if (path?.startsWith("/")) { - return path.substring(1) - } - - return path -} - -// SWAGGER - -const parseSwagger2Info = (swagger2: Swagger2): DatasourceInfo => { - const scheme = swagger2.schemes?.includes("https") ? "https" : "http" - const basePath = swagger2.basePath || "" - const host = swagger2.host || "" - const url = `${scheme}://${host}${basePath}` - const name = swagger2.info.title || "Swagger Import" - - return { - url: url, - name: name, - defaultHeaders: [], - } -} - -const parseSwagger2Queries = ( - datasourceId: string, - swagger2: Swagger2 -): Query[] => { - const queries = [] - - for (let [pathName, path] of Object.entries(swagger2.paths)) { - for (let [methodName, op] of Object.entries(path)) { - let operation = op as Operation - - const name = operation.operationId || pathName - const queryString = "" - const headers = {} - const parameters: Parameter[] = [] - - const query = constructQuery( - datasourceId, - name, - methodName, - pathName, - queryString, - headers, - parameters - ) - queries.push(query) - } - } - - return queries -} - -// OPEN API - -const parseOpenAPI3Info = (data: any): DatasourceInfo => { - return { - url: "http://localhost:3000", - name: "swagger", - defaultHeaders: [], - } -} - -const parseOpenAPI3Queries = (datasourceId: string, data: string): Query[] => { - return [] -} - -// CURL - -const parseCurl = (data: string): any => { - const curlJson = curlconverter.toJsonString(data) - return JSON.parse(curlJson) -} - -const parseCurlDatasourceInfo = (data: any): DatasourceInfo => { - const curl = parseCurl(data) - - const url = new URL(curl.url) - - return { - url: url.origin, - name: url.hostname, - defaultHeaders: [], - } -} - -const parseCurlQueries = (datasourceId: string, data: string): Query[] => { - const curl = parseCurl(data) - - const url = new URL(curl.url) - const name = url.pathname - const path = url.pathname - const method = curl.method - const queryString = url.search - const headers = curl.headers - - const query = constructQuery( - datasourceId, - name, - method, - path, - queryString, - headers - ) - return [query] -} - -const verbFromMethod = (method: string) => { - const verb = (MethodToVerb)[method] - if (!verb) { - throw new Error(`Unsupported method: ${method}`) - } - return verb -} - -const constructQuery = ( - datasourceId: string, - name: string, - method: string, - path: string, - queryString: string, - headers: any = {}, - parameters: Parameter[] = [] -): Query => { - const readable = true - const queryVerb = verbFromMethod(method) - const transformer = "return data" - const schema = {} - path = processPath(path) - - const query: Query = { - datasourceId, - name, - parameters, - fields: { - headers, - queryString, - path, - }, - transformer, - schema, - readable, - queryVerb, - } - - return query -} - -export const getDatasourceInfo = (data: string): DatasourceInfo => { - const strategy = parseImportStrategy(data) - - let info: DatasourceInfo - switch (strategy) { - case Strategy.SWAGGER2: - info = parseSwagger2Info(JSON.parse(data)) - break - case Strategy.OPENAPI3: - info = parseOpenAPI3Info(JSON.parse(data)) - break - case Strategy.CURL: - info = parseCurlDatasourceInfo(data) - break - } - - return info -} - -export const importQueries = async ( - appId: string, - datasourceId: string, - data: string -): Promise => { - const strategy = parseImportStrategy(data) - - // constuct the queries - let queries: Query[] - switch (strategy) { - case Strategy.SWAGGER2: - queries = parseSwagger2Queries(datasourceId, JSON.parse(data)) - break - case Strategy.OPENAPI3: - queries = parseOpenAPI3Queries(datasourceId, JSON.parse(data)) - break - case Strategy.CURL: - queries = parseCurlQueries(datasourceId, data) - break - } - - // validate queries - const errorQueries = [] - const schema = queryValidation() - queries = queries - .filter(query => { - const validation = schema.validate(query) - if (validation.error) { - errorQueries.push(query) - return false - } - return true - }) - .map(query => { - query._id = generateQueryID(query.datasourceId) - return query - }) - - // persist queries - const db = new CouchDB(appId) - for (const query of queries) { - try { - await db.put(query) - } catch (error) { - errorQueries.push(query) - } - } - - return { - errorQueries, - } -} diff --git a/packages/server/src/api/controllers/query/import/index.ts b/packages/server/src/api/controllers/query/import/index.ts new file mode 100644 index 0000000000..808959798d --- /dev/null +++ b/packages/server/src/api/controllers/query/import/index.ts @@ -0,0 +1,78 @@ +import CouchDB from "../../../../db" +import { queryValidation } from "../validation" +import { generateQueryID } from "../../../../db/utils" +import { Query, ImportInfo, ImportSource } from "./sources/base" +import { OpenAPI2 } from "./sources/openapi2" +import { OpenAPI3 } from "./sources/openapi3" +import { Curl } from "./sources/curl" + +interface ImportResult { + errorQueries: Query[] +} + +export class RestImporter { + data: string + sources: ImportSource[] + source!: ImportSource + + constructor(data: string) { + this.data = data + this.sources = [new OpenAPI2(), new OpenAPI3(), new Curl()] + } + + init = async () => { + for (let source of this.sources) { + if (await source.isSupported(this.data)){ + this.source = source + break + } + } + } + + getInfo = async (): Promise => { + return this.source.getInfo() + } + + importQueries = async ( + appId: string, + datasourceId: string, + ): Promise => { + + // constuct the queries + let queries = await this.source.getQueries(datasourceId) + + // validate queries + const errorQueries = [] + const schema = queryValidation() + queries = queries + .filter(query => { + const validation = schema.validate(query) + if (validation.error) { + errorQueries.push(query) + return false + } + return true + }) + .map(query => { + query._id = generateQueryID(query.datasourceId) + return query + }) + + // persist queries + const db = new CouchDB(appId) + for (const query of queries) { + try { + await db.put(query) + } catch (error) { + errorQueries.push(query) + } + } + + return { + errorQueries, + } + } + +} + + diff --git a/packages/server/src/api/controllers/query/import/sources/base/index.ts b/packages/server/src/api/controllers/query/import/sources/base/index.ts new file mode 100644 index 0000000000..f6a6b2b57c --- /dev/null +++ b/packages/server/src/api/controllers/query/import/sources/base/index.ts @@ -0,0 +1,92 @@ +export interface ImportInfo { + url: string + name: string +} + +export interface QueryParameter { + name: string + default: string +} + +export interface Query { + _id?: string + datasourceId: string + name: string + parameters: QueryParameter[] + fields: { + headers: object + queryString: string | null + path: string + requestBody?: object + } + transformer: string | null + schema: any + readable: boolean + queryVerb: string +} + +enum MethodToVerb { + get = "read", + post = "create", + put = "update", + patch = "patch", + delete = "delete", +} + +export abstract class ImportSource { + + abstract isSupported(data: string): Promise + abstract getInfo(): Promise + abstract getQueries(datasourceId: string): Promise + + constructQuery = ( + datasourceId: string, + name: string, + method: string, + path: string, + queryString: string, + headers: object = {}, + parameters: QueryParameter[] = [], + requestBody: object | undefined = undefined, + ): Query => { + const readable = true + const queryVerb = this.verbFromMethod(method) + const transformer = "return data" + const schema = {} + path = this.processPath(path) + + const query: Query = { + datasourceId, + name, + parameters, + fields: { + headers, + queryString, + path, + requestBody + }, + transformer, + schema, + readable, + queryVerb, + } + + return query + } + + verbFromMethod = (method: string) => { + const verb = (MethodToVerb)[method] + if (!verb) { + throw new Error(`Unsupported method: ${method}`) + } + return verb + } + + processPath = (path: string): string => { + if (path?.startsWith("/")) { + return path.substring(1) + } + + return path + } +} diff --git a/packages/server/src/api/controllers/query/import/sources/base/openapi.ts b/packages/server/src/api/controllers/query/import/sources/base/openapi.ts new file mode 100644 index 0000000000..dfe3c68c08 --- /dev/null +++ b/packages/server/src/api/controllers/query/import/sources/base/openapi.ts @@ -0,0 +1,13 @@ + +import { ImportSource } from "." +import SwaggerParser from "@apidevtools/swagger-parser"; +import { OpenAPI } from "openapi-types"; + +export abstract class OpenAPISource extends ImportSource { + + parseData = async (data: string): Promise => { + const json = JSON.parse(data) + return SwaggerParser.validate(json, {}) + } + +} diff --git a/packages/server/src/api/controllers/query/import/sources/curl.ts b/packages/server/src/api/controllers/query/import/sources/curl.ts new file mode 100644 index 0000000000..b5a3e624d9 --- /dev/null +++ b/packages/server/src/api/controllers/query/import/sources/curl.ts @@ -0,0 +1,54 @@ +import { ImportSource, ImportInfo, Query } from "./base" +import { URL } from 'url' +const curlconverter = require("curlconverter") + +const parseCurl = (data: string): any => { + const curlJson = curlconverter.toJsonString(data) + return JSON.parse(curlJson) +} + +/** + * Curl + * https://curl.se/docs/manpage.html + */ +export class Curl extends ImportSource { + curl: any + + isSupported = async (data: string): Promise => { + try { + const curl = parseCurl(data) + this.curl = curl + } catch (err) { + return false + } + return true + } + + getInfo = async (): Promise => { + const url = new URL(this.curl.url) + return { + url: url.origin, + name: url.hostname, + } + } + + getQueries = async (datasourceId: string): Promise => { + const url = new URL(this.curl.url) + const name = url.pathname + const path = url.pathname + const method = this.curl.method + const queryString = url.search + const headers = this.curl.headers + + const query = this.constructQuery( + datasourceId, + name, + method, + path, + queryString, + headers + ) + + return [query] + } +} diff --git a/packages/server/src/api/controllers/query/import/sources/openapi2.ts b/packages/server/src/api/controllers/query/import/sources/openapi2.ts new file mode 100644 index 0000000000..05ee41d531 --- /dev/null +++ b/packages/server/src/api/controllers/query/import/sources/openapi2.ts @@ -0,0 +1,104 @@ +import { ImportInfo, QueryParameter, Query } from "./base" +import { OpenAPIV2 } from "openapi-types" +import { OpenAPISource } from "./base/openapi"; + +const isBodyParameter = (param: OpenAPIV2.Parameter): param is OpenAPIV2.InBodyParameterObject => { + return param.in === "body" +} + +const isParameter = (param: OpenAPIV2.Parameter | OpenAPIV2.ReferenceObject): param is OpenAPIV2.Parameter => { + // we can guarantee this is not a reference object + // due to the deferencing done by the parser library + return true +} + +const isOpenAPI2 = (document: any): document is OpenAPIV2.Document => { + if (document.swagger === "2.0") { + return true + } else { + return false + } +} + +/** + * OpenAPI Version 2.0 - aka "Swagger" + * https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md + */ +export class OpenAPI2 extends OpenAPISource { + document!: OpenAPIV2.Document + + isSupported = async (data: string): Promise => { + try { + const document: any = await this.parseData(data) + if (isOpenAPI2(document)) { + this.document = document + return true + } else { + return false + } + } catch (err) { + return false + } + } + + getInfo = async (): Promise => { + const scheme = this.document.schemes?.includes("https") ? "https" : "http" + const basePath = this.document.basePath || "" + const host = this.document.host || "" + const url = `${scheme}://${host}${basePath}` + const name = this.document.info.title || "Swagger Import" + + return { + url: url, + name: name, + } + } + + getQueries = async (datasourceId: string): Promise => { + const queries = [] + + let pathName: string + let path: OpenAPIV2.PathItemObject + + for ([pathName, path] of Object.entries(this.document.paths)) { + for (let [methodName, op] of Object.entries(path)) { + let operation = op as OpenAPIV2.OperationObject + + const name = operation.operationId || pathName + const queryString = "" + const headers = {} + let requestBody = undefined + const parameters: QueryParameter[] = [] + + if (operation.parameters) { + for (let param of operation.parameters) { + if (isParameter(param)) { + if (isBodyParameter(param)) { + requestBody = {} + } else { + parameters.push({ + name: param.name, + default: "", + }) + } + } + } + } + + const query = this.constructQuery( + datasourceId, + name, + methodName, + pathName, + queryString, + headers, + parameters, + requestBody + ) + queries.push(query) + } + } + + return queries + } +} diff --git a/packages/server/src/api/controllers/query/import/sources/openapi3.ts b/packages/server/src/api/controllers/query/import/sources/openapi3.ts new file mode 100644 index 0000000000..655c680ed4 --- /dev/null +++ b/packages/server/src/api/controllers/query/import/sources/openapi3.ts @@ -0,0 +1,40 @@ +import { ImportInfo, Query } from "./base" +import { OpenAPISource } from "./base/openapi" +import { OpenAPIV3 } from "openapi-types" + +const isOpenAPI3 = (document: any): document is OpenAPIV3.Document => { + return document.openapi === "3.0.0" +} + +/** + * OpenAPI Version 3.0.0 + * https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md + */ +export class OpenAPI3 extends OpenAPISource { + document!: OpenAPIV3.Document + + isSupported = async (data: string): Promise => { + try { + const document: any = await this.parseData(data) + if (isOpenAPI3(document)) { + this.document = document + return true + } else { + return false + } + } catch (err) { + return false + } + } + + getInfo = async (): Promise => { + return { + url: "http://localhost:3000", + name: "swagger", + } + } + + getQueries = async (datasourceId: string): Promise => { + return [] + } +} diff --git a/packages/server/src/api/controllers/query/import/sources/tests/curl.spec.js b/packages/server/src/api/controllers/query/import/sources/tests/curl.spec.js new file mode 100644 index 0000000000..da90f4a61c --- /dev/null +++ b/packages/server/src/api/controllers/query/import/sources/tests/curl.spec.js @@ -0,0 +1,71 @@ +// const Airtable = require("airtable") +// const AirtableIntegration = require("../airtable") + +jest.mock("airtable") + +// class TestConfiguration { +// constructor(config = {}) { +// this.integration = new AirtableIntegration.integration(config) +// this.client = { +// create: jest.fn(), +// select: jest.fn(), +// update: jest.fn(), +// destroy: jest.fn(), +// } +// this.integration.client = () => this.client +// } +// } + +describe("Airtable Integration", () => { + let config + + beforeEach(() => { + config = new TestConfiguration() + }) + + it("calls the create method with the correct params", async () => { + const response = await config.integration.create({ + table: "test", + json: {} + }) + expect(config.client.create).toHaveBeenCalledWith([ + { + fields: {} + } + ]) + }) + + it("calls the read method with the correct params", async () => { + const response = await config.integration.read({ + table: "test", + view: "Grid view" + }) + expect(config.client.select).toHaveBeenCalledWith({ + maxRecords: 10, view: "Grid view" + }) + }) + + it("calls the update method with the correct params", async () => { + const response = await config.integration.update({ + table: "test", + id: "123", + json: { + name: "test" + } + }) + expect(config.client.update).toHaveBeenCalledWith([ + { + id: "123", + fields: { name: "test" } + } + ]) + }) + + it("calls the delete method with the correct params", async () => { + const ids = [1,2,3,4] + const response = await config.integration.delete({ + ids + }) + expect(config.client.destroy).toHaveBeenCalledWith(ids) + }) +}) \ No newline at end of file diff --git a/packages/server/src/api/controllers/query/import/sources/tests/openapi.spec.js b/packages/server/src/api/controllers/query/import/sources/tests/openapi.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/server/src/api/controllers/query/import/sources/tests/openapi2.spec.js b/packages/server/src/api/controllers/query/import/sources/tests/openapi2.spec.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/server/src/api/controllers/query/index.js b/packages/server/src/api/controllers/query/index.js index fb3d233c4e..17d2b8aa36 100644 --- a/packages/server/src/api/controllers/query/index.js +++ b/packages/server/src/api/controllers/query/index.js @@ -4,8 +4,8 @@ const { generateQueryID, getQueryParams } = require("../../../db/utils") const { BaseQueryVerbs } = require("../../../constants") const env = require("../../../environment") const { Thread, ThreadType } = require("../../../threads") -const { importQueries, getDatasourceInfo } = require("./import") const { save: saveDatasource } = require("../datasource") +const { RestImporter } = require("./import") const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 }) @@ -37,16 +37,19 @@ exports.import = async ctx => { const body = ctx.request.body const data = body.data + const importer = new RestImporter(data) + await importer.init() + let datasourceId if (!body.datasourceId) { // construct new datasource - const info = getDatasourceInfo(data) + const info = await importer.getInfo() let datasource = { type: "datasource", source: "REST", config: { url: info.url, - defaultHeaders: info.defaultHeaders, + defaultHeaders: [], }, name: info.name, } @@ -60,7 +63,7 @@ exports.import = async ctx => { datasourceId = body.datasourceId } - const importResult = await importQueries(ctx.appId, datasourceId, data) + const importResult = await importer.importQueries(ctx.appId, datasourceId) ctx.body = { ...importResult, diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 997a856f6f..fb9c3a3baa 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -7,6 +7,38 @@ resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4" integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w== +"@apidevtools/json-schema-ref-parser@^9.0.6": + version "9.0.9" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b" + integrity sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.6" + call-me-maybe "^1.0.1" + js-yaml "^4.1.0" + +"@apidevtools/openapi-schemas@^2.0.4": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17" + integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== + +"@apidevtools/swagger-methods@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" + integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== + +"@apidevtools/swagger-parser@^10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz#32057ae99487872c4dd96b314a1ab4b95d89eaf5" + integrity sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g== + dependencies: + "@apidevtools/json-schema-ref-parser" "^9.0.6" + "@apidevtools/openapi-schemas" "^2.0.4" + "@apidevtools/swagger-methods" "^3.0.2" + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + z-schema "^5.0.1" + "@azure/abort-controller@^1.0.0": version "1.0.4" resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.4.tgz#fd3c4d46c8ed67aace42498c8e2270960250eafd" @@ -1826,6 +1858,11 @@ "@babel/runtime" "^7.7.2" regenerator-runtime "^0.13.3" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + "@koa/router@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.0.tgz#fd4ffa6f03d8293a04c023cb4a22b612401fbe70" @@ -2393,6 +2430,11 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" +"@types/json-schema@^7.0.6": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + "@types/keygrip@*": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" @@ -2500,11 +2542,6 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/swagger-schema-official@^2.0.22": - version "2.0.22" - resolved "https://registry.yarnpkg.com/@types/swagger-schema-official/-/swagger-schema-official-2.0.22.tgz#f7e06168e6994574dfd86928ac04b196870ab043" - integrity sha512-7yQiX6MWSFSvc/1wW5smJMZTZ4fHOd+hqLr3qr/HONDxHEa2bnYAsOcGBOEqFIjd4yetwMOdEDdeW+udRAQnHA== - "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" @@ -2865,6 +2902,11 @@ argparse@^1.0.10, argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + args@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/args/-/args-5.0.1.tgz#4bf298df90a4799a09521362c579278cc2fdd761" @@ -3535,6 +3577,11 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -3787,7 +3834,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.19.0, commander@^2.5.0, commander@^2.8.1: +commander@^2.19.0, commander@^2.5.0, commander@^2.7.1, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -7418,6 +7465,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbi@^3.1.1: version "3.2.5" resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" @@ -8096,6 +8150,11 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -8913,6 +8972,11 @@ open@7.3.0: is-docker "^2.0.0" is-wsl "^2.1.1" +openapi-types@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-9.3.1.tgz#617ae6db3efd3e3f68e849f65ced58801d01d3cf" + integrity sha512-/Yvsd2D7miYB4HLJ3hOOS0+vnowQpaT75FsHzr/y5M9P4q9bwa7RcbW2YdH6KZBn8ceLbKGnHxMZ1CHliGHUFw== + opencollective-postinstall@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" @@ -11887,6 +11951,11 @@ validate.js@0.13.1: resolved "https://registry.yarnpkg.com/validate.js/-/validate.js-0.13.1.tgz#b58bfac04a0f600a340f62e5227e70d95971e92a" integrity sha512-PnFM3xiZ+kYmLyTiMgTYmU7ZHkjBZz2/+F0DaALc/uUtVzdCt1wAosvYJ5hFQi/hz8O4zb52FQhHZRC+uVkJ+g== +validator@^13.7.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== + vary@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -12320,6 +12389,17 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== +z-schema@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.2.tgz#f410394b2c9fcb9edaf6a7511491c0bb4e89a504" + integrity sha512-40TH47ukMHq5HrzkeVE40Ad7eIDKaRV2b+Qpi2prLc9X9eFJFzV7tMe5aH12e6avaSS/u5l653EQOv+J9PirPw== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^2.7.1" + zlib@1.0.5, zlib@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0"