forked from tsai/budibase
7 changed files with 2869 additions and 2 deletions
@ -0,0 +1,194 @@ |
|||
import { ImportInfo } from "./base" |
|||
import { Query, QueryParameter } from "../../../../../definitions/datasource" |
|||
import { OpenAPIV3 } from "openapi-types" |
|||
import { OpenAPISource } from "./base/openapi" |
|||
import { URL } from "url" |
|||
import { |
|||
getGlobalDB, |
|||
getTenantId, |
|||
isMultiTenant, |
|||
} from "@budibase/backend-core/tenancy" |
|||
import { getScopedConfig } from "@budibase/backend-core/db" |
|||
|
|||
const jsonMimeType = "application/json" |
|||
const parameterNotRef = ( |
|||
param: OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject |
|||
): param is OpenAPIV3.ParameterObject => { |
|||
// all refs are deferenced by parser library
|
|||
return true |
|||
} |
|||
|
|||
const requestBodyNotRef = ( |
|||
param: OpenAPIV3.RequestBodyObject | OpenAPIV3.ReferenceObject | undefined |
|||
): param is OpenAPIV3.RequestBodyObject => { |
|||
// all refs are deferenced by parser library
|
|||
return param !== undefined |
|||
} |
|||
|
|||
const isOpenAPI3 = (document: any): document is OpenAPIV3.Document => { |
|||
return document.openapi.includes("3") |
|||
} |
|||
|
|||
const methods: string[] = Object.values(OpenAPIV3.HttpMethods) |
|||
|
|||
const isOperation = ( |
|||
key: string, |
|||
pathItem: any |
|||
): pathItem is OpenAPIV3.OperationObject => { |
|||
return methods.includes(key) |
|||
} |
|||
|
|||
const isParameter = ( |
|||
key: string, |
|||
pathItem: any |
|||
): pathItem is OpenAPIV3.ParameterObject => { |
|||
return !isOperation(key, pathItem) |
|||
} |
|||
|
|||
const getRequestBody = (operation: OpenAPIV3.OperationObject) => { |
|||
if (requestBodyNotRef(operation.requestBody)) { |
|||
const request: OpenAPIV3.RequestBodyObject = |
|||
operation.requestBody as OpenAPIV3.RequestBodyObject |
|||
return request.content[jsonMimeType]?.example |
|||
} |
|||
return null |
|||
} |
|||
|
|||
/** |
|||
* OpenAPI Version 3.1 - aka "Swagger" |
|||
* https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
|
|||
*/ |
|||
export class OpenAPI3 extends OpenAPISource { |
|||
document!: OpenAPIV3.Document |
|||
|
|||
isSupported = async (data: string): Promise<boolean> => { |
|||
try { |
|||
const document: any = await this.parseData(data) |
|||
if (isOpenAPI3(document)) { |
|||
this.document = document |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} catch (err) { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
getPlatformUrl = async (): Promise<string> => { |
|||
const db = getGlobalDB() |
|||
const publicConfig = await getScopedConfig(db, { |
|||
type: "settings", |
|||
}) |
|||
|
|||
let url = publicConfig.platformUrl |
|||
if (isMultiTenant()) { |
|||
url += `/${getTenantId()}` |
|||
} |
|||
return url |
|||
} |
|||
|
|||
getUrl = async (): Promise<URL> => { |
|||
const platformUrl = await this.getPlatformUrl() |
|||
let url = this.document.servers ? this.document.servers[0].url : null |
|||
if (url) { |
|||
// check if url is relative or absolute
|
|||
if (url.includes("http") || url.includes("https")) { |
|||
return new URL(url) |
|||
} |
|||
|
|||
return new URL(`${platformUrl}${url}`) |
|||
} |
|||
// if the specification doesn't contain a servers object, return the PLATFORM_URM environment variable
|
|||
return new URL(platformUrl) |
|||
} |
|||
|
|||
getInfo = async (): Promise<ImportInfo> => { |
|||
const name = this.document.info.title || "Swagger Import" |
|||
return { |
|||
name, |
|||
} |
|||
} |
|||
|
|||
getQueries = async (datasourceId: string): Promise<Query[]> => { |
|||
const url: URL = await this.getUrl() |
|||
const queries: Query[] = [] |
|||
|
|||
for (let [path, pathItemObject] of Object.entries(this.document.paths)) { |
|||
// parameters that apply to every operation in the path
|
|||
let pathParams: OpenAPIV3.ParameterObject[] = [] |
|||
|
|||
// pathItemObject can be undefined
|
|||
if (!pathItemObject) { |
|||
continue |
|||
} |
|||
|
|||
for (let [key, opOrParams] of Object.entries(pathItemObject)) { |
|||
if (isParameter(key, opOrParams)) { |
|||
const pathParameters = opOrParams as OpenAPIV3.ParameterObject[] |
|||
pathParams.push(...pathParameters) |
|||
continue |
|||
} |
|||
// can not be a parameter, must be an operation
|
|||
const operation = opOrParams as OpenAPIV3.OperationObject |
|||
|
|||
const methodName = key |
|||
const name = operation.operationId || path |
|||
let queryString = "" |
|||
const headers: any = {} |
|||
let requestBody = getRequestBody(operation) |
|||
const parameters: QueryParameter[] = [] |
|||
|
|||
// 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 |
|||
} |
|||
|
|||
// add the parameter if it can be bound in our config
|
|||
if (["query", "header", "path"].includes(param.in)) { |
|||
parameters.push({ |
|||
name: param.name, |
|||
default: "", |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
const query = this.constructQuery( |
|||
datasourceId, |
|||
name, |
|||
methodName, |
|||
path, |
|||
url, |
|||
queryString, |
|||
headers, |
|||
parameters, |
|||
requestBody |
|||
) |
|||
queries.push(query) |
|||
} |
|||
} |
|||
|
|||
return queries |
|||
} |
|||
} |
|||
@ -0,0 +1,231 @@ |
|||
{ |
|||
"openapi": "3.0.2", |
|||
"info": { |
|||
"description": "A basic swagger file", |
|||
"version": "1.0.0", |
|||
"title": "CRUD" |
|||
}, |
|||
"tags": [ |
|||
{ |
|||
"name": "entity" |
|||
} |
|||
], |
|||
"paths": { |
|||
"/entities": { |
|||
"post": { |
|||
"tags": [ |
|||
"entity" |
|||
], |
|||
"operationId": "createEntity", |
|||
"parameters": [ |
|||
{ |
|||
"$ref": "#/components/schemas/CreateEntityParameter" |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "successful operation", |
|||
"schema": { |
|||
"$ref": "#/components/schemas/Entity" |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"get": { |
|||
"tags": [ |
|||
"entity" |
|||
], |
|||
"operationId": "getEntities", |
|||
"produces": [ |
|||
"application/json" |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"$ref": "#/components/schemas/PageParameter" |
|||
}, |
|||
{ |
|||
"$ref": "#/components/schemas/SizeParameter" |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "successful operation", |
|||
"schema": { |
|||
"$ref": "#/components/schemas/Entities" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"/entities/{entityId}": { |
|||
"parameters": [ |
|||
{ |
|||
"$ref": "#/components/schemas/EntityIdParameter" |
|||
} |
|||
], |
|||
"get": { |
|||
"tags": [ |
|||
"entity" |
|||
], |
|||
"operationId": "getEntity", |
|||
"produces": [ |
|||
"application/json" |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "successful operation", |
|||
"schema": { |
|||
"$ref": "#/components/schemas/Entity" |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"put": { |
|||
"tags": [ |
|||
"entity" |
|||
], |
|||
"operationId": "updateEntity", |
|||
"parameters": [ |
|||
{ |
|||
"$ref": "#/components/schemas/EntityParameter" |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "successful operation", |
|||
"schema": { |
|||
"$ref": "#/components/schemas/Entity" |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"patch": { |
|||
"tags": [ |
|||
"entity" |
|||
], |
|||
"operationId": "patchEntity", |
|||
"parameters": [ |
|||
{ |
|||
"$ref": "#/components/schemas/EntityParameter" |
|||
} |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "successful operation", |
|||
"schema": { |
|||
"$ref": "#/components/schemas/Entity" |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"delete": { |
|||
"tags": [ |
|||
"entity" |
|||
], |
|||
"parameters": [ |
|||
{ |
|||
"type": "string", |
|||
"name": "x-api-key", |
|||
"in": "header", |
|||
"required": false |
|||
} |
|||
], |
|||
"operationId": "deleteEntity", |
|||
"responses": { |
|||
"204": { |
|||
"description": "successful operation" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"EntityIdParameter": { |
|||
"type": "integer", |
|||
"format": "int64", |
|||
"name": "entityId", |
|||
"in": "path", |
|||
"required": true |
|||
}, |
|||
"CreateEntityParameter": { |
|||
"name": "entity", |
|||
"in": "body", |
|||
"required": true, |
|||
"schema": { |
|||
"$ref": "#/components/schemas/CreateEntity" |
|||
} |
|||
}, |
|||
"EntityParameter": { |
|||
"name": "entity", |
|||
"in": "body", |
|||
"required": true, |
|||
"schema": { |
|||
"$ref": "#/components/schemas/Entity" |
|||
} |
|||
}, |
|||
"PageParameter": { |
|||
"type": "integer", |
|||
"format": "int32", |
|||
"name": "page", |
|||
"in": "query", |
|||
"required": false |
|||
}, |
|||
"SizeParameter": { |
|||
"type": "integer", |
|||
"format": "int32", |
|||
"name": "size", |
|||
"in": "query", |
|||
"required": false |
|||
}, |
|||
"APIKeyParameter": { |
|||
"type": "string", |
|||
"name": "x-api-key", |
|||
"in": "header", |
|||
"required": false |
|||
}, |
|||
"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": "#/components/schemas/CreateEntity" |
|||
} |
|||
], |
|||
"example": { |
|||
"id": 1, |
|||
"name": "name", |
|||
"type": "type" |
|||
} |
|||
}, |
|||
"Entities" : { |
|||
"type": "array", |
|||
"items": { |
|||
"$ref": "#/components/schemas/Entity" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
--- |
|||
openapi: "3.0.2" |
|||
info: |
|||
description: A basic swagger file |
|||
version: 1.0.0 |
|||
title: CRUD |
|||
host: example.com |
|||
tags: |
|||
- name: entity |
|||
paths: |
|||
"/entities": |
|||
post: |
|||
tags: |
|||
- entity |
|||
operationId: createEntity |
|||
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,804 @@ |
|||
--- |
|||
openapi: 3.0.2 |
|||
info: |
|||
title: Swagger Petstore - OpenAPI 3.0 |
|||
description: |- |
|||
This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about |
|||
Swagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! |
|||
You can now help us improve the API whether it's by making changes to the definition itself or to the code. |
|||
That way, with time, we can improve the API in general, and expose some of the new features in OAS3. |
|||
|
|||
Some useful links: |
|||
- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) |
|||
- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) |
|||
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 |
|||
version: 1.0.11 |
|||
externalDocs: |
|||
description: Find out more about Swagger |
|||
url: http://swagger.io |
|||
servers: |
|||
- url: "/api/v3" |
|||
tags: |
|||
- name: pet |
|||
description: Everything about your Pets |
|||
externalDocs: |
|||
description: Find out more |
|||
url: http://swagger.io |
|||
- name: store |
|||
description: Access to Petstore orders |
|||
externalDocs: |
|||
description: Find out more about our store |
|||
url: http://swagger.io |
|||
- name: user |
|||
description: Operations about user |
|||
paths: |
|||
"/pet": |
|||
put: |
|||
tags: |
|||
- pet |
|||
summary: Update an existing pet |
|||
description: Update an existing pet by Id |
|||
operationId: updatePet |
|||
requestBody: |
|||
description: Update an existent pet in the store |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/x-www-form-urlencoded: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
required: true |
|||
responses: |
|||
'200': |
|||
description: Successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
'400': |
|||
description: Invalid ID supplied |
|||
'404': |
|||
description: Pet not found |
|||
'405': |
|||
description: Validation exception |
|||
security: |
|||
- petstore_auth: |
|||
- write:pets |
|||
- read:pets |
|||
post: |
|||
tags: |
|||
- pet |
|||
summary: Add a new pet to the store |
|||
description: Add a new pet to the store |
|||
operationId: addPet |
|||
requestBody: |
|||
description: Create a new pet in the store |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/x-www-form-urlencoded: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
required: true |
|||
responses: |
|||
'200': |
|||
description: Successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
'405': |
|||
description: Invalid input |
|||
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 |
|||
parameters: |
|||
- name: status |
|||
in: query |
|||
description: Status values that need to be considered for filter |
|||
required: false |
|||
explode: true |
|||
schema: |
|||
type: string |
|||
default: available |
|||
enum: |
|||
- available |
|||
- pending |
|||
- sold |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
type: array |
|||
items: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/json: |
|||
schema: |
|||
type: array |
|||
items: |
|||
"$ref": "#/components/schemas/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 |
|||
parameters: |
|||
- name: tags |
|||
in: query |
|||
description: Tags to filter by |
|||
required: false |
|||
explode: true |
|||
schema: |
|||
type: array |
|||
items: |
|||
type: string |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
type: array |
|||
items: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/json: |
|||
schema: |
|||
type: array |
|||
items: |
|||
"$ref": "#/components/schemas/Pet" |
|||
'400': |
|||
description: Invalid tag value |
|||
security: |
|||
- petstore_auth: |
|||
- write:pets |
|||
- read:pets |
|||
"/pet/{petId}": |
|||
get: |
|||
tags: |
|||
- pet |
|||
summary: Find pet by ID |
|||
description: Returns a single pet |
|||
operationId: getPetById |
|||
parameters: |
|||
- name: petId |
|||
in: path |
|||
description: ID of pet to return |
|||
required: true |
|||
schema: |
|||
type: integer |
|||
format: int64 |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
'400': |
|||
description: Invalid ID supplied |
|||
'404': |
|||
description: Pet not found |
|||
security: |
|||
- api_key: [] |
|||
- petstore_auth: |
|||
- write:pets |
|||
- read:pets |
|||
post: |
|||
tags: |
|||
- pet |
|||
summary: Updates a pet in the store with form data |
|||
description: '' |
|||
operationId: updatePetWithForm |
|||
parameters: |
|||
- name: petId |
|||
in: path |
|||
description: ID of pet that needs to be updated |
|||
required: true |
|||
schema: |
|||
type: integer |
|||
format: int64 |
|||
- name: name |
|||
in: query |
|||
description: Name of pet that needs to be updated |
|||
schema: |
|||
type: string |
|||
- name: status |
|||
in: query |
|||
description: Status of pet that needs to be updated |
|||
schema: |
|||
type: string |
|||
responses: |
|||
'405': |
|||
description: Invalid input |
|||
security: |
|||
- petstore_auth: |
|||
- write:pets |
|||
- read:pets |
|||
delete: |
|||
tags: |
|||
- pet |
|||
summary: Deletes a pet |
|||
description: '' |
|||
operationId: deletePet |
|||
parameters: |
|||
- name: api_key |
|||
in: header |
|||
description: '' |
|||
required: false |
|||
schema: |
|||
type: string |
|||
- name: petId |
|||
in: path |
|||
description: Pet id to delete |
|||
required: true |
|||
schema: |
|||
type: integer |
|||
format: int64 |
|||
responses: |
|||
'400': |
|||
description: Invalid pet value |
|||
security: |
|||
- petstore_auth: |
|||
- write:pets |
|||
- read:pets |
|||
"/pet/{petId}/uploadImage": |
|||
post: |
|||
tags: |
|||
- pet |
|||
summary: uploads an image |
|||
description: '' |
|||
operationId: uploadFile |
|||
parameters: |
|||
- name: petId |
|||
in: path |
|||
description: ID of pet to update |
|||
required: true |
|||
schema: |
|||
type: integer |
|||
format: int64 |
|||
- name: additionalMetadata |
|||
in: query |
|||
description: Additional Metadata |
|||
required: false |
|||
schema: |
|||
type: string |
|||
requestBody: |
|||
content: |
|||
application/octet-stream: |
|||
schema: |
|||
type: string |
|||
format: binary |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/ApiResponse" |
|||
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 |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/json: |
|||
schema: |
|||
type: object |
|||
additionalProperties: |
|||
type: integer |
|||
format: int32 |
|||
security: |
|||
- api_key: [] |
|||
"/store/order": |
|||
post: |
|||
tags: |
|||
- store |
|||
summary: Place an order for a pet |
|||
description: Place a new order in the store |
|||
operationId: placeOrder |
|||
requestBody: |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Order" |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Order" |
|||
application/x-www-form-urlencoded: |
|||
schema: |
|||
"$ref": "#/components/schemas/Order" |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Order" |
|||
'405': |
|||
description: Invalid input |
|||
"/store/order/{orderId}": |
|||
get: |
|||
tags: |
|||
- store |
|||
summary: Find purchase order by ID |
|||
description: For valid response try integer IDs with value <= 5 or > 10. Other |
|||
values will generate exceptions. |
|||
operationId: getOrderById |
|||
parameters: |
|||
- name: orderId |
|||
in: path |
|||
description: ID of order that needs to be fetched |
|||
required: true |
|||
schema: |
|||
type: integer |
|||
format: int64 |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Order" |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/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 value < 1000. Anything |
|||
above 1000 or nonintegers will generate API errors |
|||
operationId: deleteOrder |
|||
parameters: |
|||
- name: orderId |
|||
in: path |
|||
description: ID of the order that needs to be deleted |
|||
required: true |
|||
schema: |
|||
type: integer |
|||
format: int64 |
|||
responses: |
|||
'400': |
|||
description: Invalid ID supplied |
|||
'404': |
|||
description: Order not found |
|||
"/user": |
|||
post: |
|||
tags: |
|||
- user |
|||
summary: Create user |
|||
description: This can only be done by the logged in user. |
|||
operationId: createUser |
|||
requestBody: |
|||
description: Created user object |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
application/x-www-form-urlencoded: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
responses: |
|||
default: |
|||
description: successful operation |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
"/user/createWithList": |
|||
post: |
|||
tags: |
|||
- user |
|||
summary: Creates list of users with given input array |
|||
description: Creates list of users with given input array |
|||
operationId: createUsersWithListInput |
|||
requestBody: |
|||
content: |
|||
application/json: |
|||
schema: |
|||
type: array |
|||
items: |
|||
"$ref": "#/components/schemas/User" |
|||
responses: |
|||
'200': |
|||
description: Successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
default: |
|||
description: successful operation |
|||
"/user/login": |
|||
get: |
|||
tags: |
|||
- user |
|||
summary: Logs user into the system |
|||
description: '' |
|||
operationId: loginUser |
|||
parameters: |
|||
- name: username |
|||
in: query |
|||
description: The user name for login |
|||
required: false |
|||
schema: |
|||
type: string |
|||
- name: password |
|||
in: query |
|||
description: The password for login in clear text |
|||
required: false |
|||
schema: |
|||
type: string |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
headers: |
|||
X-Rate-Limit: |
|||
description: calls per hour allowed by the user |
|||
schema: |
|||
type: integer |
|||
format: int32 |
|||
X-Expires-After: |
|||
description: date in UTC when token expires |
|||
schema: |
|||
type: string |
|||
format: date-time |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
type: string |
|||
application/json: |
|||
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 |
|||
parameters: [] |
|||
responses: |
|||
default: |
|||
description: successful operation |
|||
"/user/{username}": |
|||
get: |
|||
tags: |
|||
- user |
|||
summary: Get user by user name |
|||
description: '' |
|||
operationId: getUserByName |
|||
parameters: |
|||
- name: username |
|||
in: path |
|||
description: 'The name that needs to be fetched. Use user1 for testing. ' |
|||
required: true |
|||
schema: |
|||
type: string |
|||
responses: |
|||
'200': |
|||
description: successful operation |
|||
content: |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
'400': |
|||
description: Invalid username supplied |
|||
'404': |
|||
description: User not found |
|||
put: |
|||
tags: |
|||
- user |
|||
summary: Update user |
|||
description: This can only be done by the logged in user. |
|||
operationId: updateUser |
|||
parameters: |
|||
- name: username |
|||
in: path |
|||
description: name that need to be deleted |
|||
required: true |
|||
schema: |
|||
type: string |
|||
requestBody: |
|||
description: Update an existent user in the store |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
application/x-www-form-urlencoded: |
|||
schema: |
|||
"$ref": "#/components/schemas/User" |
|||
responses: |
|||
default: |
|||
description: successful operation |
|||
delete: |
|||
tags: |
|||
- user |
|||
summary: Delete user |
|||
description: This can only be done by the logged in user. |
|||
operationId: deleteUser |
|||
parameters: |
|||
- name: username |
|||
in: path |
|||
description: The name that needs to be deleted |
|||
required: true |
|||
schema: |
|||
type: string |
|||
responses: |
|||
'400': |
|||
description: Invalid username supplied |
|||
'404': |
|||
description: User not found |
|||
components: |
|||
schemas: |
|||
Order: |
|||
type: object |
|||
properties: |
|||
id: |
|||
type: integer |
|||
format: int64 |
|||
example: 10 |
|||
petId: |
|||
type: integer |
|||
format: int64 |
|||
example: 198772 |
|||
quantity: |
|||
type: integer |
|||
format: int32 |
|||
example: 7 |
|||
shipDate: |
|||
type: string |
|||
format: date-time |
|||
status: |
|||
type: string |
|||
description: Order Status |
|||
example: approved |
|||
enum: |
|||
- placed |
|||
- approved |
|||
- delivered |
|||
complete: |
|||
type: boolean |
|||
xml: |
|||
name: order |
|||
Customer: |
|||
type: object |
|||
properties: |
|||
id: |
|||
type: integer |
|||
format: int64 |
|||
example: 100000 |
|||
username: |
|||
type: string |
|||
example: fehguy |
|||
address: |
|||
type: array |
|||
xml: |
|||
name: addresses |
|||
wrapped: true |
|||
items: |
|||
"$ref": "#/components/schemas/Address" |
|||
xml: |
|||
name: customer |
|||
Address: |
|||
type: object |
|||
properties: |
|||
street: |
|||
type: string |
|||
example: 437 Lytton |
|||
city: |
|||
type: string |
|||
example: Palo Alto |
|||
state: |
|||
type: string |
|||
example: CA |
|||
zip: |
|||
type: string |
|||
example: '94301' |
|||
xml: |
|||
name: address |
|||
Category: |
|||
type: object |
|||
properties: |
|||
id: |
|||
type: integer |
|||
format: int64 |
|||
example: 1 |
|||
name: |
|||
type: string |
|||
example: Dogs |
|||
xml: |
|||
name: category |
|||
User: |
|||
type: object |
|||
properties: |
|||
id: |
|||
type: integer |
|||
format: int64 |
|||
example: 10 |
|||
username: |
|||
type: string |
|||
example: theUser |
|||
firstName: |
|||
type: string |
|||
example: John |
|||
lastName: |
|||
type: string |
|||
example: James |
|||
email: |
|||
type: string |
|||
example: john@email.com |
|||
password: |
|||
type: string |
|||
example: '12345' |
|||
phone: |
|||
type: string |
|||
example: '12345' |
|||
userStatus: |
|||
type: integer |
|||
description: User Status |
|||
format: int32 |
|||
example: 1 |
|||
xml: |
|||
name: user |
|||
Tag: |
|||
type: object |
|||
properties: |
|||
id: |
|||
type: integer |
|||
format: int64 |
|||
name: |
|||
type: string |
|||
xml: |
|||
name: tag |
|||
Pet: |
|||
required: |
|||
- name |
|||
- photoUrls |
|||
type: object |
|||
properties: |
|||
id: |
|||
type: integer |
|||
format: int64 |
|||
example: 10 |
|||
name: |
|||
type: string |
|||
example: doggie |
|||
category: |
|||
"$ref": "#/components/schemas/Category" |
|||
photoUrls: |
|||
type: array |
|||
xml: |
|||
wrapped: true |
|||
items: |
|||
type: string |
|||
xml: |
|||
name: photoUrl |
|||
tags: |
|||
type: array |
|||
xml: |
|||
wrapped: true |
|||
items: |
|||
"$ref": "#/components/schemas/Tag" |
|||
status: |
|||
type: string |
|||
description: pet status in the store |
|||
enum: |
|||
- available |
|||
- pending |
|||
- sold |
|||
xml: |
|||
name: pet |
|||
ApiResponse: |
|||
type: object |
|||
properties: |
|||
code: |
|||
type: integer |
|||
format: int32 |
|||
type: |
|||
type: string |
|||
message: |
|||
type: string |
|||
xml: |
|||
name: "##default" |
|||
requestBodies: |
|||
Pet: |
|||
description: Pet object that needs to be added to the store |
|||
content: |
|||
application/json: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
application/xml: |
|||
schema: |
|||
"$ref": "#/components/schemas/Pet" |
|||
UserArray: |
|||
description: List of user object |
|||
content: |
|||
application/json: |
|||
schema: |
|||
type: array |
|||
items: |
|||
"$ref": "#/components/schemas/User" |
|||
securitySchemes: |
|||
petstore_auth: |
|||
type: oauth2 |
|||
flows: |
|||
implicit: |
|||
authorizationUrl: https://petstore3.swagger.io/oauth/authorize |
|||
scopes: |
|||
write:pets: modify pets in your account |
|||
read:pets: read your pets |
|||
api_key: |
|||
type: apiKey |
|||
name: api_key |
|||
in: header |
|||
@ -0,0 +1,256 @@ |
|||
const fs = require("fs") |
|||
const path = require("path") |
|||
const getGlobalDB = jest.fn().mockReturnValue("db") |
|||
const getTenantId = jest.fn().mockReturnValue("1") |
|||
const isMultiTenant = jest.fn().mockReturnValue(true) |
|||
const getScopedConfig = jest |
|||
.fn() |
|||
.mockResolvedValue({ platformUrl: "somePlatform" }) |
|||
|
|||
jest.mock("@budibase/backend-core/db", () => ({ |
|||
getScopedConfig, |
|||
})) |
|||
|
|||
jest.mock("@budibase/backend-core/tenancy", () => ({ |
|||
getTenantId, |
|||
getGlobalDB, |
|||
isMultiTenant, |
|||
})) |
|||
const { OpenAPI3 } = require("../../openapi3") |
|||
|
|||
const getData = (file, extension) => { |
|||
return fs.readFileSync( |
|||
path.join(__dirname, `./data/${file}/${file}.${extension}`), |
|||
"utf8" |
|||
) |
|||
} |
|||
|
|||
describe("OpenAPI3 Import", () => { |
|||
let openapi3 |
|||
|
|||
beforeEach(() => { |
|||
openapi3 = new OpenAPI3() |
|||
}) |
|||
|
|||
it("validates unsupported data", async () => { |
|||
let data |
|||
let supported |
|||
|
|||
// non json / yaml
|
|||
data = "curl http://example.com" |
|||
supported = await openapi3.isSupported(data) |
|||
expect(supported).toBe(false) |
|||
|
|||
// Empty
|
|||
data = "" |
|||
supported = await openapi3.isSupported(data) |
|||
expect(supported).toBe(false) |
|||
}) |
|||
|
|||
const runTests = async (filename, test, assertions) => { |
|||
/*for (let extension of ["json", "yaml"]) { |
|||
await test(filename, extension, assertions) |
|||
}*/ |
|||
await test(filename, "json", assertions) |
|||
} |
|||
|
|||
const testImportInfo = async (file, extension) => { |
|||
await openapi3.isSupported(getData(file, extension)) |
|||
const info = await openapi3.getInfo() |
|||
expect(info.name).toBe("Swagger Petstore - OpenAPI 3.0") |
|||
} |
|||
|
|||
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 openapi3.isSupported(getData(file, extension)) |
|||
const queries = await openapi3.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: "http://example.com/entities", |
|||
getEntities: "http://example.com/entities", |
|||
getEntity: "http://example.com/entities/{{entityId}}", |
|||
updateEntity: "http://example.com/entities/{{entityId}}", |
|||
patchEntity: "http://example.com/entities/{{entityId}}", |
|||
deleteEntity: "http://example.com/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) |
|||
}) |
|||
}) |
|||
}) |
|||
Loading…
Reference in new issue