1
0
Fork 0
mirror of synced 2024-05-18 11:23:28 +12:00
budibase/packages/server/src/api/routes/utils/validators.js
2022-11-17 14:59:18 +00:00

245 lines
7.2 KiB
JavaScript

const { joiValidator } = require("@budibase/backend-core/auth")
const { DataSourceOperation } = require("../../../constants")
const {
BuiltinPermissionID,
PermissionLevel,
} = require("@budibase/backend-core/permissions")
const { WebhookActionType } = require("@budibase/types")
const Joi = require("joi")
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
const OPTIONAL_NUMBER = Joi.number().optional().allow(null)
const OPTIONAL_BOOLEAN = Joi.boolean().optional().allow(null)
const APP_NAME_REGEX = /^[\w\s]+$/
exports.tableValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
type: OPTIONAL_STRING.valid("table", "internal", "external"),
primaryDisplay: OPTIONAL_STRING,
schema: Joi.object().required(),
name: Joi.string().required(),
views: Joi.object(),
dataImport: Joi.object(),
}).unknown(true))
}
exports.nameValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
name: OPTIONAL_STRING,
}))
}
exports.datasourceValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
_id: Joi.string(),
_rev: Joi.string(),
type: OPTIONAL_STRING.allow("datasource_plus"),
relationships: Joi.array().items(Joi.object({
from: Joi.string().required(),
to: Joi.string().required(),
cardinality: Joi.valid("1:N", "1:1", "N:N").required()
})),
}).unknown(true))
}
function filterObject() {
// prettier-ignore
return Joi.object({
string: Joi.object().optional(),
fuzzy: Joi.object().optional(),
range: Joi.object().optional(),
equal: Joi.object().optional(),
notEqual: Joi.object().optional(),
empty: Joi.object().optional(),
notEmpty: Joi.object().optional(),
oneOf: Joi.object().optional(),
contains: Joi.object().optional(),
notContains: Joi.object().optional(),
allOr: Joi.boolean().optional(),
}).unknown(true)
}
exports.internalSearchValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
tableId: OPTIONAL_STRING,
query: filterObject(),
limit: OPTIONAL_NUMBER,
sort: OPTIONAL_STRING,
sortOrder: OPTIONAL_STRING,
sortType: OPTIONAL_STRING,
paginate: Joi.boolean(),
bookmark: Joi.alternatives().try(OPTIONAL_STRING, OPTIONAL_NUMBER).optional(),
}))
}
exports.externalSearchValidator = () => {
return joiValidator.body(
Joi.object({
query: filterObject(),
paginate: Joi.boolean().optional(),
bookmark: Joi.alternatives()
.try(OPTIONAL_STRING, OPTIONAL_NUMBER)
.optional(),
limit: OPTIONAL_NUMBER,
sort: Joi.object({
column: Joi.string(),
order: OPTIONAL_STRING.valid("ascending", "descending"),
type: OPTIONAL_STRING.valid("string", "number"),
}).optional(),
})
)
}
exports.datasourceQueryValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
endpoint: Joi.object({
datasourceId: Joi.string().required(),
operation: Joi.string().required().valid(...Object.values(DataSourceOperation)),
entityId: Joi.string().required(),
}).required(),
resource: Joi.object({
fields: Joi.array().items(Joi.string()).optional(),
}).optional(),
body: Joi.object().optional(),
sort: Joi.object().optional(),
filters: filterObject().optional(),
paginate: Joi.object({
page: Joi.string().alphanum().optional(),
limit: Joi.number().optional(),
}).optional(),
}))
}
exports.webhookValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
live: Joi.bool(),
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
name: Joi.string().required(),
bodySchema: Joi.object().optional(),
action: Joi.object({
type: Joi.string().required().valid(WebhookActionType.AUTOMATION),
target: Joi.string().required(),
}).required(),
}).unknown(true))
}
exports.roleValidator = () => {
const permLevelArray = Object.values(PermissionLevel)
// prettier-ignore
return joiValidator.body(Joi.object({
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
name: Joi.string().required(),
// this is the base permission ID (for now a built in)
permissionId: Joi.string().valid(...Object.values(BuiltinPermissionID)).required(),
permissions: Joi.object()
.pattern(/.*/, [Joi.string().valid(...permLevelArray)])
.optional(),
inherits: OPTIONAL_STRING,
}).unknown(true))
}
exports.permissionValidator = () => {
const permLevelArray = Object.values(PermissionLevel)
// prettier-ignore
return joiValidator.params(Joi.object({
level: Joi.string().valid(...permLevelArray).required(),
resourceId: Joi.string(),
roleId: Joi.string(),
}).unknown(true))
}
exports.screenValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
name: Joi.string().required(),
showNavigation: OPTIONAL_BOOLEAN,
width: OPTIONAL_STRING,
routing: Joi.object({
route: Joi.string().required(),
roleId: Joi.string().required().allow(""),
homeScreen: OPTIONAL_BOOLEAN,
}).required().unknown(true),
props: Joi.object({
_id: Joi.string().required(),
_component: Joi.string().required(),
_children: Joi.array().required(),
_styles: Joi.object().required(),
type: OPTIONAL_STRING,
table: OPTIONAL_STRING,
layoutId: OPTIONAL_STRING,
}).required().unknown(true),
}).unknown(true))
}
function generateStepSchema(allowStepTypes) {
// prettier-ignore
return Joi.object({
stepId: Joi.string().required(),
id: Joi.string().required(),
description: Joi.string().required(),
name: Joi.string().required(),
tagline: Joi.string().required(),
icon: Joi.string().required(),
params: Joi.object(),
args: Joi.object(),
type: Joi.string().required().valid(...allowStepTypes),
}).unknown(true)
}
exports.automationValidator = (existing = false) => {
// prettier-ignore
return joiValidator.body(Joi.object({
_id: existing ? Joi.string().required() : OPTIONAL_STRING,
_rev: existing ? Joi.string().required() : OPTIONAL_STRING,
name: Joi.string().required(),
type: Joi.string().valid("automation").required(),
definition: Joi.object({
steps: Joi.array().required().items(generateStepSchema(["ACTION", "LOGIC"])),
trigger: generateStepSchema(["TRIGGER"]).allow(null),
}).required().unknown(true),
}).unknown(true))
}
exports.applicationValidator = (opts = { isCreate: true }) => {
// prettier-ignore
const base = {
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
url: OPTIONAL_STRING,
template: Joi.object({
templateString: OPTIONAL_STRING,
})
}
const appNameValidator = Joi.string()
.pattern(new RegExp(APP_NAME_REGEX))
.error(new Error("App name must be letters, numbers and spaces only"))
if (opts.isCreate) {
base.name = appNameValidator.required()
} else {
base.name = appNameValidator.optional()
}
return joiValidator.body(
Joi.object({
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
name: appNameValidator,
url: OPTIONAL_STRING,
template: Joi.object({
templateString: OPTIONAL_STRING,
}).unknown(true),
}).unknown(true)
)
}