Work in progress - this is the scaffolding for views to be added to the public API.

This commit is contained in:
mike12345567 2024-10-25 17:32:42 +01:00
parent a9279b35d5
commit f8f9c4da8e
4 changed files with 418 additions and 0 deletions

View File

@ -0,0 +1,193 @@
import { FieldType, FormulaType, RelationshipType } from "@budibase/types"
import { object } from "./utils"
import Resource from "./utils/Resource"
const view = {
_id: "ta_5b1649e42a5b41dea4ef7742a36a7a70",
name: "People",
schema: {
name: {
type: "string",
name: "name",
},
age: {
type: "number",
name: "age",
},
relationship: {
type: "link",
name: "relationship",
tableId: "ta_...",
fieldName: "relatedColumn",
relationshipType: "many-to-many",
},
},
}
const baseColumnDef = {
type: {
type: "string",
enum: Object.values(FieldType),
description:
"Defines the type of the column, most explain themselves, a link column is a relationship.",
},
constraints: {
type: "object",
description:
"A constraint can be applied to the column which will be validated against when a row is saved.",
properties: {
type: {
type: "string",
enum: ["string", "number", "object", "boolean"],
},
presence: {
type: "boolean",
description: "Defines whether the column is required or not.",
},
},
},
name: {
type: "string",
description: "The name of the column.",
},
autocolumn: {
type: "boolean",
description: "Defines whether the column is automatically generated.",
},
}
const viewSchema = {
description: "The table to be created/updated.",
type: "object",
required: ["name", "schema"],
properties: {
name: {
description: "The name of the table.",
type: "string",
},
primaryDisplay: {
type: "string",
description:
"The name of the column which should be used in relationship tags when relating to this table.",
},
schema: {
type: "object",
additionalProperties: {
oneOf: [
// relationship
{
type: "object",
properties: {
...baseColumnDef,
type: {
type: "string",
enum: [FieldType.LINK],
description: "A relationship column.",
},
fieldName: {
type: "string",
description:
"The name of the column which a relationship column is related to in another table.",
},
tableId: {
type: "string",
description:
"The ID of the table which a relationship column is related to.",
},
relationshipType: {
type: "string",
enum: Object.values(RelationshipType),
description:
"Defines the type of relationship that this column will be used for.",
},
through: {
type: "string",
description:
"When using a SQL table that contains many to many relationships this defines the table the relationships are linked through.",
},
foreignKey: {
type: "string",
description:
"When using a SQL table that contains a one to many relationship this defines the foreign key.",
},
throughFrom: {
type: "string",
description:
"When using a SQL table that utilises a through table, this defines the primary key in the through table for this table.",
},
throughTo: {
type: "string",
description:
"When using a SQL table that utilises a through table, this defines the primary key in the through table for the related table.",
},
},
},
{
type: "object",
properties: {
...baseColumnDef,
type: {
type: "string",
enum: [FieldType.FORMULA],
description: "A formula column.",
},
formula: {
type: "string",
description:
"Defines a Handlebars or JavaScript formula to use, note that Javascript formulas are expected to be provided in the base64 format.",
},
formulaType: {
type: "string",
enum: Object.values(FormulaType),
description:
"Defines whether this is a static or dynamic formula.",
},
},
},
{
type: "object",
properties: baseColumnDef,
},
],
},
},
},
}
const viewOutputSchema = {
...viewSchema,
properties: {
...viewSchema.properties,
_id: {
description: "The ID of the view.",
type: "string",
},
},
required: [...viewSchema.required, "_id"],
}
export default new Resource()
.setExamples({
view: {
value: {
data: view,
},
},
views: {
value: {
data: [view],
},
},
})
.setSchemas({
view: viewSchema,
viewOutput: object({
data: viewOutputSchema,
}),
viewSearch: object({
data: {
type: "array",
items: viewOutputSchema,
},
}),
})

View File

