Refactoring to TS on public endpoints.

This commit is contained in:
mike12345567 2022-02-24 15:13:14 +00:00
parent 812f69815f
commit 74dcce6b1d
19 changed files with 237 additions and 135 deletions

View File

@ -15,7 +15,15 @@ const application = {
lockedBy: userResource.getExamples().user.value.user,
}
const applicationSchema = object({})
const applicationSchema = object({
name: {
type: "string",
required: true,
},
url: {
type: "string",
},
})
module.exports = new Resource()
.setExamples({

View File

@ -1,40 +1,52 @@
const { search } = require("./utils")
const { getAllApps } = require("@budibase/backend-core/db")
const { updateAppId } = require("@budibase/backend-core/context")
const controller = require("../application")
import { search as stringSearch } from "./utils"
import { default as controller } from "../application"
import { Application } from "../../../definitions/common"
async function setResponseApp(ctx) {
function fixAppID(app: Application, params: any) {
if (!params) {
return app
}
if (!app._id && params.appId) {
app._id = params.appId
}
return app
}
async function setResponseApp(ctx: any) {
if (ctx.body && ctx.body.appId && (!ctx.params || !ctx.params.appId)) {
ctx.params = { appId: ctx.body.appId }
}
await controller.fetchAppPackage(ctx)
}
exports.search = async ctx => {
export async function search(ctx: any) {
const { name } = ctx.request.body
const apps = await getAllApps({ all: true })
ctx.body = {
applications: search(apps, name),
applications: stringSearch(apps, name),
}
}
exports.create = async ctx => {
export async function create(ctx: any) {
await controller.create(ctx)
await setResponseApp(ctx)
}
exports.read = async ctx => {
export async function read(ctx: any) {
updateAppId(ctx.params.appId)
await setResponseApp(ctx)
}
exports.update = async ctx => {
export async function update(ctx: any) {
ctx.request.body = fixAppID(ctx.request.body, ctx.params)
updateAppId(ctx.params.appId)
await controller.update(ctx)
await setResponseApp(ctx)
}
exports.delete = async ctx => {
export async function destroy(ctx: any) {
updateAppId(ctx.params.appId)
// get the app before deleting it
await setResponseApp(ctx)
@ -43,3 +55,11 @@ exports.delete = async ctx => {
// overwrite the body again
ctx.body = body
}
export default {
create,
update,
read,
destroy,
search,
}

View File

@ -1,14 +0,0 @@
const { search } = require("./utils")
const queryController = require("../query")
exports.search = async ctx => {
await queryController.fetch(ctx)
const { name } = ctx.request.body
ctx.body = {
queries: search(ctx.body, name),
}
}
exports.execute = async ctx => {
await queryController.executeV2(ctx)
}

View File

@ -0,0 +1,19 @@
import { search as stringSearch } from "./utils"
import { default as queryController } from "../query"
export async function search(ctx: any) {
await queryController.fetch(ctx)
const { name } = ctx.request.body
ctx.body = {
queries: stringSearch(ctx.body, name),
}
}
export async function execute(ctx: any) {
await queryController.executeV2(ctx)
}
export default {
search,
execute,
}

View File

@ -1,9 +1,10 @@
const rowController = require("../row")
const { addRev } = require("./utils")
import { default as rowController } from "../row"
import { addRev } from "./utils"
import { Row } from "../../../definitions/common"
// makes sure that the user doesn't need to pass in the type, tableId or _id params for
// the call to be correct
function fixRow(row, params) {
function fixRow(row: Row, params: any) {
if (!params || !row) {
return row
}
@ -19,27 +20,41 @@ function fixRow(row, params) {
return row
}
exports.search = async ctx => {
export async function search(ctx: any) {
let { sort, paginate, bookmark, limit, query } = ctx.request.body
// update the body to the correct format of the internal search
if (!sort) {
sort = {}
}
ctx.request.body = {
sort: sort.column,
sortType: sort.type,
sortOrder: sort.order,
bookmark,
paginate,
limit,
query,
}
await rowController.search(ctx)
}
exports.create = async ctx => {
export async function create(ctx: any) {
ctx.request.body = fixRow(ctx.request.body, ctx.params)
await rowController.save(ctx)
ctx.body = { row: ctx.body }
}
exports.read = async ctx => {
export async function read(ctx: any) {
await rowController.find(ctx)
ctx.body = { row: ctx.body }
}
exports.update = async ctx => {
export async function update(ctx: any) {
ctx.request.body = await addRev(fixRow(ctx.request.body, ctx.params.tableId))
ctx.body = { row: ctx.body }
}
exports.delete = async ctx => {
export async function destroy(ctx: any) {
// set the body as expected, with the _id and _rev fields
ctx.request.body = await addRev(fixRow({}, ctx.params.tableId))
await rowController.destroy(ctx)
@ -47,3 +62,11 @@ exports.delete = async ctx => {
// in the public API to be correct
ctx.body = { row: ctx.row }
}
export default {
create,
read,
update,
destroy,
search,
}

View File

@ -1,31 +1,39 @@
const { search, addRev } = require("./utils")
const controller = require("../table")
import { search as stringSearch, addRev } from "./utils"
import { default as controller } from "../table"
exports.search = async ctx => {
export async function search(ctx: any) {
const { name } = ctx.request.body
await controller.fetch(ctx)
ctx.body = {
tables: search(ctx.body, name),
tables: stringSearch(ctx.body, name),
}
}
exports.create = async ctx => {
export async function create(ctx: any) {
await controller.save(ctx)
ctx.body = { table: ctx.body }
}
exports.read = async ctx => {
export async function read(ctx: any) {
await controller.find(ctx)
ctx.body = { table: ctx.body }
}
exports.update = async ctx => {
export async function update(ctx: any) {
ctx.request.body = await addRev(ctx.request.body, ctx.params.tableId)
await controller.save(ctx)
ctx.body = { table: ctx.body }
}
exports.delete = async ctx => {
export async function destroy(ctx: any) {
await controller.destroy(ctx)
ctx.body = { table: ctx.table }
}
export default {
create,
read,
update,
destroy,
search,
}

View File

@ -1,9 +0,0 @@
exports.search = () => {}
exports.create = () => {}
exports.read = () => {}
exports.update = () => {}
exports.delete = () => {}

View File

@ -0,0 +1,17 @@
export async function search() {}
export async function create() {}
export async function read() {}
export async function update() {}
export async function destroy() {}
export default {
create,
read,
update,
destroy,
search,
}

View File

@ -1,8 +1,11 @@
const { getAppDB } = require("@budibase/backend-core/context")
const { isExternalTable } = require("../../../integrations/utils")
import { isExternalTable } from "../../../integrations/utils"
exports.addRev = async (body, tableId) => {
if (!body._id || isExternalTable(tableId)) {
export async function addRev(
body: { _id?: string; _rev?: string },
tableId?: string
) {
if (!body._id || (tableId && isExternalTable(tableId))) {
return body
}
const db = getAppDB()
@ -16,7 +19,7 @@ exports.addRev = async (body, tableId) => {
* provided key and value. This will be a string based search, using the
* startsWith function.
*/
exports.search = (docs, value, key = "name") => {
export function search(docs: any[], value: any, key = "name") {
if (!value || typeof value !== "string") {
return docs
}

View File

@ -1,6 +1,6 @@
const controller = require("../../controllers/public/applications")
const Endpoint = require("./utils/Endpoint")
const { nameValidator } = require("../utils/validators")
import controller from "../../controllers/public/applications"
import Endpoint from "./utils/Endpoint"
const { nameValidator, applicationValidator } = require("../utils/validators")
const read = [],
write = []
@ -118,7 +118,7 @@ write.push(new Endpoint("put", "/applications/:appId", controller.update))
* application:
* $ref: '#/components/examples/application'
*/
write.push(new Endpoint("delete", "/applications/:appId", controller.delete))
write.push(new Endpoint("delete", "/applications/:appId", controller.destroy))
/**
* @openapi
@ -142,4 +142,4 @@ write.push(new Endpoint("delete", "/applications/:appId", controller.delete))
*/
read.push(new Endpoint("get", "/applications/:appId", controller.read))
module.exports = { read, write }
export default { read, write }

View File

@ -1,15 +1,13 @@
const appEndpoints = require("./applications")
const queryEndpoints = require("./queries")
const tableEndpoints = require("./tables")
const rowEndpoints = require("./rows")
const userEndpoints = require("./users")
import appEndpoints from "./applications"
import queryEndpoints from "./queries"
import tableEndpoints from "./tables"
import rowEndpoints from "./rows"
import userEndpoints from "./users"
import usage from "../../../middleware/usageQuota"
import authorized from "../../../middleware/authorized"
import { paramResource, paramSubResource } from "../../../middleware/resourceId"
import { CtxFn } from "./utils/Endpoint"
const Router = require("@koa/router")
const usage = require("../../../middleware/usageQuota")
const authorized = require("../../../middleware/authorized")
const {
paramResource,
paramSubResource,
} = require("../../../middleware/resourceId")
const {
PermissionLevels,
PermissionTypes,
@ -21,7 +19,7 @@ const publicRouter = new Router({
prefix: PREFIX,
})
function addMiddleware(endpoints, middleware) {
function addMiddleware(endpoints: any, middleware: CtxFn) {
if (!Array.isArray(endpoints)) {
endpoints = [endpoints]
}
@ -30,13 +28,18 @@ function addMiddleware(endpoints, middleware) {
}
}
function addToRouter(endpoints) {
function addToRouter(endpoints: any) {
for (let endpoint of endpoints) {
endpoint.apply(publicRouter)
}
}
function applyRoutes(endpoints, permType, resource, subResource = null) {
function applyRoutes(
endpoints: any,
permType: string,
resource: string,
subResource?: string
) {
const paramMiddleware = subResource
? paramSubResource(resource, subResource)
: paramResource(resource)
@ -53,6 +56,7 @@ applyRoutes(appEndpoints, PermissionTypes.APP, "appId")
applyRoutes(tableEndpoints, PermissionTypes.TABLE, "tableId")
applyRoutes(userEndpoints, PermissionTypes.USER, "userId")
applyRoutes(queryEndpoints, PermissionTypes.QUERY, "queryId")
//applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
// needs to be applied last for routing purposes, don't override other endpoints
applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
module.exports = publicRouter

View File

@ -1,6 +1,6 @@
const controller = require("../../controllers/public/queries")
const Endpoint = require("./utils/Endpoint")
const { nameValidator } = require("../utils/validators")
import controller from "../../controllers/public/queries"
import Endpoint from "./utils/Endpoint"
import { nameValidator } from "../utils/validators"
const read = [],
write = []
@ -89,4 +89,4 @@ read.push(
*/
write.push(new Endpoint("post", "/queries/:queryId", controller.execute))
module.exports = { read, write }
export default { read, write }

View File

@ -1,5 +1,6 @@
const controller = require("../../controllers/public/rows")
const Endpoint = require("./utils/Endpoint")
import controller from "../../controllers/public/rows"
import Endpoint from "./utils/Endpoint"
import { externalSearchValidator } from "../utils/validators"
const read = [],
write = []
@ -121,7 +122,11 @@ const read = [],
* $ref: '#/components/examples/rows'
*/
read.push(
new Endpoint("post", "/tables/:tableId/rows/search", controller.search)
new Endpoint(
"post",
"/tables/:tableId/rows/search",
controller.search
).addMiddleware(externalSearchValidator())
)
/**
@ -215,7 +220,7 @@ write.push(
* $ref: '#/components/examples/row'
*/
write.push(
new Endpoint("delete", "/tables/:tableId/rows/:rowId", controller.delete)
new Endpoint("delete", "/tables/:tableId/rows/:rowId", controller.destroy)
)
/**
@ -242,4 +247,4 @@ write.push(
*/
read.push(new Endpoint("get", "/tables/:tableId/rows/:rowId", controller.read))
module.exports = { read, write }
export default { read, write }

View File

@ -1,6 +1,6 @@
const controller = require("../../controllers/public/tables")
const Endpoint = require("./utils/Endpoint")
const { tableValidator, nameValidator } = require("../utils/validators")
import controller from "../../controllers/public/tables"
import Endpoint from "./utils/Endpoint"
import { tableValidator, nameValidator } from "../utils/validators"
const read = [],
write = []
@ -133,7 +133,7 @@ write.push(
* table:
* $ref: '#/components/examples/table'
*/
write.push(new Endpoint("delete", "/tables/:tableId", controller.delete))
write.push(new Endpoint("delete", "/tables/:tableId", controller.destroy))
/**
* @openapi
@ -158,4 +158,4 @@ write.push(new Endpoint("delete", "/tables/:tableId", controller.delete))
*/
read.push(new Endpoint("get", "/tables/:tableId", controller.read))
module.exports = { read, write }
export default { read, write }

View File

@ -1,6 +1,6 @@
const controller = require("../../controllers/public/users")
const Endpoint = require("./utils/Endpoint")
const { nameValidator } = require("../utils/validators")
import controller from "../../controllers/public/users"
import Endpoint from "./utils/Endpoint"
import { nameValidator } from "../utils/validators"
const read = [],
write = []
@ -117,7 +117,7 @@ write.push(new Endpoint("put", "/users/:userId", controller.update))
* user:
* $ref: '#/components/examples/user'
*/
write.push(new Endpoint("delete", "/users/:userId", controller.delete))
write.push(new Endpoint("delete", "/users/:userId", controller.destroy))
/**
* @openapi
@ -142,4 +142,4 @@ write.push(new Endpoint("delete", "/users/:userId", controller.delete))
*/
read.push(new Endpoint("get", "/users/:userId", controller.read))
module.exports = { read, write }
export default { read, write }

View File

@ -1,24 +1,34 @@
import Router from "koa-router"
export type CtxFn = (ctx: any) => void
class Endpoint {
constructor(method, url, controller) {
method: string
url: string
controller: CtxFn
middlewares: CtxFn[]
constructor(method: string, url: string, controller: CtxFn) {
this.method = method
this.url = url
this.controller = controller
this.middlewares = []
}
addMiddleware(middleware) {
addMiddleware(middleware: CtxFn) {
this.middlewares.push(middleware)
return this
}
apply(router) {
apply(router: Router) {
const method = this.method,
url = this.url
const middlewares = this.middlewares,
controller = this.controller
const params = [url, ...middlewares, controller]
// @ts-ignore
router[method](...params)
}
}
module.exports = Endpoint
export default Endpoint

View File

@ -10,6 +10,7 @@ const {
PermissionLevels,
PermissionTypes,
} = require("@budibase/backend-core/permissions")
const { internalSearchValidator } = require("./utils/validators")
const router = Router()
@ -138,6 +139,7 @@ router
*/
.post(
"/api/:tableId/search",
internalSearchValidator(),
paramResource("tableId"),
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
rowController.search

View File

@ -47,42 +47,49 @@ exports.datasourceValidator = () => {
}).unknown(true))
}
/**
* * {
* "tableId": "ta_70260ff0b85c467ca74364aefc46f26d",
* "query": {
* "string": {},
* "fuzzy": {},
* "range": {
* "columnName": {
* "high": 20,
* "low": 10,
* }
* },
* "equal": {
* "columnName": "someValue"
* },
* "notEqual": {},
* "empty": {},
* "notEmpty": {},
* "oneOf": {
* "columnName": ["value"]
* }
* },
* "limit": 10,
* "sort": "name",
* "sortOrder": "descending",
* "sortType": "string",
* "paginate": true
* }
*/
exports.searchValidator = () => {
function filterObject() {
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(),
})
}
exports.internalSearchValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
tableId: Joi.string()
tableId: Joi.string(),
query: filterObject(),
limit: Joi.number().optional(),
sort: Joi.string().optional(),
sortOrder: Joi.string().optional(),
sortType: Joi.string().optional(),
paginate: Joi.boolean().optional(),
bookmark: Joi.alternatives().try(Joi.string(), Joi.number()).optional(),
}))
}
exports.externalSearchValidator = () => {
return joiValidator.body(
Joi.object({
query: filterObject(),
paginate: Joi.boolean().optional(),
bookmark: Joi.alternatives().try(Joi.string(), Joi.number()).optional(),
limit: Joi.number().optional(),
sort: Joi.object({
column: Joi.string(),
order: Joi.string().optional().valid("ascending", "descending"),
type: Joi.string().valid("string", "number"),
}).optional(),
})
)
}
exports.datasourceQueryValidator = () => {
// prettier-ignore
return joiValidator.body(Joi.object({
@ -96,14 +103,7 @@ exports.datasourceQueryValidator = () => {
}).optional(),
body: Joi.object().optional(),
sort: Joi.object().optional(),
filters: Joi.object({
string: Joi.object().optional(),
range: Joi.object().optional(),
equal: Joi.object().optional(),
notEqual: Joi.object().optional(),
empty: Joi.object().optional(),
notEmpty: Joi.object().optional(),
}).optional(),
filters: filterObject().optional(),
paginate: Joi.object({
page: Joi.string().alphanum().optional(),
limit: Joi.number().optional(),
@ -201,3 +201,5 @@ exports.automationValidator = (existing = false) => {
}).required().unknown(true),
}).unknown(true))
}
exports.applicationValidator = () => {}

View File

@ -5,6 +5,10 @@ export interface Base {
_rev?: string
}
export interface Application extends Base {
appId?: string
}
export interface FieldSchema {
// TODO: replace with field types enum when done
type: string