mirror of https://github.com/Budibase/budibase.git
56 changed files with 3981 additions and 1010 deletions
@ -0,0 +1,135 @@ |
|||||
|
<script> |
||||
|
import { goto } from "@roxi/routify" |
||||
|
import { |
||||
|
ModalContent, |
||||
|
notifications, |
||||
|
Body, |
||||
|
Layout, |
||||
|
Tabs, |
||||
|
Tab, |
||||
|
Input, |
||||
|
Heading, |
||||
|
TextArea, |
||||
|
Dropzone, |
||||
|
} from "@budibase/bbui" |
||||
|
import analytics, { Events } from "analytics" |
||||
|
import { datasources, queries } from "stores/backend" |
||||
|
import { writable } from "svelte/store" |
||||
|
|
||||
|
export let navigateDatasource = false |
||||
|
export let datasourceId |
||||
|
export let createDatasource = false |
||||
|
export let onCancel |
||||
|
|
||||
|
const data = writable({ |
||||
|
url: "", |
||||
|
raw: "", |
||||
|
file: undefined, |
||||
|
}) |
||||
|
|
||||
|
let lastTouched = "url" |
||||
|
|
||||
|
const getData = async () => { |
||||
|
let dataString |
||||
|
|
||||
|
// parse the file into memory and send as string |
||||
|
if (lastTouched === "file") { |
||||
|
dataString = await $data.file.text() |
||||
|
} else if (lastTouched === "url") { |
||||
|
const response = await fetch($data.url) |
||||
|
dataString = await response.text() |
||||
|
} else if (lastTouched === "raw") { |
||||
|
dataString = $data.raw |
||||
|
} |
||||
|
|
||||
|
return dataString |
||||
|
} |
||||
|
|
||||
|
async function importQueries() { |
||||
|
try { |
||||
|
const dataString = await getData() |
||||
|
|
||||
|
if (!datasourceId && !createDatasource) { |
||||
|
throw new Error("No datasource id") |
||||
|
} |
||||
|
|
||||
|
const body = { |
||||
|
data: dataString, |
||||
|
datasourceId, |
||||
|
} |
||||
|
|
||||
|
const importResult = await queries.import(body) |
||||
|
if (!datasourceId) { |
||||
|
datasourceId = importResult.datasourceId |
||||
|
} |
||||
|
|
||||
|
// reload |
||||
|
await datasources.fetch() |
||||
|
await queries.fetch() |
||||
|
await datasources.select(datasourceId) |
||||
|
|
||||
|
if (navigateDatasource) { |
||||
|
$goto(`./datasource/${datasourceId}`) |
||||
|
} |
||||
|
|
||||
|
notifications.success(`Imported successfully.`) |
||||
|
analytics.captureEvent(Events.QUERIES.REST.IMPORTED, { |
||||
|
importType: lastTouched, |
||||
|
newDatasource: createDatasource, |
||||
|
}) |
||||
|
|
||||
|
return true |
||||
|
} catch (err) { |
||||
|
notifications.error(`Error importing: ${err}`) |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<ModalContent |
||||
|
onConfirm={() => importQueries()} |
||||
|
{onCancel} |
||||
|
confirmText={"Import"} |
||||
|
cancelText="Back" |
||||
|
size="L" |
||||
|
> |
||||
|
<Layout noPadding> |
||||
|
<Heading size="S">Import</Heading> |
||||
|
<Body size="XS" |
||||
|
>Import your rest collection using one of the options below</Body |
||||
|
> |
||||
|
<Tabs selected="Link"> |
||||
|
<Tab title="Link"> |
||||
|
<Input |
||||
|
bind:value={$data.url} |
||||
|
on:change={() => (lastTouched = "url")} |
||||
|
label="Enter a URL" |
||||
|
placeholder="e.g. https://petstore.swagger.io/v2/swagger.json" |
||||
|
/> |
||||
|
</Tab> |
||||
|
<Tab title="File"> |
||||
|
<Dropzone |
||||
|
gallery={false} |
||||
|
value={$data.file ? [$data.file] : []} |
||||
|
on:change={e => { |
||||
|
$data.file = e.detail?.[0] |
||||
|
lastTouched = "file" |
||||
|
}} |
||||
|
fileTags={["OpenAPI 2.0", "Swagger 2.0", "cURL", "YAML", "JSON"]} |
||||
|
maximum={1} |
||||
|
/> |
||||
|
</Tab> |
||||
|
<Tab title="Raw Text"> |
||||
|
<TextArea |
||||
|
bind:value={$data.raw} |
||||
|
on:change={() => (lastTouched = "raw")} |
||||
|
label={"Paste raw text"} |
||||
|
placeholder={'e.g. curl --location --request GET "https://example.com"'} |
||||
|
/> |
||||
|
</Tab> |
||||
|
</Tabs> |
||||
|
</Layout> |
||||
|
</ModalContent> |
||||
|
|
||||
|
<style> |
||||
|
</style> |
||||
@ -0,0 +1,50 @@ |
|||||
|
/** |
||||
|
* Duplicates a name with respect to a collection of existing names |
||||
|
* e.g. |
||||
|
* name all names result |
||||
|
* ------ ----------- -------- |
||||
|
* ("foo") ["foo"] "foo (1)" |
||||
|
* ("foo") ["foo", "foo (1)"] "foo (2)" |
||||
|
* ("foo (1)") ["foo", "foo (1)"] "foo (2)" |
||||
|
* ("foo") ["foo", "foo (2)"] "foo (1)" |
||||
|
* |
||||
|
* Repl |
||||
|
*/ |
||||
|
export const duplicateName = (name, allNames) => { |
||||
|
const baseName = name.split(" (")[0] |
||||
|
const isDuplicate = new RegExp(`${baseName}\\s\\((\\d+)\\)$`) |
||||
|
|
||||
|
// get the sequence from matched names
|
||||
|
const sequence = [] |
||||
|
allNames.filter(n => { |
||||
|
if (n === baseName) { |
||||
|
return true |
||||
|
} |
||||
|
const match = n.match(isDuplicate) |
||||
|
if (match) { |
||||
|
sequence.push(parseInt(match[1])) |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
}) |
||||
|
sequence.sort((a, b) => a - b) |
||||
|
|
||||
|
// get the next number in the sequence
|
||||
|
let number |
||||
|
if (sequence.length === 0) { |
||||
|
number = 1 |
||||
|
} else { |
||||
|
// get the next number in the sequence
|
||||
|
for (let i = 0; i < sequence.length; i++) { |
||||
|
if (sequence[i] !== i + 1) { |
||||
|
number = i + 1 |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if (!number) { |
||||
|
number = sequence.length + 1 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return `${baseName} (${number})` |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
const { duplicateName } = require("../duplicate") |
||||
|
|
||||
|
describe("duplicate", () => { |
||||
|
|
||||
|
describe("duplicates a name ", () => { |
||||
|
it("with a single existing", async () => { |
||||
|
const names = ["foo"] |
||||
|
const name = "foo" |
||||
|
|
||||
|
const duplicate = duplicateName(name, names) |
||||
|
|
||||
|
expect(duplicate).toBe("foo (1)") |
||||
|
}) |
||||
|
|
||||
|
it("with multiple existing", async () => { |
||||
|
const names = ["foo", "foo (1)", "foo (2)"] |
||||
|
const name = "foo" |
||||
|
|
||||
|
const duplicate = duplicateName(name, names) |
||||
|
|
||||
|
expect(duplicate).toBe("foo (3)") |
||||
|
}) |
||||
|
|
||||
|
it("with mixed multiple existing", async () => { |
||||
|
const names = ["foo", "foo (1)", "foo (2)", "bar", "bar (1)", "bar (2)"] |
||||
|
const name = "foo" |
||||
|
|
||||
|
const duplicate = duplicateName(name, names) |
||||
|
|
||||
|
expect(duplicate).toBe("foo (3)") |
||||
|
}) |
||||
|
|
||||
|
it("with incomplete sequence", async () => { |
||||
|
const names = ["foo", "foo (2)", "foo (3)"] |
||||
|
const name = "foo" |
||||
|
|
||||
|
const duplicate = duplicateName(name, names) |
||||
|
|
||||
|
expect(duplicate).toBe("foo (1)") |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,85 @@ |
|||||
|
import CouchDB from "../../../../db" |
||||
|
import { queryValidation } from "../validation" |
||||
|
import { generateQueryID } from "../../../../db/utils" |
||||
|
import { ImportInfo, ImportSource } from "./sources/base" |
||||
|
import { OpenAPI2 } from "./sources/openapi2" |
||||
|
import { Query } from './../../../../definitions/common'; |
||||
|
import { Curl } from "./sources/curl" |
||||
|
interface ImportResult { |
||||
|
errorQueries: Query[] |
||||
|
queries: Query[] |
||||
|
} |
||||
|
|
||||
|
export class RestImporter { |
||||
|
data: string |
||||
|
sources: ImportSource[] |
||||
|
source!: ImportSource |
||||
|
|
||||
|
constructor(data: string) { |
||||
|
this.data = data |
||||
|
this.sources = [new OpenAPI2(), new Curl()] |
||||
|
} |
||||
|
|
||||
|
init = async () => { |
||||
|
for (let source of this.sources) { |
||||
|
if (await source.isSupported(this.data)) { |
||||
|
this.source = source |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getInfo = async (): Promise<ImportInfo> => { |
||||
|
return this.source.getInfo() |
||||
|
} |
||||
|
|
||||
|
importQueries = async ( |
||||
|
appId: string, |
||||
|
datasourceId: string |
||||
|
): Promise<ImportResult> => { |
||||
|
// constuct the queries
|
||||
|
let queries = await this.source.getQueries(datasourceId) |
||||
|
|
||||
|
// validate queries
|
||||
|
const errorQueries: Query[] = [] |
||||
|
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) |
||||
|
const response = await db.bulkDocs(queries) |
||||
|
|
||||
|
// create index to seperate queries and errors
|
||||
|
const queryIndex = queries.reduce((acc, query) => { |
||||
|
if (query._id) { |
||||
|
acc[query._id] = query |
||||
|
} |
||||
|
return acc |
||||
|
}, {} as { [key: string]: Query }) |
||||
|
|
||||
|
// check for failed writes
|
||||
|
response.forEach((query: any) => { |
||||
|
if (!query.ok) { |
||||
|
errorQueries.push(queryIndex[query.id]) |
||||
|
delete queryIndex[query.id] |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
return { |
||||
|
errorQueries, |
||||
|
queries: Object.values(queryIndex), |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
|
||||
|
import { Query, QueryParameter } from "../../../../../../definitions/common" |
||||
|
|
||||
|
export interface ImportInfo { |
||||
|
url: string |
||||
|
name: string |
||||
|
} |
||||
|
|
||||
|
enum MethodToVerb { |
||||
|
get = "read", |
||||
|
post = "create", |
||||
|
put = "update", |
||||
|
patch = "patch", |
||||
|
delete = "delete", |
||||
|
} |
||||
|
|
||||
|
export abstract class ImportSource { |
||||
|
abstract isSupported(data: string): Promise<boolean> |
||||
|
abstract getInfo(): Promise<ImportInfo> |
||||
|
abstract getQueries(datasourceId: string): Promise<Query[]> |
||||
|
|
||||
|
constructQuery = ( |
||||
|
datasourceId: string, |
||||
|
name: string, |
||||
|
method: string, |
||||
|
path: string, |
||||
|
queryString: string, |
||||
|
headers: object = {}, |
||||
|
parameters: QueryParameter[] = [], |
||||
|
body: object | undefined = undefined |
||||
|
): Query => { |
||||
|
const readable = true |
||||
|
const queryVerb = this.verbFromMethod(method) |
||||
|
const transformer = "return data" |
||||
|
const schema = {} |
||||
|
path = this.processPath(path) |
||||
|
queryString = this.processQuery(queryString) |
||||
|
const requestBody = JSON.stringify(body, null, 2) |
||||
|
|
||||
|
const query: Query = { |
||||
|
datasourceId, |
||||
|
name, |
||||
|
parameters, |
||||
|
fields: { |
||||
|
headers, |
||||
|
queryString, |
||||
|
path, |
||||
|
requestBody, |
||||
|
}, |
||||
|
transformer, |
||||
|
schema, |
||||
|
readable, |
||||
|
queryVerb, |
||||
|
} |
||||
|
|
||||
|
return query |
||||
|
} |
||||
|
|
||||
|
verbFromMethod = (method: string) => { |
||||
|
const verb = (<any>MethodToVerb)[method] |
||||
|
if (!verb) { |
||||
|
throw new Error(`Unsupported method: ${method}`) |
||||
|
} |
||||
|
return verb |
||||
|
} |
||||
|
|
||||
|
processPath = (path: string): string => { |
||||
|
if (path?.startsWith("/")) { |
||||
|
path = path.substring(1) |
||||
|
} |
||||
|
|
||||
|
// add extra braces around params for binding
|
||||
|
path = path.replace(/[{]/g, "{{") |
||||
|
path = path.replace(/[}]/g, "}}") |
||||
|
|
||||
|
return path |
||||
|
} |
||||
|
|
||||
|
processQuery = (queryString: string): string => { |
||||
|
if (queryString?.startsWith("?")) { |
||||
|
return queryString.substring(1) |
||||
|
} |
||||
|
|
||||
|
return queryString |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
import { ImportSource } from "." |
||||
|
import SwaggerParser from "@apidevtools/swagger-parser" |
||||
|
import { OpenAPI } from "openapi-types" |
||||
|
const yaml = require("js-yaml") |
||||
|
|
||||
|
export abstract class OpenAPISource extends ImportSource { |
||||
|
parseData = async (data: string): Promise<OpenAPI.Document> => { |
||||
|
let json: OpenAPI.Document |
||||
|
try { |
||||
|
json = JSON.parse(data) |
||||
|
} catch (jsonErr) { |
||||
|
// couldn't parse json
|
||||
|
// try to convert yaml -> json
|
||||
|
try { |
||||
|
json = yaml.load(data) |
||||
|
} catch (yamlErr) { |
||||
|
// couldn't parse yaml
|
||||
|
throw new Error("Could not parse JSON or YAML") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return SwaggerParser.validate(json, {}) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,94 @@ |
|||||
|
import { ImportSource, ImportInfo } from "./base" |
||||
|
import { Query } from "../../../../../definitions/common" |
||||
|
import { URL } from "url" |
||||
|
const curlconverter = require("curlconverter") |
||||
|
|
||||
|
const parseCurl = (data: string): any => { |
||||
|
const curlJson = curlconverter.toJsonString(data) |
||||
|
return JSON.parse(curlJson) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The curl converter parses the request body into the key field of an object |
||||
|
* e.g. --d '{"key":"val"}' produces an object { "{"key":"val"}" : "" } |
||||
|
* This is not what we want, so we need to parse out the key from the object |
||||
|
*/ |
||||
|
const parseBody = (curl: any) => { |
||||
|
if (curl.data) { |
||||
|
const keys = Object.keys(curl.data) |
||||
|
if (keys.length) { |
||||
|
const key = keys[0] |
||||
|
try { |
||||
|
return JSON.parse(key) |
||||
|
} catch (e) { |
||||
|
// do nothing
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return undefined |
||||
|
} |
||||
|
|
||||
|
const parseCookie = (curl: any) => { |
||||
|
if (curl.cookies){ |
||||
|
return Object.entries(curl.cookies).reduce((acc, entry) => { |
||||
|
const [key, value] = entry |
||||
|
return acc + `${key}=${value}; ` |
||||
|
}, "") |
||||
|
} |
||||
|
|
||||
|
return null |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Curl |
||||
|
* https://curl.se/docs/manpage.html
|
||||
|
*/ |
||||
|
export class Curl extends ImportSource { |
||||
|
curl: any |
||||
|
|
||||
|
isSupported = async (data: string): Promise<boolean> => { |
||||
|
try { |
||||
|
const curl = parseCurl(data) |
||||
|
this.curl = curl |
||||
|
} catch (err) { |
||||
|
return false |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
getInfo = async (): Promise<ImportInfo> => { |
||||
|
const url = new URL(this.curl.url) |
||||
|
return { |
||||
|
url: url.origin, |
||||
|
name: url.hostname, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getQueries = async (datasourceId: string): Promise<Query[]> => { |
||||
|
const url = new URL(this.curl.raw_url) |
||||
|
const name = url.pathname |
||||
|
const path = url.pathname |
||||
|
const method = this.curl.method |
||||
|
const queryString = url.search |
||||
|
const headers = this.curl.headers |
||||
|
const requestBody = parseBody(this.curl) |
||||
|
|
||||
|
const cookieHeader = parseCookie(this.curl) |
||||
|
if (cookieHeader) { |
||||
|
headers["Cookie"] = cookieHeader |
||||
|
} |
||||
|
|
||||
|
const query = this.constructQuery( |
||||
|
datasourceId, |
||||
|
name, |
||||
|
method, |
||||
|
path, |
||||
|
queryString, |
||||
|
headers, |
||||
|
[], |
||||
|
requestBody |
||||
|
) |
||||
|
|
||||
|
return [query] |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
import { ImportInfo } from "./base" |
||||
|
import { Query, QueryParameter } from "../../../../../definitions/common" |
||||
|
import { OpenAPIV2 } from "openapi-types" |
||||
|
import { OpenAPISource } from "./base/openapi" |
||||
|
|
||||
|
const parameterNotRef = ( |
||||
|
param: OpenAPIV2.Parameter | OpenAPIV2.ReferenceObject |
||||
|
): param is OpenAPIV2.Parameter => { |
||||
|
// all refs are deferenced by parser library
|
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
const isOpenAPI2 = (document: any): document is OpenAPIV2.Document => { |
||||
|
if (document.swagger === "2.0") { |
||||
|
return true |
||||
|
} else { |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const methods: string[] = Object.values(OpenAPIV2.HttpMethods) |
||||
|
|
||||
|
const isOperation = ( |
||||
|
key: string, |
||||
|
pathItem: any |
||||
|
): pathItem is OpenAPIV2.OperationObject => { |
||||
|
return methods.includes(key) |
||||
|
} |
||||
|
|
||||
|
const isParameter = ( |
||||
|
key: string, |
||||
|
pathItem: any |
||||
|
): pathItem is OpenAPIV2.Parameter => { |
||||
|
return !isOperation(key, pathItem) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 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<boolean> => { |
||||
|
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<ImportInfo> => { |
||||
|
const scheme = this.document.schemes?.includes("https") ? "https" : "http" |
||||
|
const basePath = this.document.basePath || "" |
||||
|
const host = this.document.host || "<host>" |
||||
|
const url = `${scheme}://${host}${basePath}` |
||||
|
const name = this.document.info.title || "Swagger Import" |
||||
|
|
||||
|
return { |
||||
|
url: url, |
||||
|
name: name, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getQueries = async (datasourceId: string): Promise<Query[]> => { |
||||
|
const queries = [] |
||||
|
|
||||
|
for (let [path, pathItem] of Object.entries(this.document.paths)) { |
||||
|
// parameters that apply to every operation in the path
|
||||
|
let pathParams: OpenAPIV2.Parameter[] = [] |
||||
|
|
||||
|
for (let [key, opOrParams] of Object.entries(pathItem)) { |
||||
|
if (isParameter(key, opOrParams)) { |
||||
|
const pathParameters = opOrParams as OpenAPIV2.Parameter[] |
||||
|
pathParams.push(...pathParameters) |
||||
|
continue |
||||
|
} |
||||
|
// can not be a parameter, must be an operation
|
||||
|
const operation = opOrParams as OpenAPIV2.OperationObject |
||||
|
|
||||
|
const methodName = key |
||||
|
const name = operation.operationId || path |
||||
|
let queryString = "" |
||||
|
const headers: any = {} |
||||
|
let requestBody = undefined |
||||
|
const parameters: QueryParameter[] = [] |
||||
|
|
||||
|
if (operation.consumes) { |
||||
|
headers["Content-Type"] = operation.consumes[0] |
||||
|
} |
||||
|
|
||||
|
// combine the path parameters with the operation parameters
|
||||
|
const operationParams = operation.parameters || [] |
||||
|
const allParams = [...pathParams, ...operationParams] |
||||
|
|
||||
|
for (let param of allParams) { |
||||
|
if (parameterNotRef(param)) { |
||||
|
switch (param.in) { |
||||
|
case "query": |
||||
|
let prefix = "" |
||||
|
if (queryString) { |
||||
|
prefix = "&" |
||||
|
} |
||||
|
queryString = `${queryString}${prefix}${param.name}={{${param.name}}}` |
||||
|
break |
||||
|
case "header": |
||||
|
headers[param.name] = `{{${param.name}}}` |
||||
|
break |
||||
|
case "path": |
||||
|
// do nothing: param is already in the path
|
||||
|
break |
||||
|
case "formData": |
||||
|
// future enhancement
|
||||
|
break |
||||
|
case "body": |
||||
|
// set the request body to the example provided
|
||||
|
// future enhancement: generate an example from the schema
|
||||
|
let bodyParam: OpenAPIV2.InBodyParameterObject = |
||||
|
param as OpenAPIV2.InBodyParameterObject |
||||
|
if (param.schema.example) { |
||||
|
const schema = bodyParam.schema as OpenAPIV2.SchemaObject |
||||
|
requestBody = schema.example |
||||
|
} |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
// add the parameter if it can be bound in our config
|
||||
|
if (["query", "header", "path"].includes(param.in)) { |
||||
|
parameters.push({ |
||||
|
name: param.name, |
||||
|
default: param.default || "", |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const query = this.constructQuery( |
||||
|
datasourceId, |
||||
|
name, |
||||
|
methodName, |
||||
|
path, |
||||
|
queryString, |
||||
|
headers, |
||||
|
parameters, |
||||
|
requestBody |
||||
|
) |
||||
|
queries.push(query) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return queries |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
const { Curl } = require("../../curl") |
||||
|
const fs = require("fs") |
||||
|
const path = require('path') |
||||
|
|
||||
|
const getData = (file) => { |
||||
|
return fs.readFileSync(path.join(__dirname, `./data/${file}.txt`), "utf8") |
||||
|
} |
||||
|
|
||||
|
describe("Curl Import", () => { |
||||
|
let curl |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
curl = new Curl() |
||||
|
}) |
||||
|
|
||||
|
it("validates unsupported data", async () => { |
||||
|
let data |
||||
|
let supported |
||||
|
|
||||
|
// JSON
|
||||
|
data = "{}" |
||||
|
supported = await curl.isSupported(data) |
||||
|
expect(supported).toBe(false) |
||||
|
|
||||
|
// Empty
|
||||
|
data = "" |
||||
|
supported = await curl.isSupported(data) |
||||
|
expect(supported).toBe(false) |
||||
|
}) |
||||
|
|
||||
|
const init = async (file) => { |
||||
|
await curl.isSupported(getData(file)) |
||||
|
} |
||||
|
|
||||
|
it("returns import info", async () => { |
||||
|
await init("get") |
||||
|
const info = await curl.getInfo() |
||||
|
expect(info.url).toBe("http://example.com") |
||||
|
expect(info.name).toBe("example.com") |
||||
|
}) |
||||
|
|
||||
|
describe("Returns queries", () => { |
||||
|
|
||||
|
const getQueries = async (file) => { |
||||
|
await init(file) |
||||
|
const queries = await curl.getQueries() |
||||
|
expect(queries.length).toBe(1) |
||||
|
return queries |
||||
|
} |
||||
|
|
||||
|
const testVerb = async (file, verb) => { |
||||
|
const queries = await getQueries(file) |
||||
|
expect(queries[0].queryVerb).toBe(verb) |
||||
|
} |
||||
|
|
||||
|
it("populates verb", async () => { |
||||
|
await testVerb("get", "read") |
||||
|
await testVerb("post", "create") |
||||
|
await testVerb("put", "update") |
||||
|
await testVerb("delete", "delete") |
||||
|
await testVerb("patch", "patch") |
||||
|
}) |
||||
|
|
||||
|
const testPath = async (file, urlPath) => { |
||||
|
const queries = await getQueries(file) |
||||
|
expect(queries[0].fields.path).toBe(urlPath) |
||||
|
} |
||||
|
|
||||
|
it("populates path", async () => { |
||||
|
await testPath("get", "") |
||||
|
await testPath("path", "paths/abc") |
||||
|
}) |
||||
|
|
||||
|
const testHeaders = async (file, headers) => { |
||||
|
const queries = await getQueries(file) |
||||
|
expect(queries[0].fields.headers).toStrictEqual(headers) |
||||
|
} |
||||
|
|
||||
|
it("populates headers", async () => { |
||||
|
await testHeaders("get", {}) |
||||
|
await testHeaders("headers", { "x-bb-header-1" : "123", "x-bb-header-2" : "456"} ) |
||||
|
}) |
||||
|
|
||||
|
const testQuery = async (file, queryString) => { |
||||
|
const queries = await getQueries(file) |
||||
|
expect(queries[0].fields.queryString).toBe(queryString) |
||||
|
} |
||||
|
it("populates query", async () => { |
||||
|
await testQuery("get", "") |
||||
|
await testQuery("query", "q1=v1&q1=v2") |
||||
|
}) |
||||
|
|
||||
|
const testBody = async (file, body) => { |
||||
|
const queries = await getQueries(file) |
||||
|
expect(queries[0].fields.requestBody).toStrictEqual(JSON.stringify(body, null, 2)) |
||||
|
} |
||||
|
|
||||
|
it("populates body", async () => { |
||||
|
await testBody("get", undefined) |
||||
|
await testBody("post", { "key" : "val" }) |
||||
|
await testBody("empty-body", {}) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1 @@ |
|||||
|
curl -X DELETE 'http://example.com' |
||||
@ -0,0 +1,2 @@ |
|||||
|
curl -X POST 'http://example.com' \ |
||||
|
--data-raw '{}' |
||||
@ -0,0 +1 @@ |
|||||
|
curl 'http://example.com' |
||||
@ -0,0 +1,3 @@ |
|||||
|
curl 'http://example.com' \ |
||||
|
-H 'x-bb-header-1: 123' \ |
||||
|
-H 'x-bb-header-2: 456' |
||||
@ -0,0 +1,2 @@ |
|||||
|
curl -X PATCH 'http://example.com/paths/abc' \ |
||||
|
--data-raw '{ "key" : "val" }' |
||||
@ -0,0 +1 @@ |
|||||
|
curl 'http://example.com/paths/abc' |
||||
@ -0,0 +1,2 @@ |
|||||
|
curl -X POST 'http://example.com' \ |
||||
|
--data-raw '{ "key" : "val" }' |
||||
@ -0,0 +1,2 @@ |
|||||
|
curl -X PUT 'http://example.com/paths/abc' \ |
||||
|
--data-raw '{ "key" : "val" }' |
||||
@ -0,0 +1 @@ |
|||||
|
curl 'http://example.com/paths/abc?q1=v1&q1=v2' |
||||
@ -0,0 +1,253 @@ |
|||||
|
{ |
||||
|
"swagger": "2.0", |
||||
|
"info": { |
||||
|
"description": "A basic swagger file", |
||||
|
"version": "1.0.0", |
||||
|
"title": "CRUD" |
||||
|
}, |
||||
|
"host": "example.com", |
||||
|
"tags": [ |
||||
|
{ |
||||
|
"name": "entity" |
||||
|
} |
||||
|
], |
||||
|
"schemes": [ |
||||
|
"http" |
||||
|
], |
||||
|
"paths": { |
||||
|
"/entities": { |
||||
|
"post": { |
||||
|
"tags": [ |
||||
|
"entity" |
||||
|
], |
||||
|
"operationId": "createEntity", |
||||
|
"consumes": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"produces": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"parameters": [ |
||||
|
{ |
||||
|
"$ref": "#/parameters/CreateEntity" |
||||
|
} |
||||
|
], |
||||
|
"responses": { |
||||
|
"200": { |
||||
|
"description": "successful operation", |
||||
|
"schema": { |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"get": { |
||||
|
"tags": [ |
||||
|
"entity" |
||||
|
], |
||||
|
"operationId": "getEntities", |
||||
|
"produces": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"parameters": [ |
||||
|
{ |
||||
|
"$ref": "#/parameters/Page" |
||||
|
}, |
||||
|
{ |
||||
|
"$ref": "#/parameters/Size" |
||||
|
} |
||||
|
], |
||||
|
"responses": { |
||||
|
"200": { |
||||
|
"description": "successful operation", |
||||
|
"schema": { |
||||
|
"$ref": "#/definitions/Entities" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"/entities/{entityId}": { |
||||
|
"parameters": [ |
||||
|
{ |
||||
|
"$ref": "#/parameters/EntityId" |
||||
|
} |
||||
|
], |
||||
|
"get": { |
||||
|
"tags": [ |
||||
|
"entity" |
||||
|
], |
||||
|
"operationId": "getEntity", |
||||
|
"produces": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"responses": { |
||||
|
"200": { |
||||
|
"description": "successful operation", |
||||
|
"schema": { |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"put": { |
||||
|
"tags": [ |
||||
|
"entity" |
||||
|
], |
||||
|
"operationId": "updateEntity", |
||||
|
"consumes": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"produces": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"parameters": [ |
||||
|
{ |
||||
|
"$ref": "#/parameters/Entity" |
||||
|
} |
||||
|
], |
||||
|
"responses": { |
||||
|
"200": { |
||||
|
"description": "successful operation", |
||||
|
"schema": { |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"patch": { |
||||
|
"tags": [ |
||||
|
"entity" |
||||
|
], |
||||
|
"operationId": "patchEntity", |
||||
|
"consumes": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"produces": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"parameters": [ |
||||
|
{ |
||||
|
"$ref": "#/parameters/Entity" |
||||
|
} |
||||
|
], |
||||
|
"responses": { |
||||
|
"200": { |
||||
|
"description": "successful operation", |
||||
|
"schema": { |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"delete": { |
||||
|
"tags": [ |
||||
|
"entity" |
||||
|
], |
||||
|
"parameters": [ |
||||
|
{ |
||||
|
"$ref": "#/parameters/APIKey" |
||||
|
} |
||||
|
], |
||||
|
"operationId": "deleteEntity", |
||||
|
"produces": [ |
||||
|
"application/json" |
||||
|
], |
||||
|
"responses": { |
||||
|
"204": { |
||||
|
"description": "successful operation" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"parameters": { |
||||
|
"EntityId": { |
||||
|
"type": "integer", |
||||
|
"format": "int64", |
||||
|
"name": "entityId", |
||||
|
"in": "path", |
||||
|
"required": true |
||||
|
}, |
||||
|
"CreateEntity": { |
||||
|
"name": "entity", |
||||
|
"in": "body", |
||||
|
"required": true, |
||||
|
"schema": { |
||||
|
"$ref": "#/definitions/CreateEntity" |
||||
|
} |
||||
|
}, |
||||
|
"Entity": { |
||||
|
"name": "entity", |
||||
|
"in": "body", |
||||
|
"required": true, |
||||
|
"schema": { |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
} |
||||
|
}, |
||||
|
"Page": { |
||||
|
"type": "integer", |
||||
|
"format": "int32", |
||||
|
"name": "page", |
||||
|
"in": "query", |
||||
|
"required": false |
||||
|
}, |
||||
|
"Size": { |
||||
|
"type": "integer", |
||||
|
"format": "int32", |
||||
|
"name": "size", |
||||
|
"in": "query", |
||||
|
"required": false |
||||
|
}, |
||||
|
"APIKey": { |
||||
|
"type": "string", |
||||
|
"name": "x-api-key", |
||||
|
"in": "header", |
||||
|
"required": false |
||||
|
} |
||||
|
}, |
||||
|
"definitions": { |
||||
|
"CreateEntity": { |
||||
|
"type": "object", |
||||
|
"properties": { |
||||
|
"name": { |
||||
|
"type": "string" |
||||
|
}, |
||||
|
"type": { |
||||
|
"type": "string" |
||||
|
} |
||||
|
}, |
||||
|
"example": { |
||||
|
"name": "name", |
||||
|
"type": "type" |
||||
|
} |
||||
|
}, |
||||
|
"Entity": { |
||||
|
"allOf": [ |
||||
|
{ |
||||
|
"type": "object", |
||||
|
"properties": { |
||||
|
"id": { |
||||
|
"type": "integer", |
||||
|
"format": "int64" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"$ref": "#/definitions/CreateEntity" |
||||
|
} |
||||
|
], |
||||
|
"example": { |
||||
|
"id": 1, |
||||
|
"name": "name", |
||||
|
"type": "type" |
||||
|
} |
||||
|
}, |
||||
|
"Entities" : { |
||||
|
"type": "array", |
||||
|
"items": { |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,160 @@ |
|||||
|
--- |
||||
|
swagger: "2.0" |
||||
|
info: |
||||
|
description: A basic swagger file |
||||
|
version: 1.0.0 |
||||
|
title: CRUD |
||||
|
host: example.com |
||||
|
tags: |
||||
|
- name: entity |
||||
|
schemes: |
||||
|
- http |
||||
|
paths: |
||||
|
"/entities": |
||||
|
post: |
||||
|
tags: |
||||
|
- entity |
||||
|
operationId: createEntity |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
parameters: |
||||
|
- "$ref": "#/parameters/CreateEntity" |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
get: |
||||
|
tags: |
||||
|
- entity |
||||
|
operationId: getEntities |
||||
|
produces: |
||||
|
- application/json |
||||
|
parameters: |
||||
|
- "$ref": "#/parameters/Page" |
||||
|
- "$ref": "#/parameters/Size" |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
"$ref": "#/definitions/Entities" |
||||
|
"/entities/{entityId}": |
||||
|
parameters: |
||||
|
- "$ref": "#/parameters/EntityId" |
||||
|
get: |
||||
|
tags: |
||||
|
- entity |
||||
|
operationId: getEntity |
||||
|
produces: |
||||
|
- application/json |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
put: |
||||
|
tags: |
||||
|
- entity |
||||
|
operationId: updateEntity |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
parameters: |
||||
|
- "$ref": "#/parameters/Entity" |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
patch: |
||||
|
tags: |
||||
|
- entity |
||||
|
operationId: patchEntity |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
parameters: |
||||
|
- "$ref": "#/parameters/Entity" |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
delete: |
||||
|
tags: |
||||
|
- entity |
||||
|
parameters: |
||||
|
- "$ref": "#/parameters/APIKey" |
||||
|
operationId: deleteEntity |
||||
|
produces: |
||||
|
- application/json |
||||
|
responses: |
||||
|
"204": |
||||
|
description: successful operation |
||||
|
parameters: |
||||
|
EntityId: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
name: entityId |
||||
|
in: path |
||||
|
required: true |
||||
|
CreateEntity: |
||||
|
name: entity |
||||
|
in: body |
||||
|
required: true |
||||
|
schema: |
||||
|
"$ref": "#/definitions/CreateEntity" |
||||
|
Entity: |
||||
|
name: entity |
||||
|
in: body |
||||
|
required: true |
||||
|
schema: |
||||
|
"$ref": "#/definitions/Entity" |
||||
|
Page: |
||||
|
type: integer |
||||
|
format: int32 |
||||
|
name: page |
||||
|
in: query |
||||
|
required: false |
||||
|
Size: |
||||
|
type: integer |
||||
|
format: int32 |
||||
|
name: size |
||||
|
in: query |
||||
|
required: false |
||||
|
APIKey: |
||||
|
type: string |
||||
|
name: x-api-key |
||||
|
in: header |
||||
|
required: false |
||||
|
definitions: |
||||
|
CreateEntity: |
||||
|
type: object |
||||
|
properties: |
||||
|
name: |
||||
|
type: string |
||||
|
type: |
||||
|
type: string |
||||
|
example: |
||||
|
name: name |
||||
|
type: type |
||||
|
Entity: |
||||
|
allOf: |
||||
|
- type: object |
||||
|
properties: |
||||
|
id: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
- "$ref": "#/definitions/CreateEntity" |
||||
|
example: |
||||
|
id: 1 |
||||
|
name: name |
||||
|
type: type |
||||
|
Entities: |
||||
|
type: array |
||||
|
items: |
||||
|
"$ref": "#/definitions/Entity" |
||||
File diff suppressed because it is too large
@ -0,0 +1,711 @@ |
|||||
|
swagger: "2.0" |
||||
|
info: |
||||
|
description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." |
||||
|
version: 1.0.5 |
||||
|
title: Swagger Petstore |
||||
|
termsOfService: http://swagger.io/terms/ |
||||
|
contact: |
||||
|
email: apiteam@swagger.io |
||||
|
license: |
||||
|
name: Apache 2.0 |
||||
|
url: http://www.apache.org/licenses/LICENSE-2.0.html |
||||
|
host: petstore.swagger.io |
||||
|
basePath: /v2 |
||||
|
tags: |
||||
|
- name: pet |
||||
|
description: Everything about your Pets |
||||
|
externalDocs: |
||||
|
description: Find out more |
||||
|
url: http://swagger.io |
||||
|
- name: store |
||||
|
description: Access to Petstore orders |
||||
|
- name: user |
||||
|
description: Operations about user |
||||
|
externalDocs: |
||||
|
description: Find out more about our store |
||||
|
url: http://swagger.io |
||||
|
schemes: |
||||
|
- https |
||||
|
- http |
||||
|
paths: |
||||
|
/pet/{petId}/uploadImage: |
||||
|
post: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: uploads an image |
||||
|
description: "" |
||||
|
operationId: uploadFile |
||||
|
consumes: |
||||
|
- multipart/form-data |
||||
|
produces: |
||||
|
- application/json |
||||
|
parameters: |
||||
|
- name: petId |
||||
|
in: path |
||||
|
description: ID of pet to update |
||||
|
required: true |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
- name: additionalMetadata |
||||
|
in: formData |
||||
|
description: Additional data to pass to server |
||||
|
required: false |
||||
|
type: string |
||||
|
- name: file |
||||
|
in: formData |
||||
|
description: file to upload |
||||
|
required: false |
||||
|
type: file |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
$ref: "#/definitions/ApiResponse" |
||||
|
security: |
||||
|
- petstore_auth: |
||||
|
- write:pets |
||||
|
- read:pets |
||||
|
/pet: |
||||
|
post: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: Add a new pet to the store |
||||
|
description: "" |
||||
|
operationId: addPet |
||||
|
consumes: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- in: body |
||||
|
name: body |
||||
|
description: Pet object that needs to be added to the store |
||||
|
required: true |
||||
|
schema: |
||||
|
$ref: "#/definitions/Pet" |
||||
|
responses: |
||||
|
"405": |
||||
|
description: Invalid input |
||||
|
security: |
||||
|
- petstore_auth: |
||||
|
- write:pets |
||||
|
- read:pets |
||||
|
put: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: Update an existing pet |
||||
|
description: "" |
||||
|
operationId: updatePet |
||||
|
consumes: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- in: body |
||||
|
name: body |
||||
|
description: Pet object that needs to be added to the store |
||||
|
required: true |
||||
|
schema: |
||||
|
$ref: "#/definitions/Pet" |
||||
|
responses: |
||||
|
"400": |
||||
|
description: Invalid ID supplied |
||||
|
"404": |
||||
|
description: Pet not found |
||||
|
"405": |
||||
|
description: Validation exception |
||||
|
security: |
||||
|
- petstore_auth: |
||||
|
- write:pets |
||||
|
- read:pets |
||||
|
/pet/findByStatus: |
||||
|
get: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: Finds Pets by status |
||||
|
description: Multiple status values can be provided with comma separated strings |
||||
|
operationId: findPetsByStatus |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: status |
||||
|
in: query |
||||
|
description: Status values that need to be considered for filter |
||||
|
required: true |
||||
|
type: array |
||||
|
items: |
||||
|
type: string |
||||
|
enum: |
||||
|
- available |
||||
|
- pending |
||||
|
- sold |
||||
|
default: available |
||||
|
collectionFormat: multi |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
type: array |
||||
|
items: |
||||
|
$ref: "#/definitions/Pet" |
||||
|
"400": |
||||
|
description: Invalid status value |
||||
|
security: |
||||
|
- petstore_auth: |
||||
|
- write:pets |
||||
|
- read:pets |
||||
|
/pet/findByTags: |
||||
|
get: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: Finds Pets by tags |
||||
|
description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. |
||||
|
operationId: findPetsByTags |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: tags |
||||
|
in: query |
||||
|
description: Tags to filter by |
||||
|
required: true |
||||
|
type: array |
||||
|
items: |
||||
|
type: string |
||||
|
collectionFormat: multi |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
type: array |
||||
|
items: |
||||
|
$ref: "#/definitions/Pet" |
||||
|
"400": |
||||
|
description: Invalid tag value |
||||
|
security: |
||||
|
- petstore_auth: |
||||
|
- write:pets |
||||
|
- read:pets |
||||
|
deprecated: true |
||||
|
/pet/{petId}: |
||||
|
get: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: Find pet by ID |
||||
|
description: Returns a single pet |
||||
|
operationId: getPetById |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: petId |
||||
|
in: path |
||||
|
description: ID of pet to return |
||||
|
required: true |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
$ref: "#/definitions/Pet" |
||||
|
"400": |
||||
|
description: Invalid ID supplied |
||||
|
"404": |
||||
|
description: Pet not found |
||||
|
security: |
||||
|
- api_key: [] |
||||
|
post: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: Updates a pet in the store with form data |
||||
|
description: "" |
||||
|
operationId: updatePetWithForm |
||||
|
consumes: |
||||
|
- application/x-www-form-urlencoded |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: petId |
||||
|
in: path |
||||
|
description: ID of pet that needs to be updated |
||||
|
required: true |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
- name: name |
||||
|
in: formData |
||||
|
description: Updated name of the pet |
||||
|
required: false |
||||
|
type: string |
||||
|
- name: status |
||||
|
in: formData |
||||
|
description: Updated status of the pet |
||||
|
required: false |
||||
|
type: string |
||||
|
responses: |
||||
|
"405": |
||||
|
description: Invalid input |
||||
|
security: |
||||
|
- petstore_auth: |
||||
|
- write:pets |
||||
|
- read:pets |
||||
|
delete: |
||||
|
tags: |
||||
|
- pet |
||||
|
summary: Deletes a pet |
||||
|
description: "" |
||||
|
operationId: deletePet |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: api_key |
||||
|
in: header |
||||
|
required: false |
||||
|
type: string |
||||
|
- name: petId |
||||
|
in: path |
||||
|
description: Pet id to delete |
||||
|
required: true |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
responses: |
||||
|
"400": |
||||
|
description: Invalid ID supplied |
||||
|
"404": |
||||
|
description: Pet not found |
||||
|
security: |
||||
|
- petstore_auth: |
||||
|
- write:pets |
||||
|
- read:pets |
||||
|
/store/inventory: |
||||
|
get: |
||||
|
tags: |
||||
|
- store |
||||
|
summary: Returns pet inventories by status |
||||
|
description: Returns a map of status codes to quantities |
||||
|
operationId: getInventory |
||||
|
produces: |
||||
|
- application/json |
||||
|
parameters: [] |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
type: object |
||||
|
additionalProperties: |
||||
|
type: integer |
||||
|
format: int32 |
||||
|
security: |
||||
|
- api_key: [] |
||||
|
/store/order: |
||||
|
post: |
||||
|
tags: |
||||
|
- store |
||||
|
summary: Place an order for a pet |
||||
|
description: "" |
||||
|
operationId: placeOrder |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- in: body |
||||
|
name: body |
||||
|
description: order placed for purchasing the pet |
||||
|
required: true |
||||
|
schema: |
||||
|
$ref: "#/definitions/Order" |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
$ref: "#/definitions/Order" |
||||
|
"400": |
||||
|
description: Invalid Order |
||||
|
/store/order/{orderId}: |
||||
|
get: |
||||
|
tags: |
||||
|
- store |
||||
|
summary: Find purchase order by ID |
||||
|
description: For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions |
||||
|
operationId: getOrderById |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: orderId |
||||
|
in: path |
||||
|
description: ID of pet that needs to be fetched |
||||
|
required: true |
||||
|
type: integer |
||||
|
maximum: 10 |
||||
|
minimum: 1 |
||||
|
format: int64 |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
$ref: "#/definitions/Order" |
||||
|
"400": |
||||
|
description: Invalid ID supplied |
||||
|
"404": |
||||
|
description: Order not found |
||||
|
delete: |
||||
|
tags: |
||||
|
- store |
||||
|
summary: Delete purchase order by ID |
||||
|
description: For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors |
||||
|
operationId: deleteOrder |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: orderId |
||||
|
in: path |
||||
|
description: ID of the order that needs to be deleted |
||||
|
required: true |
||||
|
type: integer |
||||
|
minimum: 1 |
||||
|
format: int64 |
||||
|
responses: |
||||
|
"400": |
||||
|
description: Invalid ID supplied |
||||
|
"404": |
||||
|
description: Order not found |
||||
|
/user/createWithList: |
||||
|
post: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Creates list of users with given input array |
||||
|
description: "" |
||||
|
operationId: createUsersWithListInput |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- in: body |
||||
|
name: body |
||||
|
description: List of user object |
||||
|
required: true |
||||
|
schema: |
||||
|
type: array |
||||
|
items: |
||||
|
$ref: "#/definitions/User" |
||||
|
responses: |
||||
|
default: |
||||
|
description: successful operation |
||||
|
/user/{username}: |
||||
|
get: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Get user by user name |
||||
|
description: "" |
||||
|
operationId: getUserByName |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: username |
||||
|
in: path |
||||
|
description: "The name that needs to be fetched. Use user1 for testing. " |
||||
|
required: true |
||||
|
type: string |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
schema: |
||||
|
$ref: "#/definitions/User" |
||||
|
"400": |
||||
|
description: Invalid username supplied |
||||
|
"404": |
||||
|
description: User not found |
||||
|
put: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Updated user |
||||
|
description: This can only be done by the logged in user. |
||||
|
operationId: updateUser |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: username |
||||
|
in: path |
||||
|
description: name that need to be updated |
||||
|
required: true |
||||
|
type: string |
||||
|
- in: body |
||||
|
name: body |
||||
|
description: Updated user object |
||||
|
required: true |
||||
|
schema: |
||||
|
$ref: "#/definitions/User" |
||||
|
responses: |
||||
|
"400": |
||||
|
description: Invalid user supplied |
||||
|
"404": |
||||
|
description: User not found |
||||
|
delete: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Delete user |
||||
|
description: This can only be done by the logged in user. |
||||
|
operationId: deleteUser |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: username |
||||
|
in: path |
||||
|
description: The name that needs to be deleted |
||||
|
required: true |
||||
|
type: string |
||||
|
responses: |
||||
|
"400": |
||||
|
description: Invalid username supplied |
||||
|
"404": |
||||
|
description: User not found |
||||
|
/user/login: |
||||
|
get: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Logs user into the system |
||||
|
description: "" |
||||
|
operationId: loginUser |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- name: username |
||||
|
in: query |
||||
|
description: The user name for login |
||||
|
required: true |
||||
|
type: string |
||||
|
- name: password |
||||
|
in: query |
||||
|
description: The password for login in clear text |
||||
|
required: true |
||||
|
type: string |
||||
|
responses: |
||||
|
"200": |
||||
|
description: successful operation |
||||
|
headers: |
||||
|
X-Expires-After: |
||||
|
type: string |
||||
|
format: date-time |
||||
|
description: date in UTC when token expires |
||||
|
X-Rate-Limit: |
||||
|
type: integer |
||||
|
format: int32 |
||||
|
description: calls per hour allowed by the user |
||||
|
schema: |
||||
|
type: string |
||||
|
"400": |
||||
|
description: Invalid username/password supplied |
||||
|
/user/logout: |
||||
|
get: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Logs out current logged in user session |
||||
|
description: "" |
||||
|
operationId: logoutUser |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: [] |
||||
|
responses: |
||||
|
default: |
||||
|
description: successful operation |
||||
|
/user/createWithArray: |
||||
|
post: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Creates list of users with given input array |
||||
|
description: "" |
||||
|
operationId: createUsersWithArrayInput |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- in: body |
||||
|
name: body |
||||
|
description: List of user object |
||||
|
required: true |
||||
|
schema: |
||||
|
type: array |
||||
|
items: |
||||
|
$ref: "#/definitions/User" |
||||
|
responses: |
||||
|
default: |
||||
|
description: successful operation |
||||
|
/user: |
||||
|
post: |
||||
|
tags: |
||||
|
- user |
||||
|
summary: Create user |
||||
|
description: This can only be done by the logged in user. |
||||
|
operationId: createUser |
||||
|
consumes: |
||||
|
- application/json |
||||
|
produces: |
||||
|
- application/json |
||||
|
- application/xml |
||||
|
parameters: |
||||
|
- in: body |
||||
|
name: body |
||||
|
description: Created user object |
||||
|
required: true |
||||
|
schema: |
||||
|
$ref: "#/definitions/User" |
||||
|
responses: |
||||
|
default: |
||||
|
description: successful operation |
||||
|
securityDefinitions: |
||||
|
api_key: |
||||
|
type: apiKey |
||||
|
name: api_key |
||||
|
in: header |
||||
|
petstore_auth: |
||||
|
type: oauth2 |
||||
|
authorizationUrl: https://petstore.swagger.io/oauth/authorize |
||||
|
flow: implicit |
||||
|
scopes: |
||||
|
read:pets: read your pets |
||||
|
write:pets: modify pets in your account |
||||
|
definitions: |
||||
|
ApiResponse: |
||||
|
type: object |
||||
|
properties: |
||||
|
code: |
||||
|
type: integer |
||||
|
format: int32 |
||||
|
type: |
||||
|
type: string |
||||
|
message: |
||||
|
type: string |
||||
|
Category: |
||||
|
type: object |
||||
|
properties: |
||||
|
id: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
name: |
||||
|
type: string |
||||
|
xml: |
||||
|
name: Category |
||||
|
Pet: |
||||
|
type: object |
||||
|
required: |
||||
|
- name |
||||
|
- photoUrls |
||||
|
properties: |
||||
|
id: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
category: |
||||
|
$ref: "#/definitions/Category" |
||||
|
name: |
||||
|
type: string |
||||
|
example: doggie |
||||
|
photoUrls: |
||||
|
type: array |
||||
|
xml: |
||||
|
wrapped: true |
||||
|
items: |
||||
|
type: string |
||||
|
xml: |
||||
|
name: photoUrl |
||||
|
tags: |
||||
|
type: array |
||||
|
xml: |
||||
|
wrapped: true |
||||
|
items: |
||||
|
xml: |
||||
|
name: tag |
||||
|
$ref: "#/definitions/Tag" |
||||
|
status: |
||||
|
type: string |
||||
|
description: pet status in the store |
||||
|
enum: |
||||
|
- available |
||||
|
- pending |
||||
|
- sold |
||||
|
xml: |
||||
|
name: Pet |
||||
|
Tag: |
||||
|
type: object |
||||
|
properties: |
||||
|
id: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
name: |
||||
|
type: string |
||||
|
xml: |
||||
|
name: Tag |
||||
|
Order: |
||||
|
type: object |
||||
|
properties: |
||||
|
id: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
petId: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
quantity: |
||||
|
type: integer |
||||
|
format: int32 |
||||
|
shipDate: |
||||
|
type: string |
||||
|
format: date-time |
||||
|
status: |
||||
|
type: string |
||||
|
description: Order Status |
||||
|
enum: |
||||
|
- placed |
||||
|
- approved |
||||
|
- delivered |
||||
|
complete: |
||||
|
type: boolean |
||||
|
xml: |
||||
|
name: Order |
||||
|
User: |
||||
|
type: object |
||||
|
properties: |
||||
|
id: |
||||
|
type: integer |
||||
|
format: int64 |
||||
|
username: |
||||
|
type: string |
||||
|
firstName: |
||||
|
type: string |
||||
|
lastName: |
||||
|
type: string |
||||
|
email: |
||||
|
type: string |
||||
|
password: |
||||
|
type: string |
||||
|
phone: |
||||
|
type: string |
||||
|
userStatus: |
||||
|
type: integer |
||||
|
format: int32 |
||||
|
description: User Status |
||||
|
xml: |
||||
|
name: User |
||||
|
externalDocs: |
||||
|
description: Find out more about Swagger |
||||
|
url: http://swagger.io |
||||
@ -0,0 +1,239 @@ |
|||||
|
const { OpenAPI2 } = require("../../openapi2") |
||||
|
const fs = require("fs") |
||||
|
const path = require('path') |
||||
|
|
||||
|
const getData = (file, extension) => { |
||||
|
return fs.readFileSync(path.join(__dirname, `./data/${file}/${file}.${extension}`), "utf8") |
||||
|
} |
||||
|
|
||||
|
describe("OpenAPI2 Import", () => { |
||||
|
let openapi2 |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
openapi2 = new OpenAPI2() |
||||
|
}) |
||||
|
|
||||
|
it("validates unsupported data", async () => { |
||||
|
let data |
||||
|
let supported |
||||
|
|
||||
|
// non json / yaml
|
||||
|
data = "curl http://example.com" |
||||
|
supported = await openapi2.isSupported(data) |
||||
|
expect(supported).toBe(false) |
||||
|
|
||||
|
// Empty
|
||||
|
data = "" |
||||
|
supported = await openapi2.isSupported(data) |
||||
|
expect(supported).toBe(false) |
||||
|
}) |
||||
|
|
||||
|
const init = async (file, extension) => { |
||||
|
await openapi2.isSupported(getData(file, extension)) |
||||
|
} |
||||
|
|
||||
|
const runTests = async (filename, test, assertions) => { |
||||
|
for (let extension of ["json", "yaml"]) { |
||||
|
await test(filename, extension, assertions) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const testImportInfo = async (file, extension) => { |
||||
|
await init(file, extension) |
||||
|
const info = await openapi2.getInfo() |
||||
|
expect(info.url).toBe("https://petstore.swagger.io/v2") |
||||
|
expect(info.name).toBe("Swagger Petstore") |
||||
|
} |
||||
|
|
||||
|
it("returns import info", async () => { |
||||
|
await runTests("petstore", testImportInfo) |
||||
|
}) |
||||
|
|
||||
|
describe("Returns queries", () => { |
||||
|
const indexQueries = (queries) => { |
||||
|
return queries.reduce((acc, query) => { |
||||
|
acc[query.name] = query |
||||
|
return acc |
||||
|
}, {}) |
||||
|
} |
||||
|
|
||||
|
const getQueries = async (file, extension) => { |
||||
|
await init(file, extension) |
||||
|
const queries = await openapi2.getQueries() |
||||
|
expect(queries.length).toBe(6) |
||||
|
return indexQueries(queries) |
||||
|
} |
||||
|
|
||||
|
const testVerb = async (file, extension, assertions) => { |
||||
|
const queries = await getQueries(file, extension) |
||||
|
for (let [operationId, method] of Object.entries(assertions)) { |
||||
|
expect(queries[operationId].queryVerb).toBe(method) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
it("populates verb", async () => { |
||||
|
const assertions = { |
||||
|
"createEntity" : "create", |
||||
|
"getEntities" : "read", |
||||
|
"getEntity" : "read", |
||||
|
"updateEntity" : "update", |
||||
|
"patchEntity" : "patch", |
||||
|
"deleteEntity" : "delete" |
||||
|
} |
||||
|
await runTests("crud", testVerb, assertions) |
||||
|
}) |
||||
|
|
||||
|
const testPath = async (file, extension, assertions) => { |
||||
|
const queries = await getQueries(file, extension) |
||||
|
for (let [operationId, urlPath] of Object.entries(assertions)) { |
||||
|
expect(queries[operationId].fields.path).toBe(urlPath) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
it("populates path", async () => { |
||||
|
const assertions = { |
||||
|
"createEntity" : "entities", |
||||
|
"getEntities" : "entities", |
||||
|
"getEntity" : "entities/{{entityId}}", |
||||
|
"updateEntity" : "entities/{{entityId}}", |
||||
|
"patchEntity" : "entities/{{entityId}}", |
||||
|
"deleteEntity" : "entities/{{entityId}}" |
||||
|
} |
||||
|
await runTests("crud", testPath, assertions) |
||||
|
}) |
||||
|
|
||||
|
const testHeaders = async (file, extension, assertions) => { |
||||
|
const queries = await getQueries(file, extension) |
||||
|
for (let [operationId, headers] of Object.entries(assertions)) { |
||||
|
expect(queries[operationId].fields.headers).toStrictEqual(headers) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const contentTypeHeader = { |
||||
|
"Content-Type" : "application/json", |
||||
|
} |
||||
|
|
||||
|
it("populates headers", async () => { |
||||
|
const assertions = { |
||||
|
"createEntity" : { |
||||
|
...contentTypeHeader |
||||
|
}, |
||||
|
"getEntities" : { |
||||
|
}, |
||||
|
"getEntity" : { |
||||
|
}, |
||||
|
"updateEntity" : { |
||||
|
...contentTypeHeader |
||||
|
}, |
||||
|
"patchEntity" : { |
||||
|
...contentTypeHeader |
||||
|
}, |
||||
|
"deleteEntity" : { |
||||
|
"x-api-key" : "{{x-api-key}}", |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
await runTests("crud", testHeaders, assertions) |
||||
|
}) |
||||
|
|
||||
|
const testQuery = async (file, extension, assertions) => { |
||||
|
const queries = await getQueries(file, extension) |
||||
|
for (let [operationId, queryString] of Object.entries(assertions)) { |
||||
|
expect(queries[operationId].fields.queryString).toStrictEqual(queryString) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
it("populates query", async () => { |
||||
|
const assertions = { |
||||
|
"createEntity" : "", |
||||
|
"getEntities" : "page={{page}}&size={{size}}", |
||||
|
"getEntity" : "", |
||||
|
"updateEntity" : "", |
||||
|
"patchEntity" : "", |
||||
|
"deleteEntity" : "" |
||||
|
} |
||||
|
await runTests("crud", testQuery, assertions) |
||||
|
}) |
||||
|
|
||||
|
const testParameters = async (file, extension, assertions) => { |
||||
|
const queries = await getQueries(file, extension) |
||||
|
for (let [operationId, parameters] of Object.entries(assertions)) { |
||||
|
expect(queries[operationId].parameters).toStrictEqual(parameters) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
it("populates parameters", async () => { |
||||
|
const assertions = { |
||||
|
"createEntity" : [], |
||||
|
"getEntities" : [ |
||||
|
{ |
||||
|
"name" : "page", |
||||
|
"default" : "", |
||||
|
}, |
||||
|
{ |
||||
|
"name" : "size", |
||||
|
"default" : "", |
||||
|
} |
||||
|
], |
||||
|
"getEntity" : [ |
||||
|
{ |
||||
|
"name" : "entityId", |
||||
|
"default" : "", |
||||
|
} |
||||
|
], |
||||
|
"updateEntity" : [ |
||||
|
{ |
||||
|
"name" : "entityId", |
||||
|
"default" : "", |
||||
|
} |
||||
|
], |
||||
|
"patchEntity" : [ |
||||
|
{ |
||||
|
"name" : "entityId", |
||||
|
"default" : "", |
||||
|
} |
||||
|
], |
||||
|
"deleteEntity" : [ |
||||
|
{ |
||||
|
"name" : "entityId", |
||||
|
"default" : "", |
||||
|
}, |
||||
|
{ |
||||
|
"name" : "x-api-key", |
||||
|
"default" : "", |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
await runTests("crud", testParameters, assertions) |
||||
|
}) |
||||
|
|
||||
|
const testBody = async (file, extension, assertions) => { |
||||
|
const queries = await getQueries(file, extension) |
||||
|
for (let [operationId, body] of Object.entries(assertions)) { |
||||
|
expect(queries[operationId].fields.requestBody).toStrictEqual(JSON.stringify(body, null, 2)) |
||||
|
} |
||||
|
} |
||||
|
it("populates body", async () => { |
||||
|
const assertions = { |
||||
|
"createEntity" : { |
||||
|
"name" : "name", |
||||
|
"type" : "type", |
||||
|
}, |
||||
|
"getEntities" : undefined, |
||||
|
"getEntity" : undefined, |
||||
|
"updateEntity" : { |
||||
|
"id": 1, |
||||
|
"name" : "name", |
||||
|
"type" : "type", |
||||
|
}, |
||||
|
"patchEntity" : { |
||||
|
"id": 1, |
||||
|
"name" : "name", |
||||
|
"type" : "type", |
||||
|
}, |
||||
|
"deleteEntity" : undefined |
||||
|
} |
||||
|
await runTests("crud", testBody, assertions) |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,115 @@ |
|||||
|
|
||||
|
const bulkDocs = jest.fn() |
||||
|
const db = jest.fn(() => { |
||||
|
return { |
||||
|
bulkDocs |
||||
|
} |
||||
|
}) |
||||
|
jest.mock("../../../../../db", () => db) |
||||
|
|
||||
|
const { RestImporter } = require("../index") |
||||
|
|
||||
|
const fs = require("fs") |
||||
|
const path = require('path') |
||||
|
|
||||
|
const getData = (file) => { |
||||
|
return fs.readFileSync(path.join(__dirname, `../sources/tests/${file}`), "utf8") |
||||
|
} |
||||
|
|
||||
|
// openapi2 (swagger)
|
||||
|
const oapi2CrudJson = getData("openapi2/data/crud/crud.json") |
||||
|
const oapi2CrudYaml = getData("openapi2/data/crud/crud.json") |
||||
|
const oapi2PetstoreJson = getData("openapi2/data/petstore/petstore.json") |
||||
|
const oapi2PetstoreYaml = getData("openapi2/data/petstore/petstore.json") |
||||
|
|
||||
|
// curl
|
||||
|
const curl = getData("curl/data/post.txt") |
||||
|
|
||||
|
const datasets = { |
||||
|
oapi2CrudJson, |
||||
|
oapi2CrudYaml, |
||||
|
oapi2PetstoreJson, |
||||
|
oapi2PetstoreYaml, |
||||
|
curl |
||||
|
} |
||||
|
|
||||
|
describe("Rest Importer", () => { |
||||
|
let restImporter |
||||
|
|
||||
|
const init = async (data) => { |
||||
|
restImporter = new RestImporter(data) |
||||
|
await restImporter.init() |
||||
|
} |
||||
|
|
||||
|
const runTest = async (test, assertions) => { |
||||
|
for (let [key, data] of Object.entries(datasets)) { |
||||
|
await test(key, data, assertions) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const testGetInfo = async (key, data, assertions) => { |
||||
|
await init(data) |
||||
|
const info = await restImporter.getInfo() |
||||
|
expect(info.name).toBe(assertions[key].name) |
||||
|
expect(info.url).toBe(assertions[key].url) |
||||
|
} |
||||
|
|
||||
|
it("gets info", async () => { |
||||
|
const assertions = { |
||||
|
"oapi2CrudJson" : { |
||||
|
name: "CRUD", |
||||
|
url: "http://example.com" |
||||
|
}, |
||||
|
"oapi2CrudYaml" : { |
||||
|
name: "CRUD", |
||||
|
url: "http://example.com" |
||||
|
}, |
||||
|
"oapi2PetstoreJson" : { |
||||
|
name: "Swagger Petstore", |
||||
|
url: "https://petstore.swagger.io/v2" |
||||
|
}, |
||||
|
"oapi2PetstoreYaml" :{ |
||||
|
name: "Swagger Petstore", |
||||
|
url: "https://petstore.swagger.io/v2" |
||||
|
}, |
||||
|
"curl": { |
||||
|
name: "example.com", |
||||
|
url: "http://example.com" |
||||
|
} |
||||
|
} |
||||
|
await runTest(testGetInfo, assertions) |
||||
|
}) |
||||
|
|
||||
|
const testImportQueries = async (key, data, assertions) => { |
||||
|
await init(data) |
||||
|
bulkDocs.mockReturnValue([]) |
||||
|
const importResult = await restImporter.importQueries("appId", "datasourceId") |
||||
|
expect(importResult.errorQueries.length).toBe(0) |
||||
|
expect(importResult.queries.length).toBe(assertions[key].count) |
||||
|
expect(bulkDocs).toHaveBeenCalledTimes(1) |
||||
|
jest.clearAllMocks() |
||||
|
} |
||||
|
|
||||
|
it("imports queries", async () => { |
||||
|
// simple sanity assertions that the whole dataset
|
||||
|
// makes it through the importer
|
||||
|
const assertions = { |
||||
|
"oapi2CrudJson" : { |
||||
|
count: 6, |
||||
|
}, |
||||
|
"oapi2CrudYaml" :{ |
||||
|
count: 6, |
||||
|
}, |
||||
|
"oapi2PetstoreJson" : { |
||||
|
count: 20, |
||||
|
}, |
||||
|
"oapi2PetstoreYaml" :{ |
||||
|
count: 20, |
||||
|
}, |
||||
|
"curl": { |
||||
|
count: 1 |
||||
|
} |
||||
|
} |
||||
|
await runTest(testImportQueries, assertions) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,40 @@ |
|||||
|
const joiValidator = require("../../../middleware/joi-validator") |
||||
|
const Joi = require("joi") |
||||
|
|
||||
|
exports.queryValidation = () => { |
||||
|
return 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(), |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
exports.generateQueryValidation = () => { |
||||
|
// prettier-ignore
|
||||
|
return joiValidator.body(exports.queryValidation()) |
||||
|
} |
||||
|
|
||||
|
exports.generateQueryPreviewValidation = () => { |
||||
|
// prettier-ignore
|
||||
|
return joiValidator.body(Joi.object({ |
||||
|
fields: Joi.object().required(), |
||||
|
queryVerb: Joi.string().allow().required(), |
||||
|
extra: Joi.object().optional(), |
||||
|
datasourceId: Joi.string().required(), |
||||
|
transformer: Joi.string().optional(), |
||||
|
parameters: Joi.object({}).required().unknown(true) |
||||
|
})) |
||||
|
} |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue