mirror of https://github.com/Budibase/budibase.git
11 changed files with 1194 additions and 213 deletions
@ -0,0 +1,275 @@ |
|||||
|
import CouchDB from "../../../db" |
||||
|
import { queryValidation } from "./validation" |
||||
|
import { generateQueryID } from "../../../db/utils" |
||||
|
import { Spec as Swagger2, Operation } from "swagger-schema-official" |
||||
|
|
||||
|
// {
|
||||
|
// "_id": "query_datasource_d62738f2d72a466997ffbf46f4952404_e7258ad382cd4c37961b81730633ff2d",
|
||||
|
// "_rev": "1-e702a18eaa96c7cb4be1b402c34eaa59",
|
||||
|
// "datasourceId": "datasource_d62738f2d72a466997ffbf46f4952404",
|
||||
|
// "parameters": [
|
||||
|
// {
|
||||
|
// "name": "paramtest",
|
||||
|
// "default": "defaultValue"
|
||||
|
// }
|
||||
|
// ],
|
||||
|
// "fields": {
|
||||
|
// "headers": {
|
||||
|
// "headertest": "test"
|
||||
|
// },
|
||||
|
// "queryString": "query=test",
|
||||
|
// "path": "/path/test"
|
||||
|
// },
|
||||
|
// "queryVerb": "read",
|
||||
|
// "transformer": "return data.test",
|
||||
|
// "schema": {},
|
||||
|
// "name": "name",
|
||||
|
// "readable": true
|
||||
|
// }
|
||||
|
|
||||
|
// return joiValidator.body(Joi.object({
|
||||
|
// _id: Joi.string(),
|
||||
|
// _rev: Joi.string(),
|
||||
|
// name: Joi.string().required(),
|
||||
|
// fields: Joi.object().required(),
|
||||
|
// datasourceId: Joi.string().required(),
|
||||
|
// readable: Joi.boolean(),
|
||||
|
// parameters: Joi.array().items(Joi.object({
|
||||
|
// name: Joi.string(),
|
||||
|
// default: Joi.string().allow(""),
|
||||
|
// })),
|
||||
|
// queryVerb: Joi.string().allow().required(),
|
||||
|
// extra: Joi.object().optional(),
|
||||
|
// schema: Joi.object({}).required().unknown(true),
|
||||
|
// transformer: Joi.string().optional(),
|
||||
|
// }))
|
||||
|
|
||||
|
interface Parameter { |
||||
|
name: string |
||||
|
default: string |
||||
|
} |
||||
|
|
||||
|
interface Query { |
||||
|
_id?: string |
||||
|
datasourceId: string |
||||
|
name: string |
||||
|
parameters: Parameter[] |
||||
|
fields: { |
||||
|
headers: any |
||||
|
queryString: string |
||||
|
path: string |
||||
|
} |
||||
|
transformer: string | null |
||||
|
schema: any |
||||
|
readable: boolean |
||||
|
queryVerb: string |
||||
|
} |
||||
|
|
||||
|
enum Strategy { |
||||
|
SWAGGER2, |
||||
|
OPENAPI3, |
||||
|
CURL, |
||||
|
} |
||||
|
|
||||
|
enum MethodToVerb { |
||||
|
get = "read", |
||||
|
post = "create", |
||||
|
put = "update", |
||||
|
patch = "patch", |
||||
|
delete = "delete", |
||||
|
} |
||||
|
|
||||
|
interface ImportResult { |
||||
|
errorQueries: Query[] |
||||
|
} |
||||
|
|
||||
|
interface DatasourceInfo { |
||||
|
url: string |
||||
|
name: string |
||||
|
defaultHeaders: any[] |
||||
|
} |
||||
|
|
||||
|
const parseImportStrategy = (data: string): Strategy => { |
||||
|
return Strategy.SWAGGER2 |
||||
|
} |
||||
|
|
||||
|
// SWAGGER
|
||||
|
|
||||
|
const parseSwagger2Info = (swagger2: Swagger2): DatasourceInfo => { |
||||
|
return { |
||||
|
url: "http://localhost:3000", |
||||
|
name: "swagger", |
||||
|
defaultHeaders: [], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const parseSwagger2Queries = ( |
||||
|
datasourceId: string, |
||||
|
swagger2: Swagger2 |
||||
|
): Query[] => { |
||||
|
const queries = [] |
||||
|
|
||||
|
for (let [pathName, path] of Object.entries(swagger2.paths)) { |
||||
|
for (let [methodName, op] of Object.entries(path)) { |
||||
|
let operation = op as Operation |
||||
|
|
||||
|
const name = operation.operationId || pathName |
||||
|
const queryString = "" |
||||
|
const headers = {} |
||||
|
const parameters: Parameter[] = [] |
||||
|
|
||||
|
const query = constructQuery( |
||||
|
datasourceId, |
||||
|
name, |
||||
|
methodName, |
||||
|
pathName, |
||||
|
queryString, |
||||
|
headers, |
||||
|
parameters |
||||
|
) |
||||
|
queries.push(query) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return queries |
||||
|
} |
||||
|
|
||||
|
// OPEN API
|
||||
|
|
||||
|
const parseOpenAPI3Info = (data: any): DatasourceInfo => { |
||||
|
return { |
||||
|
url: "http://localhost:3000", |
||||
|
name: "swagger", |
||||
|
defaultHeaders: [], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const parseOpenAPI3Queries = (datasourceId: string, data: string): Query[] => { |
||||
|
return [] |
||||
|
} |
||||
|
|
||||
|
// CURL
|
||||
|
|
||||
|
const parseCurlDatasourceInfo = (data: any): DatasourceInfo => { |
||||
|
return { |
||||
|
url: "http://localhost:3000", |
||||
|
name: "swagger", |
||||
|
defaultHeaders: [], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const parseCurlQueries = (datasourceId: string, data: string): Query[] => { |
||||
|
return [] |
||||
|
} |
||||
|
|
||||
|
const verbFromMethod = (method: string) => { |
||||
|
const verb = (<any>MethodToVerb)[method] |
||||
|
if (!verb) { |
||||
|
throw new Error(`Unsupported method: ${method}`) |
||||
|
} |
||||
|
return verb |
||||
|
} |
||||
|
|
||||
|
const constructQuery = ( |
||||
|
datasourceId: string, |
||||
|
name: string, |
||||
|
method: string, |
||||
|
path: string, |
||||
|
queryString: string, |
||||
|
headers: any = {}, |
||||
|
parameters: Parameter[] = [] |
||||
|
): Query => { |
||||
|
const readable = true |
||||
|
const queryVerb = verbFromMethod(method) |
||||
|
const transformer = "return data" |
||||
|
const schema = {} |
||||
|
|
||||
|
const query: Query = { |
||||
|
datasourceId, |
||||
|
name, |
||||
|
parameters, |
||||
|
fields: { |
||||
|
headers, |
||||
|
queryString, |
||||
|
path, |
||||
|
}, |
||||
|
transformer, |
||||
|
schema, |
||||
|
readable, |
||||
|
queryVerb, |
||||
|
} |
||||
|
|
||||
|
return query |
||||
|
} |
||||
|
|
||||
|
export const getDatasourceInfo = (data: string): DatasourceInfo => { |
||||
|
const strategy = parseImportStrategy(data) |
||||
|
|
||||
|
let info: DatasourceInfo |
||||
|
switch (strategy) { |
||||
|
case Strategy.SWAGGER2: |
||||
|
info = parseSwagger2Info(JSON.parse(data)) |
||||
|
break |
||||
|
case Strategy.OPENAPI3: |
||||
|
info = parseOpenAPI3Info(JSON.parse(data)) |
||||
|
break |
||||
|
case Strategy.CURL: |
||||
|
info = parseCurlDatasourceInfo(data) |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
return info |
||||
|
} |
||||
|
|
||||
|
export const importQueries = async ( |
||||
|
appId: string, |
||||
|
datasourceId: string, |
||||
|
data: string |
||||
|
): Promise<ImportResult> => { |
||||
|
const strategy = parseImportStrategy(data) |
||||
|
|
||||
|
// constuct the queries
|
||||
|
let queries: Query[] |
||||
|
switch (strategy) { |
||||
|
case Strategy.SWAGGER2: |
||||
|
queries = parseSwagger2Queries(datasourceId, JSON.parse(data)) |
||||
|
break |
||||
|
case Strategy.OPENAPI3: |
||||
|
queries = parseOpenAPI3Queries(datasourceId, JSON.parse(data)) |
||||
|
break |
||||
|
case Strategy.CURL: |
||||
|
queries = parseCurlQueries(datasourceId, data) |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
// validate queries
|
||||
|
const errorQueries = [] |
||||
|
const schema = queryValidation() |
||||
|
queries = queries |
||||
|
.filter(query => { |
||||
|
const validation = schema.validate(query) |
||||
|
if (validation.error) { |
||||
|
errorQueries.push(query) |
||||
|
return false |
||||
|
} |
||||
|
return true |
||||
|
}) |
||||
|
.map(query => { |
||||
|
query._id = generateQueryID(query.datasourceId) |
||||
|
return query |
||||
|
}) |
||||
|
|
||||
|
// persist queries
|
||||
|
const db = new CouchDB(appId) |
||||
|
for (const query of queries) { |
||||
|
try { |
||||
|
await db.put(query) |
||||
|
} catch (error) { |
||||
|
errorQueries.push(query) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
errorQueries, |
||||
|
} |
||||
|
} |
||||
@ -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