@ -0,0 +1,56 @@
import { search as stringSearch } from "./utils"
import * as controller from "../view"
import { ViewV2, UserCtx } from "@budibase/types"
import { Next } from "koa"
function fixView(view: ViewV2, params: any) {
if (!params || !view) {
return view
}
if (params.viewId) {
view.id = params.viewId
}
return view
}
export async function search(ctx: UserCtx, next: Next) {
const { name } = ctx.request.body
// TODO: need a view search endpoint
// await controller.v2.fetch(ctx)
ctx.body = stringSearch(ctx.body, name)
await next()
}
export async function create(ctx: UserCtx, next: Next) {
await controller.v2.create(ctx)
await next()
}
export async function read(ctx: UserCtx, next: Next) {
await controller.v2.get(ctx)
await next()
}
export async function update(ctx: UserCtx, next: Next) {
// TODO: this is more complex - no rev on views
// ctx.request.body = await addRev(
// fixView(ctx.request.body, ctx.params),
// ctx.params.tableId
// )
await controller.v2.update(ctx)
await next()
}
export async function destroy(ctx: UserCtx, next: Next) {
await controller.v2.remove(ctx)
ctx.body = ctx.table
await next()
}
export default {
create,
read,
update,
destroy,
search,
}

View File

@ -0,0 +1,165 @@
import controller from "../../controllers/public/views"
import Endpoint from "./utils/Endpoint"
import { viewValidator, nameValidator } from "../utils/validators"
const read = [],
write = []
/**
* @openapi
* /views:
* post:
* operationId: viewCreate
* summary: Create a view
* description: Create a view, this can be against an internal or external table.
* tags:
* - views
* parameters:
* - $ref: '#/components/parameters/appId'
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/view'
* examples:
* view:
* $ref: '#/components/examples/view'
* responses:
* 200:
* description: Returns the created view, including the ID which has been generated for it.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/viewOutput'
* examples:
* view:
* $ref: '#/components/examples/view'
*/
write.push(
new Endpoint("post", "/views", controller.create).addMiddleware(
viewValidator()
)
)
/**
* @openapi
* /views/{viewId}:
* put:
* operationId: viewUpdate
* summary: Update a view
* description: Update a view, this can be against an internal or external table.
* tags:
* - views
* parameters:
* - $ref: '#/components/parameters/viewId'
* - $ref: '#/components/parameters/appId'
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/view'
* examples:
* view:
* $ref: '#/components/examples/view'
* responses:
* 200:
* description: Returns the updated view.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/viewOutput'
* examples:
* view:
* $ref: '#/components/examples/view'
*/
write.push(
new Endpoint("put", "/views/:viewId", controller.update).addMiddleware(
viewValidator()
)
)
/**
* @openapi
* /views/{viewId}:
* delete:
* operationId: viewDestroy
* summary: Delete a view
* description: Delete a view, this can be against an internal or external table.
* tags:
* - views
* parameters:
* - $ref: '#/components/parameters/viewId'
* - $ref: '#/components/parameters/appId'
* responses:
* 200:
* description: Returns the deleted view.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/viewOutput'
* examples:
* view:
* $ref: '#/components/examples/view'
*/
write.push(new Endpoint("delete", "/views/:viewId", controller.destroy))
/**
* @openapi
* /views/{viewId}:
* get:
* operationId: viewGetById
* summary: Retrieve a view
* description: Lookup a view, this could be internal or external.
* tags:
* - views
* parameters:
* - $ref: '#/components/parameters/viewId'
* - $ref: '#/components/parameters/appId'
* responses:
* 200:
* description: Returns the retrieved view.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/viewOutput'
* examples:
* view:
* $ref: '#/components/examples/view'
*/
read.push(new Endpoint("get", "/views/:viewId", controller.read))
/**
* @openapi
* /views/search:
* post:
* operationId: viewSearch
* summary: Search for views
* description: Based on view properties (currently only name) search for views.
* tags:
* - views
* parameters:
* - $ref: '#/components/parameters/appId'
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/viewSearch'
* responses:
* 200:
* description: Returns the found views, based on the search parameters.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/viewSearch'
* examples:
* views:
* $ref: '#/components/examples/views'
*/
read.push(
new Endpoint("post", "/views/search", controller.search).addMiddleware(
nameValidator()
)
)
export default { read, write }

View File

@ -66,6 +66,10 @@ export function tableValidator() {
)
}
export function viewValidator() {
return auth.joiValidator.body(Joi.object())
}
export function nameValidator() {
return auth.joiValidator.body(
Joi.object({