Adding new generation technique, converting openAPI spec to typescript definitions, which are then applied in mapping output middlewares to make sure that the structures of the response are fully respected.
This commit is contained in:
parent
6cc9d2d61c
commit
d8b5cae569
|
@ -25,7 +25,7 @@
|
|||
"generate:proxy:preprod": "node scripts/proxy/generateProxyConfig preprod",
|
||||
"generate:proxy:prod": "node scripts/proxy/generateProxyConfig prod",
|
||||
"format": "prettier --config ../../.prettierrc.json 'src/**/*.ts' --write",
|
||||
"specs": "node specs/generate.js",
|
||||
"specs": "node specs/generate.js && openapi-typescript specs/openapi.yaml --output src/api/controllers/public/types/openapi.ts",
|
||||
"lint": "eslint --fix src/",
|
||||
"lint:fix": "yarn run format && yarn run lint",
|
||||
"initialise": "node scripts/initialise.js",
|
||||
|
@ -161,6 +161,7 @@
|
|||
"jest-openapi": "^0.14.2",
|
||||
"nodemon": "^2.0.4",
|
||||
"openapi-types": "^9.3.1",
|
||||
"openapi-typescript": "^5.2.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"prettier": "^2.3.1",
|
||||
"rimraf": "^3.0.2",
|
||||
|
|
|
@ -445,6 +445,10 @@
|
|||
"description": "The URL by which the app is accessed, this must be URL encoded.",
|
||||
"type": "string"
|
||||
},
|
||||
"_id": {
|
||||
"description": "The ID of the app.",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"description": "The status of the app, stating it if is the development or published version.",
|
||||
"type": "string",
|
||||
|
@ -472,20 +476,16 @@
|
|||
"lockedBy": {
|
||||
"description": "The user this app is currently being built by.",
|
||||
"type": "object"
|
||||
},
|
||||
"appId": {
|
||||
"description": "The ID of the app.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"_id",
|
||||
"name",
|
||||
"url",
|
||||
"status",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
"version",
|
||||
"appId"
|
||||
"version"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -496,16 +496,6 @@
|
|||
"row": {
|
||||
"description": "The row to be created/updated, based on the table schema.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_id": {
|
||||
"description": "The ID of the row.",
|
||||
"type": "string"
|
||||
},
|
||||
"tableId": {
|
||||
"description": "The ID of the table this row comes from.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
@ -526,22 +516,42 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"searchOutput": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "An array of rows, these will each contain an _id field which can be used to update or delete them.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"bookmark": {
|
||||
"description": "If pagination in use, this should be provided.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hasNextPage": {
|
||||
"description": "If pagination in use, this will determine if there is another page to fetch.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rowOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "The row to be created/updated, based on the table schema.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_id": {
|
||||
"description": "The ID of the row.",
|
||||
"type": "string"
|
||||
},
|
||||
"tableId": {
|
||||
"description": "The ID of the table this row comes from.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
@ -560,7 +570,21 @@
|
|||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"_id": {
|
||||
"description": "The ID of the row.",
|
||||
"type": "string"
|
||||
},
|
||||
"tableId": {
|
||||
"description": "The ID of the table this row comes from.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tableId",
|
||||
"_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -576,7 +600,7 @@
|
|||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the table",
|
||||
"description": "The name of the table.",
|
||||
"type": "string"
|
||||
},
|
||||
"primaryDisplay": {
|
||||
|
@ -774,11 +798,12 @@
|
|||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"schema"
|
||||
"schema",
|
||||
"_id"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the table",
|
||||
"description": "The name of the table.",
|
||||
"type": "string"
|
||||
},
|
||||
"primaryDisplay": {
|
||||
|
@ -965,6 +990,10 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_id": {
|
||||
"description": "The ID of the table.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -973,7 +1002,7 @@
|
|||
"data"
|
||||
]
|
||||
},
|
||||
"query": {
|
||||
"executeQuery": {
|
||||
"description": "The query body must contain the required parameters for the query, this depends on query type, setup and bindings.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
|
@ -996,6 +1025,97 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"executeQueryOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "The data response from the query.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"description": "Extra information that is not part of the main data, e.g. headers.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"headers": {
|
||||
"description": "If carrying out a REST request, this will contain the response headers.",
|
||||
"type": "object"
|
||||
},
|
||||
"raw": {
|
||||
"description": "The raw query response, as a string.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
"description": "Extra info from the query in a key-value map, like response times.",
|
||||
"type": "object"
|
||||
},
|
||||
"pagination": {
|
||||
"description": "If pagination is supported, this will contain the bookmark/anchor information for it.",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"query": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_id": {
|
||||
"description": "The ID of the query.",
|
||||
"type": "string"
|
||||
},
|
||||
"datasourceId": {
|
||||
"description": "The ID of the data source the query belongs to.",
|
||||
"type": "string"
|
||||
},
|
||||
"parameters": {
|
||||
"description": "The bindings which are required to perform this query.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"description": "The fields that are used to perform this query, e.g. the sql statement",
|
||||
"type": "object"
|
||||
},
|
||||
"queryVerb": {
|
||||
"description": "The verb that describes this query.",
|
||||
"enum": [
|
||||
"create",
|
||||
"read",
|
||||
"update",
|
||||
"delete"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"description": "The name of the query.",
|
||||
"type": "string"
|
||||
},
|
||||
"schema": {
|
||||
"description": "The schema of the data returned when the query is executed.",
|
||||
"type": "object"
|
||||
},
|
||||
"transformer": {
|
||||
"description": "The JavaScript transformer function, applied after the query responds with data.",
|
||||
"type": "string"
|
||||
},
|
||||
"readable": {
|
||||
"description": "Whether the query has readable data.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"schema",
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1120,11 +1240,16 @@
|
|||
"type": "string",
|
||||
"description": "A map of app ID (production app ID, minus the _dev component) to a role ID, e.g. ADMIN."
|
||||
}
|
||||
},
|
||||
"_id": {
|
||||
"description": "The ID of the user.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"email",
|
||||
"roles"
|
||||
"roles",
|
||||
"_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -1343,7 +1468,10 @@
|
|||
"/queries/{queryId}": {
|
||||
"post": {
|
||||
"summary": "Execute a query",
|
||||
"description": "Queries which have been created within a Budibase app can be executed using this, - queries",
|
||||
"description": "Queries which have been created within a Budibase app can be executed using this,",
|
||||
"tags": [
|
||||
"queries"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/queryId"
|
||||
|
@ -1352,41 +1480,23 @@
|
|||
"$ref": "#/components/parameters/appId"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/executeQuery"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Returns the result of the query execution.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"description": "The data retrieved from the query.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "The structure of the returned data will be an object, if it is just a string then this will be an object containing \"value\"."
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
"type": "object",
|
||||
"description": "For supported query types this returns pagination information.",
|
||||
"properties": {
|
||||
"cursor": {
|
||||
"type": "string",
|
||||
"description": "The pagination cursor location."
|
||||
}
|
||||
}
|
||||
},
|
||||
"raw": {
|
||||
"type": "string",
|
||||
"description": "The raw query response."
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"description": "For REST queries the headers in the response will be returned here."
|
||||
}
|
||||
}
|
||||
"$ref": "#/components/schemas/executeQueryOutput"
|
||||
},
|
||||
"examples": {
|
||||
"REST": {
|
||||
|
@ -1759,34 +1869,7 @@
|
|||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "An array of rows, these will each contain an _id field which can be used to update or delete them.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"bookmark": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
}
|
||||
],
|
||||
"description": "If pagination in use, this should be provided."
|
||||
},
|
||||
"hasNextPage": {
|
||||
"description": "If pagination in use, this will determine if there is another page to fetch.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
"$ref": "#/components/schemas/searchOutput"
|
||||
},
|
||||
"examples": {
|
||||
"search": {
|
||||
|
|
|
@ -314,6 +314,9 @@ components:
|
|||
url:
|
||||
description: The URL by which the app is accessed, this must be URL encoded.
|
||||
type: string
|
||||
_id:
|
||||
description: The ID of the app.
|
||||
type: string
|
||||
status:
|
||||
description: The status of the app, stating it if is the development or
|
||||
published version.
|
||||
|
@ -339,29 +342,19 @@ components:
|
|||
lockedBy:
|
||||
description: The user this app is currently being built by.
|
||||
type: object
|
||||
appId:
|
||||
description: The ID of the app.
|
||||
type: string
|
||||
required:
|
||||
- _id
|
||||
- name
|
||||
- url
|
||||
- status
|
||||
- createdAt
|
||||
- updatedAt
|
||||
- version
|
||||
- appId
|
||||
required:
|
||||
- data
|
||||
row:
|
||||
description: The row to be created/updated, based on the table schema.
|
||||
type: object
|
||||
properties:
|
||||
_id:
|
||||
description: The ID of the row.
|
||||
type: string
|
||||
tableId:
|
||||
description: The ID of the table this row comes from.
|
||||
type: string
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: string
|
||||
|
@ -369,19 +362,32 @@ components:
|
|||
- type: integer
|
||||
- type: array
|
||||
- type: boolean
|
||||
searchOutput:
|
||||
type: object
|
||||
required:
|
||||
- data
|
||||
properties:
|
||||
data:
|
||||
description: An array of rows, these will each contain an _id field which can be
|
||||
used to update or delete them.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
bookmark:
|
||||
description: If pagination in use, this should be provided.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: integer
|
||||
hasNextPage:
|
||||
description: If pagination in use, this will determine if there is another page
|
||||
to fetch.
|
||||
type: boolean
|
||||
rowOutput:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
description: The row to be created/updated, based on the table schema.
|
||||
type: object
|
||||
properties:
|
||||
_id:
|
||||
description: The ID of the row.
|
||||
type: string
|
||||
tableId:
|
||||
description: The ID of the table this row comes from.
|
||||
type: string
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: string
|
||||
|
@ -389,6 +395,16 @@ components:
|
|||
- type: integer
|
||||
- type: array
|
||||
- type: boolean
|
||||
properties:
|
||||
_id:
|
||||
description: The ID of the row.
|
||||
type: string
|
||||
tableId:
|
||||
description: The ID of the table this row comes from.
|
||||
type: string
|
||||
required:
|
||||
- tableId
|
||||
- _id
|
||||
required:
|
||||
- data
|
||||
table:
|
||||
|
@ -399,7 +415,7 @@ components:
|
|||
- schema
|
||||
properties:
|
||||
name:
|
||||
description: The name of the table
|
||||
description: The name of the table.
|
||||
type: string
|
||||
primaryDisplay:
|
||||
type: string
|
||||
|
@ -558,9 +574,10 @@ components:
|
|||
required:
|
||||
- name
|
||||
- schema
|
||||
- _id
|
||||
properties:
|
||||
name:
|
||||
description: The name of the table
|
||||
description: The name of the table.
|
||||
type: string
|
||||
primaryDisplay:
|
||||
type: string
|
||||
|
@ -710,9 +727,12 @@ components:
|
|||
autocolumn:
|
||||
type: boolean
|
||||
description: Defines whether the column is automatically generated.
|
||||
_id:
|
||||
description: The ID of the table.
|
||||
type: string
|
||||
required:
|
||||
- data
|
||||
query:
|
||||
executeQuery:
|
||||
description: The query body must contain the required parameters for the query,
|
||||
this depends on query type, setup and bindings.
|
||||
type: object
|
||||
|
@ -723,6 +743,76 @@ components:
|
|||
- type: integer
|
||||
- type: array
|
||||
- type: boolean
|
||||
executeQueryOutput:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
description: The data response from the query.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
extra:
|
||||
description: Extra information that is not part of the main data, e.g. headers.
|
||||
type: object
|
||||
properties:
|
||||
headers:
|
||||
description: If carrying out a REST request, this will contain the response
|
||||
headers.
|
||||
type: object
|
||||
raw:
|
||||
description: The raw query response, as a string.
|
||||
type: string
|
||||
info:
|
||||
description: Extra info from the query in a key-value map, like response times.
|
||||
type: object
|
||||
pagination:
|
||||
description: If pagination is supported, this will contain the bookmark/anchor
|
||||
information for it.
|
||||
type: object
|
||||
required:
|
||||
- data
|
||||
query:
|
||||
type: object
|
||||
properties:
|
||||
_id:
|
||||
description: The ID of the query.
|
||||
type: string
|
||||
datasourceId:
|
||||
description: The ID of the data source the query belongs to.
|
||||
type: string
|
||||
parameters:
|
||||
description: The bindings which are required to perform this query.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
fields:
|
||||
description: The fields that are used to perform this query, e.g. the sql
|
||||
statement
|
||||
type: object
|
||||
queryVerb:
|
||||
description: The verb that describes this query.
|
||||
enum:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- delete
|
||||
name:
|
||||
description: The name of the query.
|
||||
type: string
|
||||
schema:
|
||||
description: The schema of the data returned when the query is executed.
|
||||
type: object
|
||||
transformer:
|
||||
description: The JavaScript transformer function, applied after the query
|
||||
responds with data.
|
||||
type: string
|
||||
readable:
|
||||
description: Whether the query has readable data.
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
- schema
|
||||
- _id
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -827,9 +917,13 @@ components:
|
|||
type: string
|
||||
description: A map of app ID (production app ID, minus the _dev component) to a
|
||||
role ID, e.g. ADMIN.
|
||||
_id:
|
||||
description: The ID of the user.
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
- roles
|
||||
- _id
|
||||
required:
|
||||
- data
|
||||
nameSearch:
|
||||
|
@ -959,38 +1053,25 @@ paths:
|
|||
post:
|
||||
summary: Execute a query
|
||||
description: Queries which have been created within a Budibase app can be
|
||||
executed using this, - queries
|
||||
executed using this,
|
||||
tags:
|
||||
- queries
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/queryId"
|
||||
- $ref: "#/components/parameters/appId"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/executeQuery"
|
||||
responses:
|
||||
"200":
|
||||
description: Returns the result of the query execution.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
description: The data retrieved from the query.
|
||||
items:
|
||||
type: object
|
||||
description: The structure of the returned data will be an object, if it is just
|
||||
a string then this will be an object containing "value".
|
||||
pagination:
|
||||
type: object
|
||||
description: For supported query types this returns pagination information.
|
||||
properties:
|
||||
cursor:
|
||||
type: string
|
||||
description: The pagination cursor location.
|
||||
raw:
|
||||
type: string
|
||||
description: The raw query response.
|
||||
headers:
|
||||
type: object
|
||||
description: For REST queries the headers in the response will be returned here.
|
||||
$ref: "#/components/schemas/executeQueryOutput"
|
||||
examples:
|
||||
REST:
|
||||
$ref: "#/components/examples/restResponse"
|
||||
|
@ -1233,25 +1314,7 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- data
|
||||
properties:
|
||||
data:
|
||||
description: An array of rows, these will each contain an _id field which can be
|
||||
used to update or delete them.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
bookmark:
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: integer
|
||||
description: If pagination in use, this should be provided.
|
||||
hasNextPage:
|
||||
description: If pagination in use, this will determine if there is another page
|
||||
to fetch.
|
||||
type: boolean
|
||||
$ref: "#/components/schemas/searchOutput"
|
||||
examples:
|
||||
search:
|
||||
$ref: "#/components/examples/rows"
|
||||
|
|
|
@ -29,9 +29,13 @@ const base = {
|
|||
|
||||
const applicationSchema = object(base, { required: ["name", "url"] })
|
||||
|
||||
const applicationSchemaOutput = object(
|
||||
const applicationOutputSchema = object(
|
||||
{
|
||||
...base,
|
||||
_id: {
|
||||
description: "The ID of the app.",
|
||||
type: "string",
|
||||
},
|
||||
status: {
|
||||
description:
|
||||
"The status of the app, stating it if is the development or published version.",
|
||||
|
@ -62,20 +66,16 @@ const applicationSchemaOutput = object(
|
|||
description: "The user this app is currently being built by.",
|
||||
type: "object",
|
||||
},
|
||||
appId: {
|
||||
description: "The ID of the app.",
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
required: [
|
||||
"_id",
|
||||
"name",
|
||||
"url",
|
||||
"status",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
"version",
|
||||
"appId",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
@ -96,6 +96,6 @@ module.exports = new Resource()
|
|||
.setSchemas({
|
||||
application: applicationSchema,
|
||||
applicationOutput: object({
|
||||
data: applicationSchemaOutput,
|
||||
data: applicationOutputSchema,
|
||||
}),
|
||||
})
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const Resource = require("./utils/Resource")
|
||||
const { object } = require("./utils")
|
||||
const { BaseQueryVerbs } = require("../../src/constants")
|
||||
|
||||
const query = {
|
||||
_id: "query_datasource_plus_4d8be0c506b9465daf4bf84d890fdab6_454854487c574d45bc4029b1e153219e",
|
||||
|
@ -73,7 +75,55 @@ const sqlResponse = {
|
|||
},
|
||||
}
|
||||
|
||||
const querySchema = {
|
||||
const querySchema = object(
|
||||
{
|
||||
_id: {
|
||||
description: "The ID of the query.",
|
||||
type: "string",
|
||||
},
|
||||
datasourceId: {
|
||||
description: "The ID of the data source the query belongs to.",
|
||||
type: "string",
|
||||
},
|
||||
parameters: {
|
||||
description: "The bindings which are required to perform this query.",
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
description:
|
||||
"The fields that are used to perform this query, e.g. the sql statement",
|
||||
type: "object",
|
||||
},
|
||||
queryVerb: {
|
||||
description: "The verb that describes this query.",
|
||||
enum: Object.values(BaseQueryVerbs),
|
||||
},
|
||||
name: {
|
||||
description: "The name of the query.",
|
||||
type: "string",
|
||||
},
|
||||
schema: {
|
||||
description:
|
||||
"The schema of the data returned when the query is executed.",
|
||||
type: "object",
|
||||
},
|
||||
transformer: {
|
||||
description:
|
||||
"The JavaScript transformer function, applied after the query responds with data.",
|
||||
type: "string",
|
||||
},
|
||||
readable: {
|
||||
description: "Whether the query has readable data.",
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
{ required: ["name", "schema", "_id"] }
|
||||
)
|
||||
|
||||
const executeQuerySchema = {
|
||||
description:
|
||||
"The query body must contain the required parameters for the query, this depends on query type, setup and bindings.",
|
||||
type: "object",
|
||||
|
@ -88,6 +138,45 @@ const querySchema = {
|
|||
},
|
||||
}
|
||||
|
||||
const executeQueryOutputSchema = object(
|
||||
{
|
||||
data: {
|
||||
description: "The data response from the query.",
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
},
|
||||
},
|
||||
extra: {
|
||||
description:
|
||||
"Extra information that is not part of the main data, e.g. headers.",
|
||||
type: "object",
|
||||
properties: {
|
||||
headers: {
|
||||
description:
|
||||
"If carrying out a REST request, this will contain the response headers.",
|
||||
type: "object",
|
||||
},
|
||||
raw: {
|
||||
description: "The raw query response, as a string.",
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
info: {
|
||||
description:
|
||||
"Extra info from the query in a key-value map, like response times.",
|
||||
type: "object",
|
||||
},
|
||||
pagination: {
|
||||
description:
|
||||
"If pagination is supported, this will contain the bookmark/anchor information for it.",
|
||||
type: "object",
|
||||
},
|
||||
},
|
||||
{ required: ["data"] }
|
||||
)
|
||||
|
||||
module.exports = new Resource()
|
||||
.setExamples({
|
||||
query: {
|
||||
|
@ -104,5 +193,7 @@ module.exports = new Resource()
|
|||
sqlResponse,
|
||||
})
|
||||
.setSchemas({
|
||||
executeQuery: executeQuerySchema,
|
||||
executeQueryOutput: executeQueryOutputSchema,
|
||||
query: querySchema,
|
||||
})
|
||||
|
|
|
@ -47,7 +47,21 @@ const enrichedRow = {
|
|||
const rowSchema = {
|
||||
description: "The row to be created/updated, based on the table schema.",
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
oneOf: [
|
||||
{ type: "string" },
|
||||
{ type: "object" },
|
||||
{ type: "integer" },
|
||||
{ type: "array" },
|
||||
{ type: "boolean" },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const rowOutputSchema = {
|
||||
...rowSchema,
|
||||
properties: {
|
||||
...rowSchema.properties,
|
||||
_id: {
|
||||
description: "The ID of the row.",
|
||||
type: "string",
|
||||
|
@ -57,14 +71,30 @@ const rowSchema = {
|
|||
type: "string",
|
||||
},
|
||||
},
|
||||
additionalProperties: {
|
||||
oneOf: [
|
||||
{ type: "string" },
|
||||
{ type: "object" },
|
||||
{ type: "integer" },
|
||||
{ type: "array" },
|
||||
{ type: "boolean" },
|
||||
],
|
||||
required: ["tableId", "_id"],
|
||||
}
|
||||
|
||||
const searchOutputSchema = {
|
||||
type: "object",
|
||||
required: ["data"],
|
||||
properties: {
|
||||
data: {
|
||||
description:
|
||||
"An array of rows, these will each contain an _id field which can be used to update or delete them.",
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
},
|
||||
},
|
||||
bookmark: {
|
||||
description: "If pagination in use, this should be provided.",
|
||||
oneOf: [{ type: "string" }, { type: "integer" }],
|
||||
},
|
||||
hasNextPage: {
|
||||
description:
|
||||
"If pagination in use, this will determine if there is another page to fetch.",
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -93,7 +123,8 @@ module.exports = new Resource()
|
|||
})
|
||||
.setSchemas({
|
||||
row: rowSchema,
|
||||
searchOutput: searchOutputSchema,
|
||||
rowOutput: object({
|
||||
data: rowSchema,
|
||||
data: rowOutputSchema,
|
||||
}),
|
||||
})
|
||||
|
|
|
@ -66,7 +66,7 @@ const tableSchema = {
|
|||
required: ["name", "schema"],
|
||||
properties: {
|
||||
name: {
|
||||
description: "The name of the table",
|
||||
description: "The name of the table.",
|
||||
type: "string",
|
||||
},
|
||||
primaryDisplay: {
|
||||
|
@ -158,6 +158,18 @@ const tableSchema = {
|
|||
},
|
||||
}
|
||||
|
||||
const tableOutputSchema = {
|
||||
...tableSchema,
|
||||
properties: {
|
||||
...tableSchema.properties,
|
||||
_id: {
|
||||
description: "The ID of the table.",
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
required: [...tableSchema.required, "_id"],
|
||||
}
|
||||
|
||||
module.exports = new Resource()
|
||||
.setExamples({
|
||||
table: {
|
||||
|
@ -174,6 +186,6 @@ module.exports = new Resource()
|
|||
.setSchemas({
|
||||
table: tableSchema,
|
||||
tableOutput: object({
|
||||
data: tableSchema,
|
||||
data: tableOutputSchema,
|
||||
}),
|
||||
})
|
||||
|
|
|
@ -93,6 +93,18 @@ const userSchema = object(
|
|||
{ required: ["email", "roles"] }
|
||||
)
|
||||
|
||||
const userOutputSchema = {
|
||||
...userSchema,
|
||||
properties: {
|
||||
...userSchema.properties,
|
||||
_id: {
|
||||
description: "The ID of the user.",
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
required: [...userSchema.required, "_id"],
|
||||
}
|
||||
|
||||
module.exports = new Resource()
|
||||
.setExamples({
|
||||
user: {
|
||||
|
@ -109,6 +121,6 @@ module.exports = new Resource()
|
|||
.setSchemas({
|
||||
user: userSchema,
|
||||
userOutput: object({
|
||||
data: userSchema,
|
||||
data: userOutputSchema,
|
||||
}),
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { getAllApps } = require("@budibase/backend-core/db")
|
||||
const { updateAppId } = require("@budibase/backend-core/context")
|
||||
import { search as stringSearch, wrapResponse } from "./utils"
|
||||
import { search as stringSearch } from "./utils"
|
||||
import { default as controller } from "../application"
|
||||
import { Application } from "../../../definitions/common"
|
||||
|
||||
|
@ -19,18 +19,16 @@ async function setResponseApp(ctx: any) {
|
|||
ctx.params = { appId: ctx.body.appId }
|
||||
}
|
||||
await controller.fetchAppPackage(ctx)
|
||||
// for now remove everything else
|
||||
wrapResponse(ctx, (input: any) => input.application)
|
||||
}
|
||||
|
||||
export async function search(ctx: any) {
|
||||
export async function search(ctx: any, next: any) {
|
||||
const { name } = ctx.request.body
|
||||
const apps = await getAllApps({ all: true })
|
||||
ctx.body = stringSearch(apps, name)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function create(ctx: any) {
|
||||
export async function create(ctx: any, next: any) {
|
||||
if (!ctx.request.body || !ctx.request.body.useTemplate) {
|
||||
ctx.request.body = {
|
||||
useTemplate: false,
|
||||
|
@ -39,21 +37,24 @@ export async function create(ctx: any) {
|
|||
}
|
||||
await controller.create(ctx)
|
||||
await setResponseApp(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function read(ctx: any) {
|
||||
export async function read(ctx: any, next: any) {
|
||||
updateAppId(ctx.params.appId)
|
||||
await setResponseApp(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function update(ctx: any) {
|
||||
export async function update(ctx: any, next: any) {
|
||||
ctx.request.body = fixAppID(ctx.request.body, ctx.params)
|
||||
updateAppId(ctx.params.appId)
|
||||
await controller.update(ctx)
|
||||
await setResponseApp(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function destroy(ctx: any) {
|
||||
export async function destroy(ctx: any, next: any) {
|
||||
updateAppId(ctx.params.appId)
|
||||
// get the app before deleting it
|
||||
await setResponseApp(ctx)
|
||||
|
@ -61,6 +62,7 @@ export async function destroy(ctx: any) {
|
|||
await controller.delete(ctx)
|
||||
// overwrite the body again
|
||||
ctx.body = body
|
||||
await next()
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { Application, ApplicationOutput } from "../types/components"
|
||||
|
||||
function application(body: any): Application {
|
||||
let app = body?.application ? body.application : body
|
||||
return {
|
||||
_id: app.appId,
|
||||
name: app.name,
|
||||
url: app.url,
|
||||
status: app.status,
|
||||
createdAt: app.createdAt,
|
||||
updatedAt: app.updatedAt,
|
||||
version: app.version,
|
||||
tenantId: app.tenantId,
|
||||
lockedBy: app.lockedBy,
|
||||
}
|
||||
}
|
||||
|
||||
function mapApplication(ctx: any): ApplicationOutput {
|
||||
return {
|
||||
data: application(ctx.body),
|
||||
}
|
||||
}
|
||||
|
||||
function mapApplications(ctx: any): { data: Application[] } {
|
||||
const apps = ctx.body.map((body: any) => application(body))
|
||||
return { data: apps }
|
||||
}
|
||||
|
||||
export default {
|
||||
mapApplication,
|
||||
mapApplications,
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import tables from "./tables"
|
||||
import applications from "./applications"
|
||||
import users from "./users"
|
||||
import rows from "./rows"
|
||||
import queries from "./queries"
|
||||
|
||||
export default {
|
||||
...tables,
|
||||
...applications,
|
||||
...users,
|
||||
...rows,
|
||||
...queries,
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import { Query, ExecuteQueryOutput } from "../types/components"
|
||||
|
||||
function query(body: any): Query {
|
||||
return {
|
||||
_id: body._id,
|
||||
datasourceId: body.datasourceId,
|
||||
parameters: body.parameters,
|
||||
fields: body.fields,
|
||||
queryVerb: body.queryVerb,
|
||||
name: body.name,
|
||||
schema: body.schema,
|
||||
transformer: body.transformer,
|
||||
readable: body.readable,
|
||||
}
|
||||
}
|
||||
|
||||
function mapQueries(ctx: any): { data: Query[] } {
|
||||
const queries = ctx.body.map((body: any) => query(body))
|
||||
return {
|
||||
data: queries,
|
||||
}
|
||||
}
|
||||
|
||||
function mapQueryExecution(ctx: any): ExecuteQueryOutput {
|
||||
// very little we can map here, structure mostly unknown
|
||||
return {
|
||||
data: ctx.body.data,
|
||||
pagination: ctx.body.pagination,
|
||||
extra: ctx.body.extra,
|
||||
info: ctx.body.info,
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
mapQueries,
|
||||
mapQueryExecution,
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { Row, RowSearch, RowOutput } from "../types/components"
|
||||
|
||||
function row(body: any): Row {
|
||||
delete body._rev
|
||||
// have to input everything, since structure unknown
|
||||
return {
|
||||
...body,
|
||||
_id: body._id,
|
||||
tableId: body.tableId,
|
||||
}
|
||||
}
|
||||
|
||||
function mapRowSearch(ctx: any): RowSearch {
|
||||
const rows = ctx.body.rows.map((body: any) => row(body))
|
||||
return {
|
||||
data: rows,
|
||||
hasNextPage: ctx.body.hasNextPage,
|
||||
bookmark: ctx.body.bookmark,
|
||||
}
|
||||
}
|
||||
|
||||
function mapRow(ctx: any): RowOutput {
|
||||
return {
|
||||
data: row(ctx.body),
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
mapRowSearch,
|
||||
mapRow,
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { Table, TableOutput } from "../types/components"
|
||||
|
||||
function table(body: any): Table {
|
||||
return {
|
||||
_id: body._id,
|
||||
name: body.name,
|
||||
schema: body.schema,
|
||||
primaryDisplay: body.primaryDisplay,
|
||||
}
|
||||
}
|
||||
|
||||
function mapTable(ctx: any): TableOutput {
|
||||
return {
|
||||
data: table(ctx.body),
|
||||
}
|
||||
}
|
||||
|
||||
function mapTables(ctx: any): { data: Table[] } {
|
||||
const tables = ctx.body.map((body: any) => table(body))
|
||||
return { data: tables }
|
||||
}
|
||||
|
||||
export default {
|
||||
mapTable,
|
||||
mapTables,
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { User, UserOutput } from "../types/components"
|
||||
|
||||
function user(body: any): User {
|
||||
return {
|
||||
_id: body._id,
|
||||
email: body.email,
|
||||
password: body.password,
|
||||
status: body.status,
|
||||
firstName: body.firstName,
|
||||
lastName: body.lastName,
|
||||
forceResetPassword: body.forceResetPassword,
|
||||
builder: body.builder,
|
||||
admin: body.admin,
|
||||
roles: body.roles,
|
||||
}
|
||||
}
|
||||
|
||||
function mapUser(ctx: any): UserOutput {
|
||||
return {
|
||||
data: user(ctx.body),
|
||||
}
|
||||
}
|
||||
|
||||
function mapUsers(ctx: any): { data: User[] } {
|
||||
const users = ctx.body.map((body: any) => user(body))
|
||||
return { data: users }
|
||||
}
|
||||
|
||||
export default {
|
||||
mapUser,
|
||||
mapUsers,
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
import { search as stringSearch, wrapResponse } from "./utils"
|
||||
import { search as stringSearch } from "./utils"
|
||||
import { default as queryController } from "../query"
|
||||
|
||||
export async function search(ctx: any) {
|
||||
export async function search(ctx: any, next: any) {
|
||||
await queryController.fetch(ctx)
|
||||
const { name } = ctx.request.body
|
||||
ctx.body = stringSearch(ctx.body, name)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function execute(ctx: any) {
|
||||
export async function execute(ctx: any, next: any) {
|
||||
// don't wrap this, already returns "data"
|
||||
await queryController.executeV2(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { default as rowController } from "../row"
|
||||
import { addRev, wrapResponse } from "./utils"
|
||||
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
|
||||
|
@ -20,7 +20,7 @@ function fixRow(row: Row, params: any) {
|
|||
return row
|
||||
}
|
||||
|
||||
export async function search(ctx: any) {
|
||||
export async function search(ctx: any, next: any) {
|
||||
let { sort, paginate, bookmark, limit, query } = ctx.request.body
|
||||
// update the body to the correct format of the internal search
|
||||
if (!sort) {
|
||||
|
@ -36,28 +36,27 @@ export async function search(ctx: any) {
|
|||
query,
|
||||
}
|
||||
await rowController.search(ctx)
|
||||
ctx.body.data = ctx.body.rows
|
||||
delete ctx.body.rows
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function create(ctx: any) {
|
||||
export async function create(ctx: any, next: any) {
|
||||
ctx.request.body = fixRow(ctx.request.body, ctx.params)
|
||||
await rowController.save(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function read(ctx: any) {
|
||||
export async function read(ctx: any, next: any) {
|
||||
await rowController.fetchEnrichedRow(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function update(ctx: any) {
|
||||
export async function update(ctx: any, next: any) {
|
||||
ctx.request.body = await addRev(fixRow(ctx.request.body, ctx.params.tableId))
|
||||
await rowController.save(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function destroy(ctx: any) {
|
||||
export async function destroy(ctx: any, next: any) {
|
||||
// set the body as expected, with the _id and _rev fields
|
||||
ctx.request.body = await addRev(
|
||||
fixRow({ _id: ctx.params.rowId }, ctx.params.tableId)
|
||||
|
@ -65,7 +64,8 @@ export async function destroy(ctx: any) {
|
|||
await rowController.destroy(ctx)
|
||||
// destroy controller doesn't currently return the row as the body, need to adjust this
|
||||
// in the public API to be correct
|
||||
wrapResponse(ctx)
|
||||
ctx.body = ctx.row
|
||||
await next()
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { search as stringSearch, addRev, wrapResponse } from "./utils"
|
||||
import { search as stringSearch, addRev } from "./utils"
|
||||
import { default as controller } from "../table"
|
||||
import { Table } from "../../../definitions/common"
|
||||
|
||||
|
@ -9,39 +9,42 @@ function fixTable(table: Table, params: any) {
|
|||
if (params.tableId) {
|
||||
table._id = params.tableId
|
||||
}
|
||||
if (!table.type) {
|
||||
table.type = "table"
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
export async function search(ctx: any) {
|
||||
export async function search(ctx: any, next: any) {
|
||||
const { name } = ctx.request.body
|
||||
await controller.fetch(ctx)
|
||||
ctx.body = stringSearch(ctx.body, name)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function create(ctx: any) {
|
||||
export async function create(ctx: any, next: any) {
|
||||
await controller.save(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function read(ctx: any) {
|
||||
export async function read(ctx: any, next: any) {
|
||||
await controller.find(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function update(ctx: any) {
|
||||
export async function update(ctx: any, next: any) {
|
||||
ctx.request.body = await addRev(
|
||||
fixTable(ctx.request.body, ctx.params),
|
||||
ctx.params.tableId
|
||||
)
|
||||
await controller.save(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function destroy(ctx: any) {
|
||||
export async function destroy(ctx: any, next: any) {
|
||||
await controller.destroy(ctx)
|
||||
ctx.body = ctx.table
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { components } from "./openapi"
|
||||
|
||||
export type Query = components["schemas"]["query"]
|
||||
export type ExecuteQueryOutput = components["schemas"]["executeQueryOutput"]
|
||||
|
||||
export type Application = components["schemas"]["applicationOutput"]["data"]
|
||||
export type ApplicationOutput = components["schemas"]["applicationOutput"]
|
||||
|
||||
export type Table = components["schemas"]["tableOutput"]["data"]
|
||||
export type TableOutput = components["schemas"]["tableOutput"]
|
||||
|
||||
export type Row = components["schemas"]["rowOutput"]["data"]
|
||||
export type RowOutput = components["schemas"]["rowOutput"]
|
||||
export type RowSearch = components["schemas"]["searchOutput"]
|
||||
|
||||
export type User = components["schemas"]["userOutput"]["data"]
|
||||
export type UserOutput = components["schemas"]["userOutput"]
|
|
@ -0,0 +1,966 @@
|
|||
/**
|
||||
* This file was auto-generated by openapi-typescript.
|
||||
* Do not make direct changes to the file.
|
||||
*/
|
||||
|
||||
export interface paths {
|
||||
"/applications": {
|
||||
post: {
|
||||
parameters: {
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the created application. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["applicationOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["application"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/applications/{appId}": {
|
||||
get: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
appId: components["parameters"]["appIdUrl"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the retrieved application. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["applicationOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
put: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
appId: components["parameters"]["appIdUrl"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the updated application. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["applicationOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["application"]
|
||||
}
|
||||
}
|
||||
}
|
||||
delete: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
appId: components["parameters"]["appIdUrl"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the deleted application. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["applicationOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/applications/search": {
|
||||
/** Based on application properties (currently only name) search for applications. */
|
||||
post: {
|
||||
parameters: {
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the applications that were found based on the search parameters. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
data: components["schemas"]["application"][]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["nameSearch"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/queries/{queryId}": {
|
||||
/** Queries which have been created within a Budibase app can be executed using this, */
|
||||
post: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the query which this request is targeting. */
|
||||
queryId: components["parameters"]["queryId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the result of the query execution. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["executeQueryOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["executeQuery"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/queries/search": {
|
||||
/** Based on query properties (currently only name) search for queries. */
|
||||
post: {
|
||||
parameters: {
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the queries found based on the search parameters. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
data: components["schemas"]["query"][]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["nameSearch"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/tables/{tableId}/rows": {
|
||||
/** Creates a row within the specified table. */
|
||||
post: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the created row, including the ID which has been generated for it. This can be found in the Budibase portal, viewed under the developer information. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["rowOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["row"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/tables/{tableId}/rows/{rowId}": {
|
||||
/** This gets a single row, it will be enriched with the full related rows, rather than the squashed "primaryDisplay" format returned by the search endpoint. */
|
||||
get: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
/** The ID of the row which this request is targeting. */
|
||||
rowId: components["parameters"]["rowId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the retrieved row. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["rowOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Updates a row within the specified table. */
|
||||
put: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
/** The ID of the row which this request is targeting. */
|
||||
rowId: components["parameters"]["rowId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the created row, including the ID which has been generated for it. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["rowOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["row"]
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Deletes a row within the specified table. */
|
||||
delete: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
/** The ID of the row which this request is targeting. */
|
||||
rowId: components["parameters"]["rowId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the deleted row, including the ID which has been generated for it. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["rowOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/tables/{tableId}/rows/search": {
|
||||
post: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** The response will contain an array of rows that match the search parameters. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["searchOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": {
|
||||
query: {
|
||||
/**
|
||||
* @description A map of field name to the string to search for, this will look for rows that have a value starting with the string value.
|
||||
* @example [object Object]
|
||||
*/
|
||||
string?: { [key: string]: string }
|
||||
/** @description A fuzzy search, only supported by internal tables. */
|
||||
fuzzy?: { [key: string]: unknown }
|
||||
/**
|
||||
* @description Searches within a range, the format of this must be columnName -> [low, high].
|
||||
* @example [object Object]
|
||||
*/
|
||||
range?: { [key: string]: unknown }
|
||||
/** @description Searches for rows that have a column value that is exactly the value set. */
|
||||
equal?: { [key: string]: unknown }
|
||||
/** @description Searches for any row which does not contain the specified column value. */
|
||||
notEqual?: { [key: string]: unknown }
|
||||
/**
|
||||
* @description Searches for rows which do not contain the specified column. The object should simply contain keys of the column names, these can map to any value.
|
||||
* @example [object Object]
|
||||
*/
|
||||
empty?: { [key: string]: unknown }
|
||||
/** @description Searches for rows which have the specified column. */
|
||||
notEmpty?: { [key: string]: unknown }
|
||||
/** @description Searches for rows which have a column value that is any of the specified values. The format of this must be columnName -> [value1, value2]. */
|
||||
oneOf?: { [key: string]: unknown }
|
||||
}
|
||||
/** @description Enables pagination, by default this is disabled. */
|
||||
paginate?: boolean
|
||||
/** @description If retrieving another page, the bookmark from the previous request must be supplied. */
|
||||
bookmark?: string | number
|
||||
/** @description The maximum number of rows to return, useful when paginating, for internal tables this will be limited to 1000, for SQL tables it will be 5000. */
|
||||
limit?: number
|
||||
/** @description A set of parameters describing the sort behaviour of the search. */
|
||||
sort?: {
|
||||
/**
|
||||
* @description The order of the sort, by default this is ascending.
|
||||
* @enum {string}
|
||||
*/
|
||||
order?: "ascending" | "descending"
|
||||
/** @description The name of the column by which the rows will be sorted. */
|
||||
column?: string
|
||||
/**
|
||||
* @description Defines whether the column should be treated as a string or as numbers when sorting.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "string" | "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/tables": {
|
||||
/** Create a table, this could be internal or external. */
|
||||
post: {
|
||||
parameters: {
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the created table, including the ID which has been generated for it. This can be internal or external data sources. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["tableOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["table"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/tables/{tableId}": {
|
||||
/** Lookup a table, this could be internal or external. */
|
||||
get: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the retrieved table. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["tableOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Update a table, this could be internal or external. */
|
||||
put: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the updated table. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["tableOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["table"]
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Delete a table, this could be internal or external. */
|
||||
delete: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the table which this request is targeting. */
|
||||
tableId: components["parameters"]["tableId"]
|
||||
}
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the deleted table. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["tableOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/tables/search": {
|
||||
/** Based on table properties (currently only name) search for tables. This could be an internal or an external table. */
|
||||
post: {
|
||||
parameters: {
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the found tables, based on the search parameters. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
data: components["schemas"]["table"][]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["nameSearch"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/users": {
|
||||
post: {
|
||||
responses: {
|
||||
/** Returns the created user. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["userOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["user"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/users/{userId}": {
|
||||
get: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the user which this request is targeting. */
|
||||
userId: components["parameters"]["userId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the retrieved user. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["userOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
put: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the user which this request is targeting. */
|
||||
userId: components["parameters"]["userId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the updated user. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["userOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["user"]
|
||||
}
|
||||
}
|
||||
}
|
||||
delete: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the user which this request is targeting. */
|
||||
userId: components["parameters"]["userId"]
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** Returns the deleted user. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["userOutput"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/users/search": {
|
||||
/** Based on user properties (currently only name) search for users. */
|
||||
post: {
|
||||
responses: {
|
||||
/** Returns the found users based on search parameters. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
data: components["schemas"]["user"][]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["nameSearch"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface components {
|
||||
schemas: {
|
||||
application: {
|
||||
/** @description The name of the app. */
|
||||
name: string
|
||||
/** @description The URL by which the app is accessed, this must be URL encoded. */
|
||||
url: string
|
||||
}
|
||||
applicationOutput: {
|
||||
data: {
|
||||
/** @description The name of the app. */
|
||||
name: string
|
||||
/** @description The URL by which the app is accessed, this must be URL encoded. */
|
||||
url: string
|
||||
/** @description The ID of the app. */
|
||||
_id: string
|
||||
/**
|
||||
* @description The status of the app, stating it if is the development or published version.
|
||||
* @enum {string}
|
||||
*/
|
||||
status: "development" | "published"
|
||||
/** @description States when the app was created, will be constant. Stored in ISO format. */
|
||||
createdAt: string
|
||||
/** @description States the last time the app was updated - stored in ISO format. */
|
||||
updatedAt: string
|
||||
/** @description States the version of the Budibase client this app is currently based on. */
|
||||
version: string
|
||||
/** @description In a multi-tenant environment this will state the tenant this app is within. */
|
||||
tenantId?: string
|
||||
/** @description The user this app is currently being built by. */
|
||||
lockedBy?: { [key: string]: unknown }
|
||||
}
|
||||
}
|
||||
/** @description The row to be created/updated, based on the table schema. */
|
||||
row: {
|
||||
[key: string]:
|
||||
| string
|
||||
| { [key: string]: unknown }
|
||||
| number
|
||||
| unknown[]
|
||||
| boolean
|
||||
}
|
||||
searchOutput: {
|
||||
/** @description An array of rows, these will each contain an _id field which can be used to update or delete them. */
|
||||
data: { [key: string]: unknown }[]
|
||||
/** @description If pagination in use, this should be provided. */
|
||||
bookmark?: string | number
|
||||
/** @description If pagination in use, this will determine if there is another page to fetch. */
|
||||
hasNextPage?: boolean
|
||||
}
|
||||
rowOutput: {
|
||||
/** @description The row to be created/updated, based on the table schema. */
|
||||
data: {
|
||||
/** @description The ID of the row. */
|
||||
_id: string
|
||||
/** @description The ID of the table this row comes from. */
|
||||
tableId: string
|
||||
} & {
|
||||
[key: string]:
|
||||
| string
|
||||
| { [key: string]: unknown }
|
||||
| number
|
||||
| unknown[]
|
||||
| boolean
|
||||
}
|
||||
}
|
||||
/** @description The table to be created/updated. */
|
||||
table: {
|
||||
/** @description The name of the table. */
|
||||
name: string
|
||||
/** @description The name of the column which should be used in relationship tags when relating to this table. */
|
||||
primaryDisplay?: string
|
||||
schema: {
|
||||
[key: string]:
|
||||
| {
|
||||
/**
|
||||
* @description A relationship column.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "link"
|
||||
/** @description A constraint can be applied to the column which will be validated against when a row is saved. */
|
||||
constraints?: {
|
||||
/** @enum {string} */
|
||||
type?: "string" | "number" | "object" | "boolean"
|
||||
/** @description Defines whether the column is required or not. */
|
||||
presence?: boolean
|
||||
}
|
||||
/** @description The name of the column. */
|
||||
name?: string
|
||||
/** @description Defines whether the column is automatically generated. */
|
||||
autocolumn?: boolean
|
||||
/** @description The name of the column which a relationship column is related to in another table. */
|
||||
fieldName?: string
|
||||
/** @description The ID of the table which a relationship column is related to. */
|
||||
tableId?: string
|
||||
/**
|
||||
* @description Defines the type of relationship that this column will be used for.
|
||||
* @enum {string}
|
||||
*/
|
||||
relationshipType?: "one-to-many" | "many-to-one" | "many-to-many"
|
||||
/** @description When using a SQL table that contains many to many relationships this defines the table the relationships are linked through. */
|
||||
through?: string
|
||||
/** @description When using a SQL table that contains a one to many relationship this defines the foreign key. */
|
||||
foreignKey?: string
|
||||
/** @description When using a SQL table that utilises a through table, this defines the primary key in the through table for this table. */
|
||||
throughFrom?: 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. */
|
||||
throughTo?: string
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @description A formula column.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "formula"
|
||||
/** @description A constraint can be applied to the column which will be validated against when a row is saved. */
|
||||
constraints?: {
|
||||
/** @enum {string} */
|
||||
type?: "string" | "number" | "object" | "boolean"
|
||||
/** @description Defines whether the column is required or not. */
|
||||
presence?: boolean
|
||||
}
|
||||
/** @description The name of the column. */
|
||||
name?: string
|
||||
/** @description Defines whether the column is automatically generated. */
|
||||
autocolumn?: boolean
|
||||
/** @description Defines a Handlebars or JavaScript formula to use, note that Javascript formulas are expected to be provided in the base64 format. */
|
||||
formula?: string
|
||||
/**
|
||||
* @description Defines whether this is a static or dynamic formula.
|
||||
* @enum {string}
|
||||
*/
|
||||
formulaType?: "static" | "dynamic"
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @description Defines the type of the column, most explain themselves, a link column is a relationship.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?:
|
||||
| "string"
|
||||
| "longform"
|
||||
| "options"
|
||||
| "number"
|
||||
| "boolean"
|
||||
| "array"
|
||||
| "datetime"
|
||||
| "attachment"
|
||||
| "link"
|
||||
| "formula"
|
||||
| "auto"
|
||||
| "json"
|
||||
| "internal"
|
||||
/** @description A constraint can be applied to the column which will be validated against when a row is saved. */
|
||||
constraints?: {
|
||||
/** @enum {string} */
|
||||
type?: "string" | "number" | "object" | "boolean"
|
||||
/** @description Defines whether the column is required or not. */
|
||||
presence?: boolean
|
||||
}
|
||||
/** @description The name of the column. */
|
||||
name?: string
|
||||
/** @description Defines whether the column is automatically generated. */
|
||||
autocolumn?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
tableOutput: {
|
||||
/** @description The table to be created/updated. */
|
||||
data: {
|
||||
/** @description The name of the table. */
|
||||
name: string
|
||||
/** @description The name of the column which should be used in relationship tags when relating to this table. */
|
||||
primaryDisplay?: string
|
||||
schema: {
|
||||
[key: string]:
|
||||
| {
|
||||
/**
|
||||
* @description A relationship column.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "link"
|
||||
/** @description A constraint can be applied to the column which will be validated against when a row is saved. */
|
||||
constraints?: {
|
||||
/** @enum {string} */
|
||||
type?: "string" | "number" | "object" | "boolean"
|
||||
/** @description Defines whether the column is required or not. */
|
||||
presence?: boolean
|
||||
}
|
||||
/** @description The name of the column. */
|
||||
name?: string
|
||||
/** @description Defines whether the column is automatically generated. */
|
||||
autocolumn?: boolean
|
||||
/** @description The name of the column which a relationship column is related to in another table. */
|
||||
fieldName?: string
|
||||
/** @description The ID of the table which a relationship column is related to. */
|
||||
tableId?: string
|
||||
/**
|
||||
* @description Defines the type of relationship that this column will be used for.
|
||||
* @enum {string}
|
||||
*/
|
||||
relationshipType?:
|
||||
| "one-to-many"
|
||||
| "many-to-one"
|
||||
| "many-to-many"
|
||||
/** @description When using a SQL table that contains many to many relationships this defines the table the relationships are linked through. */
|
||||
through?: string
|
||||
/** @description When using a SQL table that contains a one to many relationship this defines the foreign key. */
|
||||
foreignKey?: string
|
||||
/** @description When using a SQL table that utilises a through table, this defines the primary key in the through table for this table. */
|
||||
throughFrom?: 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. */
|
||||
throughTo?: string
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @description A formula column.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "formula"
|
||||
/** @description A constraint can be applied to the column which will be validated against when a row is saved. */
|
||||
constraints?: {
|
||||
/** @enum {string} */
|
||||
type?: "string" | "number" | "object" | "boolean"
|
||||
/** @description Defines whether the column is required or not. */
|
||||
presence?: boolean
|
||||
}
|
||||
/** @description The name of the column. */
|
||||
name?: string
|
||||
/** @description Defines whether the column is automatically generated. */
|
||||
autocolumn?: boolean
|
||||
/** @description Defines a Handlebars or JavaScript formula to use, note that Javascript formulas are expected to be provided in the base64 format. */
|
||||
formula?: string
|
||||
/**
|
||||
* @description Defines whether this is a static or dynamic formula.
|
||||
* @enum {string}
|
||||
*/
|
||||
formulaType?: "static" | "dynamic"
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @description Defines the type of the column, most explain themselves, a link column is a relationship.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?:
|
||||
| "string"
|
||||
| "longform"
|
||||
| "options"
|
||||
| "number"
|
||||
| "boolean"
|
||||
| "array"
|
||||
| "datetime"
|
||||
| "attachment"
|
||||
| "link"
|
||||
| "formula"
|
||||
| "auto"
|
||||
| "json"
|
||||
| "internal"
|
||||
/** @description A constraint can be applied to the column which will be validated against when a row is saved. */
|
||||
constraints?: {
|
||||
/** @enum {string} */
|
||||
type?: "string" | "number" | "object" | "boolean"
|
||||
/** @description Defines whether the column is required or not. */
|
||||
presence?: boolean
|
||||
}
|
||||
/** @description The name of the column. */
|
||||
name?: string
|
||||
/** @description Defines whether the column is automatically generated. */
|
||||
autocolumn?: boolean
|
||||
}
|
||||
}
|
||||
/** @description The ID of the table. */
|
||||
_id: string
|
||||
}
|
||||
}
|
||||
/** @description The query body must contain the required parameters for the query, this depends on query type, setup and bindings. */
|
||||
executeQuery: {
|
||||
[key: string]:
|
||||
| string
|
||||
| { [key: string]: unknown }
|
||||
| number
|
||||
| unknown[]
|
||||
| boolean
|
||||
}
|
||||
executeQueryOutput: {
|
||||
/** @description The data response from the query. */
|
||||
data: { [key: string]: unknown }[]
|
||||
/** @description Extra information that is not part of the main data, e.g. headers. */
|
||||
extra?: {
|
||||
/** @description If carrying out a REST request, this will contain the response headers. */
|
||||
headers?: { [key: string]: unknown }
|
||||
/** @description The raw query response, as a string. */
|
||||
raw?: string
|
||||
}
|
||||
/** @description Extra info from the query in a key-value map, like response times. */
|
||||
info?: { [key: string]: unknown }
|
||||
/** @description If pagination is supported, this will contain the bookmark/anchor information for it. */
|
||||
pagination?: { [key: string]: unknown }
|
||||
}
|
||||
query: {
|
||||
/** @description The ID of the query. */
|
||||
_id: string
|
||||
/** @description The ID of the data source the query belongs to. */
|
||||
datasourceId?: string
|
||||
/** @description The bindings which are required to perform this query. */
|
||||
parameters?: string[]
|
||||
/** @description The fields that are used to perform this query, e.g. the sql statement */
|
||||
fields?: { [key: string]: unknown }
|
||||
/**
|
||||
* @description The verb that describes this query.
|
||||
* @enum {undefined}
|
||||
*/
|
||||
queryVerb?: "create" | "read" | "update" | "delete"
|
||||
/** @description The name of the query. */
|
||||
name: string
|
||||
/** @description The schema of the data returned when the query is executed. */
|
||||
schema: { [key: string]: unknown }
|
||||
/** @description The JavaScript transformer function, applied after the query responds with data. */
|
||||
transformer?: string
|
||||
/** @description Whether the query has readable data. */
|
||||
readable?: boolean
|
||||
}
|
||||
user: {
|
||||
/** @description The email address of the user, this must be unique. */
|
||||
email: string
|
||||
/** @description The password of the user if using password based login - this will never be returned. This can be left out of subsequent requests (updates) and will be enriched back into the user structure. */
|
||||
password?: string
|
||||
/**
|
||||
* @description The status of the user, if they are active.
|
||||
* @enum {string}
|
||||
*/
|
||||
status?: "active"
|
||||
/** @description The first name of the user */
|
||||
firstName?: string
|
||||
/** @description The last name of the user */
|
||||
lastName?: string
|
||||
/** @description If set to true forces the user to reset their password on first login. */
|
||||
forceResetPassword?: boolean
|
||||
/** @description Describes if the user is a builder user or not. */
|
||||
builder?: {
|
||||
/** @description If set to true the user will be able to build any app in the system. */
|
||||
global?: boolean
|
||||
}
|
||||
/** @description Describes if the user is an admin user or not. */
|
||||
admin?: {
|
||||
/** @description If set to true the user will be able to administrate the system. */
|
||||
global?: boolean
|
||||
}
|
||||
/** @description Contains the roles of the user per app (assuming they are not a builder user). */
|
||||
roles: { [key: string]: string }
|
||||
}
|
||||
userOutput: {
|
||||
data: {
|
||||
/** @description The email address of the user, this must be unique. */
|
||||
email: string
|
||||
/** @description The password of the user if using password based login - this will never be returned. This can be left out of subsequent requests (updates) and will be enriched back into the user structure. */
|
||||
password?: string
|
||||
/**
|
||||
* @description The status of the user, if they are active.
|
||||
* @enum {string}
|
||||
*/
|
||||
status?: "active"
|
||||
/** @description The first name of the user */
|
||||
firstName?: string
|
||||
/** @description The last name of the user */
|
||||
lastName?: string
|
||||
/** @description If set to true forces the user to reset their password on first login. */
|
||||
forceResetPassword?: boolean
|
||||
/** @description Describes if the user is a builder user or not. */
|
||||
builder?: {
|
||||
/** @description If set to true the user will be able to build any app in the system. */
|
||||
global?: boolean
|
||||
}
|
||||
/** @description Describes if the user is an admin user or not. */
|
||||
admin?: {
|
||||
/** @description If set to true the user will be able to administrate the system. */
|
||||
global?: boolean
|
||||
}
|
||||
/** @description Contains the roles of the user per app (assuming they are not a builder user). */
|
||||
roles: { [key: string]: string }
|
||||
/** @description The ID of the user. */
|
||||
_id: string
|
||||
}
|
||||
}
|
||||
nameSearch: {
|
||||
/** @description The name to be used when searching - this will be used in a case insensitive starts with match. */
|
||||
name: string
|
||||
}
|
||||
}
|
||||
parameters: {
|
||||
/** @description The ID of the table which this request is targeting. */
|
||||
tableId: string
|
||||
/** @description The ID of the row which this request is targeting. */
|
||||
rowId: string
|
||||
/** @description The ID of the app which this request is targeting. */
|
||||
appId: string
|
||||
/** @description The ID of the app which this request is targeting. */
|
||||
appIdUrl: string
|
||||
/** @description The ID of the query which this request is targeting. */
|
||||
queryId: string
|
||||
/** @description The ID of the user which this request is targeting. */
|
||||
userId: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface operations {}
|
||||
|
||||
export interface external {}
|
|
@ -4,7 +4,7 @@ import {
|
|||
readGlobalUser,
|
||||
saveGlobalUser,
|
||||
} from "../../../utilities/workerRequests"
|
||||
import { search as stringSearch, wrapResponse } from "./utils"
|
||||
import { search as stringSearch } from "./utils"
|
||||
|
||||
const { getProdAppID } = require("@budibase/backend-core/db")
|
||||
|
||||
|
@ -37,25 +37,25 @@ function getUser(ctx: any, userId?: string) {
|
|||
return readGlobalUser(ctx)
|
||||
}
|
||||
|
||||
export async function search(ctx: any) {
|
||||
export async function search(ctx: any, next: any) {
|
||||
const { name } = ctx.request.body
|
||||
const users = await allGlobalUsers(ctx)
|
||||
ctx.body = stringSearch(users, name, "email")
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function create(ctx: any) {
|
||||
export async function create(ctx: any, next: any) {
|
||||
const response = await saveGlobalUser(fixUser(ctx))
|
||||
ctx.body = await getUser(ctx, response._id)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function read(ctx: any) {
|
||||
export async function read(ctx: any, next: any) {
|
||||
ctx.body = await readGlobalUser(ctx)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function update(ctx: any) {
|
||||
export async function update(ctx: any, next: any) {
|
||||
const user = await readGlobalUser(ctx)
|
||||
ctx.request.body = {
|
||||
...ctx.request.body,
|
||||
|
@ -63,14 +63,14 @@ export async function update(ctx: any) {
|
|||
}
|
||||
const response = await saveGlobalUser(fixUser(ctx))
|
||||
ctx.body = await getUser(ctx, response._id)
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function destroy(ctx: any) {
|
||||
export async function destroy(ctx: any, next: any) {
|
||||
const user = await getUser(ctx)
|
||||
await deleteGlobalUser(ctx)
|
||||
ctx.body = user
|
||||
wrapResponse(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
@ -36,11 +36,3 @@ export function search(docs: any[], value: any, key = "name") {
|
|||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
export function wrapResponse(ctx: any, map?: (input: any) => object) {
|
||||
let data = ctx.body
|
||||
if (map) {
|
||||
data = map(data)
|
||||
}
|
||||
ctx.body = { data }
|
||||
}
|
||||
|
|
|
@ -58,5 +58,5 @@ exports.mainRoutes = [
|
|||
migrationRoutes,
|
||||
]
|
||||
|
||||
exports.staticRoutes = staticRoutes
|
||||
exports.publicRoutes = publicRoutes
|
||||
exports.staticRoutes = staticRoutes
|
||||
|
|
|
@ -7,6 +7,7 @@ import usage from "../../../middleware/usageQuota"
|
|||
import authorized from "../../../middleware/authorized"
|
||||
import { paramResource, paramSubResource } from "../../../middleware/resourceId"
|
||||
import { CtxFn } from "./utils/Endpoint"
|
||||
import mapperMiddleware from "./middleware/mapper"
|
||||
const Router = require("@koa/router")
|
||||
const {
|
||||
PermissionLevels,
|
||||
|
@ -19,7 +20,11 @@ const publicRouter = new Router({
|
|||
prefix: PREFIX,
|
||||
})
|
||||
|
||||
function addMiddleware(endpoints: any, middleware: CtxFn) {
|
||||
function addMiddleware(
|
||||
endpoints: any,
|
||||
middleware: CtxFn,
|
||||
opts: { output: boolean } = { output: false }
|
||||
) {
|
||||
if (!endpoints) {
|
||||
return
|
||||
}
|
||||
|
@ -27,9 +32,13 @@ function addMiddleware(endpoints: any, middleware: CtxFn) {
|
|||
endpoints = [endpoints]
|
||||
}
|
||||
for (let endpoint of endpoints) {
|
||||
if (opts?.output) {
|
||||
endpoint.addOutputMiddleware(middleware)
|
||||
} else {
|
||||
endpoint.addMiddleware(middleware)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addToRouter(endpoints: any) {
|
||||
if (endpoints) {
|
||||
|
@ -48,11 +57,17 @@ function applyRoutes(
|
|||
const paramMiddleware = subResource
|
||||
? paramSubResource(resource, subResource)
|
||||
: paramResource(resource)
|
||||
// add the parameter capture middleware
|
||||
addMiddleware(endpoints.read, paramMiddleware)
|
||||
addMiddleware(endpoints.write, paramMiddleware)
|
||||
// add the authorization middleware, using the correct perm type
|
||||
addMiddleware(endpoints.read, authorized(permType, PermissionLevels.READ))
|
||||
addMiddleware(endpoints.write, authorized(permType, PermissionLevels.WRITE))
|
||||
// add the usage quota middleware
|
||||
addMiddleware(endpoints.write, usage)
|
||||
// add the output mapper middleware
|
||||
addMiddleware(endpoints.read, mapperMiddleware, { output: true })
|
||||
addMiddleware(endpoints.write, mapperMiddleware, { output: true })
|
||||
addToRouter(endpoints.read)
|
||||
addToRouter(endpoints.write)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import mapping from "../../../controllers/public/mapping"
|
||||
|
||||
enum Resources {
|
||||
APPLICATION = "applications",
|
||||
TABLES = "tables",
|
||||
ROWS = "rows",
|
||||
USERS = "users",
|
||||
QUERIES = "queries",
|
||||
SEARCH = "search",
|
||||
}
|
||||
|
||||
function isSearch(ctx: any) {
|
||||
return ctx.url.endsWith(Resources.SEARCH)
|
||||
}
|
||||
|
||||
function processApplications(ctx: any) {
|
||||
if (isSearch(ctx)) {
|
||||
return mapping.mapApplications(ctx)
|
||||
} else {
|
||||
return mapping.mapApplication(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
function processTables(ctx: any) {
|
||||
if (isSearch(ctx)) {
|
||||
return mapping.mapTables(ctx)
|
||||
} else {
|
||||
return mapping.mapTable(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
function processRows(ctx: any) {
|
||||
if (isSearch(ctx)) {
|
||||
return mapping.mapRowSearch(ctx)
|
||||
} else {
|
||||
return mapping.mapRow(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
function processUsers(ctx: any) {
|
||||
if (isSearch(ctx)) {
|
||||
return mapping.mapUsers(ctx)
|
||||
} else {
|
||||
return mapping.mapUser(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
function processQueries(ctx: any) {
|
||||
if (isSearch(ctx)) {
|
||||
return mapping.mapQueries(ctx)
|
||||
} else {
|
||||
return mapping.mapQueryExecution(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
export default async (ctx: any, next: any) => {
|
||||
let urlParts = ctx.url.split("/")
|
||||
urlParts = urlParts.slice(4, urlParts.length)
|
||||
let body = {}
|
||||
switch (urlParts[0]) {
|
||||
case Resources.APPLICATION:
|
||||
body = processApplications(ctx)
|
||||
break
|
||||
case Resources.TABLES:
|
||||
if (urlParts[2] === Resources.ROWS) {
|
||||
body = processRows(ctx)
|
||||
} else {
|
||||
body = processTables(ctx)
|
||||
}
|
||||
break
|
||||
case Resources.USERS:
|
||||
body = processUsers(ctx)
|
||||
break
|
||||
case Resources.QUERIES:
|
||||
body = processQueries(ctx)
|
||||
break
|
||||
}
|
||||
// update the body based on what has occurred in the mapper
|
||||
ctx.body = body
|
||||
await next()
|
||||
}
|
|
@ -11,38 +11,24 @@ const read = [],
|
|||
* post:
|
||||
* summary: Execute a query
|
||||
* description: Queries which have been created within a Budibase app can be executed using this,
|
||||
* tags:
|
||||
* - queries
|
||||
* parameters:
|
||||
* - $ref: '#/components/parameters/queryId'
|
||||
* - $ref: '#/components/parameters/appId'
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/executeQuery'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Returns the result of the query execution.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* data:
|
||||
* type: array
|
||||
* description: The data retrieved from the query.
|
||||
* items:
|
||||
* type: object
|
||||
* description: The structure of the returned data will be an object,
|
||||
* if it is just a string then this will be an object containing "value".
|
||||
* pagination:
|
||||
* type: object
|
||||
* description: For supported query types this returns pagination information.
|
||||
* properties:
|
||||
* cursor:
|
||||
* type: string
|
||||
* description: The pagination cursor location.
|
||||
* raw:
|
||||
* type: string
|
||||
* description: The raw query response.
|
||||
* headers:
|
||||
* type: object
|
||||
* description: For REST queries the headers in the response will be returned here.
|
||||
* $ref: '#/components/schemas/executeQueryOutput'
|
||||
* examples:
|
||||
* REST:
|
||||
* $ref: '#/components/examples/restResponse'
|
||||
|
|
|
@ -226,24 +226,7 @@ read.push(new Endpoint("get", "/tables/:tableId/rows/:rowId", controller.read))
|
|||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - data
|
||||
* properties:
|
||||
* data:
|
||||
* description: An array of rows, these will each contain an _id field which can be used
|
||||
* to update or delete them.
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
* bookmark:
|
||||
* oneOf:
|
||||
* - type: string
|
||||
* - type: integer
|
||||
* description: If pagination in use, this should be provided.
|
||||
* hasNextPage:
|
||||
* description: If pagination in use, this will determine if there is another page to fetch.
|
||||
* type: boolean
|
||||
* $ref: '#/components/schemas/searchOutput'
|
||||
* examples:
|
||||
* search:
|
||||
* $ref: '#/components/examples/rows'
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import Router from "koa-router"
|
||||
|
||||
export type CtxFn = (ctx: any) => void
|
||||
export type CtxFn = (ctx: any, next?: any) => void | Promise<any>
|
||||
|
||||
class Endpoint {
|
||||
method: string
|
||||
url: string
|
||||
controller: CtxFn
|
||||
middlewares: CtxFn[]
|
||||
outputMiddlewares: CtxFn[]
|
||||
|
||||
constructor(method: string, url: string, controller: CtxFn) {
|
||||
this.method = method
|
||||
this.url = url
|
||||
this.controller = controller
|
||||
this.middlewares = []
|
||||
this.outputMiddlewares = []
|
||||
}
|
||||
|
||||
addMiddleware(middleware: CtxFn) {
|
||||
|
@ -20,12 +22,27 @@ class Endpoint {
|
|||
return this
|
||||
}
|
||||
|
||||
addOutputMiddleware(middleware: CtxFn) {
|
||||
this.outputMiddlewares.push(middleware)
|
||||
return this
|
||||
}
|
||||
|
||||
apply(router: Router) {
|
||||
const method = this.method,
|
||||
url = this.url
|
||||
const middlewares = this.middlewares,
|
||||
controller = this.controller
|
||||
const params = [url, ...middlewares, controller]
|
||||
controller = this.controller,
|
||||
outputMiddlewares = this.outputMiddlewares
|
||||
// need a function to do nothing to stop the execution at the end
|
||||
// middlewares are circular so if they always keep calling next, it'll just keep looping
|
||||
const complete = () => {}
|
||||
const params = [
|
||||
url,
|
||||
...middlewares,
|
||||
controller,
|
||||
...outputMiddlewares,
|
||||
complete,
|
||||
]
|
||||
// @ts-ignore
|
||||
router[method](...params)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue