type safe schema validation

This commit is contained in:
Martin McKeaveney 2021-01-11 21:01:21 +00:00
parent a48869a2f0
commit f7383f37da
7 changed files with 106 additions and 32 deletions

View File

@ -1,9 +1,14 @@
const handlebars = require("handlebars")
class Query { class Query {
constructor(source, schema, type) { constructor(source, schema, type, queryString) {
this.source = source this.source = source
this.schema = schema this.schema = schema
this.type = type this.type = type
this.queryString = queryString
} }
build(parameters) {} build(parameters) {
this.queryStr
}
} }

View File

@ -136,7 +136,7 @@
<Select secondary bind:value={query.queryType}> <Select secondary bind:value={query.queryType}>
<option value={''}>Select an option</option> <option value={''}>Select an option</option>
{#each Object.keys(config) as queryType} {#each Object.keys(config) as queryType}
<option value={queryType}>{queryType}</option> <option value={config[queryType].type}>{queryType}</option>
{/each} {/each}
</Select> </Select>

View File

@ -5,6 +5,8 @@
const QueryTypes = { const QueryTypes = {
SQL: "sql", SQL: "sql",
JSON: "json",
FIELDS: "fields",
} }
export let query export let query
@ -19,4 +21,6 @@
<Spacer large /> <Spacer large />
<TextArea bind:value={query.queryString} /> <TextArea bind:value={query.queryString} />
<!-- <Editor label="Query" on:change={updateQuery} value={query.queryString} /> --> <!-- <Editor label="Query" on:change={updateQuery} value={query.queryString} /> -->
{/if} {:else if query.queryType === QueryTypes.JSON}
{:else if query.queryType === QueryTypes.FIELDS}{/if}

View File

@ -1,20 +1,7 @@
const handlebars = require("handlebars") const handlebars = require("handlebars")
const Joi = require("joi")
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { generateQueryID, getQueryParams } = require("../../db/utils") const { generateQueryID, getQueryParams } = require("../../db/utils")
const { integrations } = require("../../integrations") const { integrations } = require("../../integrations")
const joiValidator = require("../../middleware/joi-validator")
function generateQueryValidation() {
// prettier-ignore
return joiValidator.body(Joi.object({
name: Joi.string().required(),
queryString: Joi.string().required(),
datasourceId: Joi.string().required(),
queryType: Joi.string().required(),
schema: Joi.object({}).required().unknown(true)
}))
}
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
@ -31,13 +18,6 @@ exports.save = async function(ctx) {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
const query = ctx.request.body const query = ctx.request.body
//
// {
// type: "",
// query: "",
// otherStuff: ""
// }
if (!query._id) { if (!query._id) {
query._id = generateQueryID(query.datasourceId) query._id = generateQueryID(query.datasourceId)
} }

View File

@ -2,15 +2,69 @@ const Router = require("@koa/router")
const queryController = require("../controllers/query") const queryController = require("../controllers/query")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/security/permissions") const { BUILDER } = require("../../utilities/security/permissions")
const Joi = require("joi")
const {
PermissionLevels,
PermissionTypes,
} = require("../../utilities/security/permissions")
const joiValidator = require("../../middleware/joi-validator")
const router = Router() const router = Router()
const QueryVerb = {
Create: "CREATE",
Read: "READ",
Update: "UPDATE",
Delete: "DELETE",
}
function generateQueryValidation() {
// prettier-ignore
return joiValidator.body(Joi.object({
_id: Joi.string(),
_rev: Joi.string(),
name: Joi.string().required(),
queryString: Joi.string().required(),
datasourceId: Joi.string().required(),
parameters: Joi.array().items(Joi.object({
name: Joi.string(),
default: Joi.string()
})),
// queryVerb: Joi.string().allow(...Object.values(QueryVerb)).required(),
queryType: Joi.string().required(),
schema: Joi.object({}).required().unknown(true)
}))
}
function generateQueryPreviewValidation() {
// prettier-ignore
return joiValidator.body(Joi.object({
query: Joi.string().required(),
datasourceId: Joi.string().required(),
parameters: Joi.object({}).required().unknown(true)
}))
}
// TODO: sort out auth so apps have the right permissions // TODO: sort out auth so apps have the right permissions
router router
.get("/api/queries", authorized(BUILDER), queryController.fetch) .get("/api/queries", authorized(BUILDER), queryController.fetch)
.post("/api/queries", authorized(BUILDER), queryController.save) .post(
.post("/api/queries/preview", authorized(BUILDER), queryController.preview) "/api/queries",
.post("/api/queries/:queryId", authorized(BUILDER), queryController.execute) authorized(BUILDER),
generateQueryValidation(),
queryController.save
)
.post(
"/api/queries/preview",
authorized(BUILDER),
generateQueryPreviewValidation(),
queryController.preview
)
.post(
"/api/queries/:queryId",
authorized(PermissionTypes.QUERY, PermissionLevels.WRITE),
queryController.execute
)
.delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy) .delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy)
module.exports = router module.exports = router

View File

@ -29,14 +29,42 @@ const SCHEMA = {
}, },
}, },
query: { query: {
sql: { SQL: {
type: "sql", type: "sql",
}, },
gui: { "Simple Query": {
type: "config", type: "fields",
fields: { fields: {
something: "", table: {
other: "", type: "string",
},
column: {
type: "string",
},
condition: {
type: "options",
options: [
{
name: "Equals",
value: "=",
},
{
name: "Not Equals",
value: "!=",
},
{
name: "Greater Than",
value: ">",
},
{
name: "Less Than",
value: "<",
},
],
},
value: {
type: "string",
},
}, },
}, },
}, },

View File

@ -58,6 +58,7 @@ exports.BUILTIN_PERMISSIONS = {
_id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY, _id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY,
name: "Read only", name: "Read only",
permissions: [ permissions: [
new Permission(PermissionTypes.QUERY, PermissionLevels.READ),
new Permission(PermissionTypes.TABLE, PermissionLevels.READ), new Permission(PermissionTypes.TABLE, PermissionLevels.READ),
new Permission(PermissionTypes.VIEW, PermissionLevels.READ), new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
], ],
@ -66,6 +67,7 @@ exports.BUILTIN_PERMISSIONS = {
_id: exports.BUILTIN_PERMISSION_IDS.WRITE, _id: exports.BUILTIN_PERMISSION_IDS.WRITE,
name: "Read/Write", name: "Read/Write",
permissions: [ permissions: [
new Permission(PermissionTypes.QUERY, PermissionLevels.WRITE),
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE), new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
new Permission(PermissionTypes.VIEW, PermissionLevels.READ), new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
], ],
@ -90,6 +92,7 @@ exports.BUILTIN_PERMISSIONS = {
new Permission(PermissionTypes.AUTOMATION, PermissionLevels.ADMIN), new Permission(PermissionTypes.AUTOMATION, PermissionLevels.ADMIN),
new Permission(PermissionTypes.VIEW, PermissionLevels.ADMIN), new Permission(PermissionTypes.VIEW, PermissionLevels.ADMIN),
new Permission(PermissionTypes.WEBHOOK, PermissionLevels.READ), new Permission(PermissionTypes.WEBHOOK, PermissionLevels.READ),
new Permission(PermissionTypes.QUERY, PermissionLevels.ADMIN),
], ],
}, },
} }