Adding controllers for row, query and applications public APIs.
This commit is contained in:
parent
8685abf99e
commit
8f6e55e65b
12 changed files with 310 additions and 33 deletions
7
packages/backend-core/src/cache/user.js
vendored
7
packages/backend-core/src/cache/user.js
vendored
|
@ -32,11 +32,10 @@ const populateFromDB = async (userId, tenantId) => {
|
||||||
* @param {*} populateUser function to provide the user for re-caching. default to couch db
|
* @param {*} populateUser function to provide the user for re-caching. default to couch db
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
exports.getUser = async (
|
exports.getUser = async (userId, tenantId = null, populateUser = null) => {
|
||||||
userId,
|
if (!populateUser) {
|
||||||
tenantId = null,
|
|
||||||
populateUser = populateFromDB
|
populateUser = populateFromDB
|
||||||
) => {
|
}
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
try {
|
try {
|
||||||
tenantId = getTenantId()
|
tenantId = getTenantId()
|
||||||
|
|
|
@ -68,6 +68,7 @@ function getDocParams(docType, docId = null, otherProps = {}) {
|
||||||
endkey: `${docType}${SEPARATOR}${docId}${UNICODE_MAX}`,
|
endkey: `${docType}${SEPARATOR}${docId}${UNICODE_MAX}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exports.getDocParams = getDocParams
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new workspace ID.
|
* Generates a new workspace ID.
|
||||||
|
|
|
@ -335,6 +335,42 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"restResponse": {
|
||||||
|
"value": {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"value": "<html lang='en-GB'></html>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pagination": {
|
||||||
|
"cursor": "2"
|
||||||
|
},
|
||||||
|
"raw": "<html lang='en-GB'></html>",
|
||||||
|
"headers": {
|
||||||
|
"content-type": "text/html; charset=ISO-8859-1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sqlResponse": {
|
||||||
|
"value": {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"personid": 1,
|
||||||
|
"lastname": "Hughes",
|
||||||
|
"firstname": "Mike",
|
||||||
|
"address": "123 Fake Street",
|
||||||
|
"city": "Belfast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"personid": 2,
|
||||||
|
"lastname": "Smith",
|
||||||
|
"firstname": "John",
|
||||||
|
"address": "64 Updown Road",
|
||||||
|
"city": "Dublin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"value": {
|
"value": {
|
||||||
"user": {
|
"user": {
|
||||||
|
@ -1144,14 +1180,42 @@
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"description": "The data retrieved from the query.",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "object"
|
"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."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"examples": {
|
"examples": {
|
||||||
"query": {
|
"REST": {
|
||||||
"$ref": "#/components/examples/query"
|
"$ref": "#/components/examples/restResponse"
|
||||||
|
},
|
||||||
|
"SQL": {
|
||||||
|
"$ref": "#/components/examples/sqlResponse"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,6 +242,28 @@ components:
|
||||||
type: string
|
type: string
|
||||||
transformer: return data
|
transformer: return data
|
||||||
readable: true
|
readable: true
|
||||||
|
restResponse:
|
||||||
|
value:
|
||||||
|
data:
|
||||||
|
- value: <html lang='en-GB'></html>
|
||||||
|
pagination:
|
||||||
|
cursor: "2"
|
||||||
|
raw: <html lang='en-GB'></html>
|
||||||
|
headers:
|
||||||
|
content-type: text/html; charset=ISO-8859-1
|
||||||
|
sqlResponse:
|
||||||
|
value:
|
||||||
|
data:
|
||||||
|
- personid: 1
|
||||||
|
lastname: Hughes
|
||||||
|
firstname: Mike
|
||||||
|
address: 123 Fake Street
|
||||||
|
city: Belfast
|
||||||
|
- personid: 2
|
||||||
|
lastname: Smith
|
||||||
|
firstname: John
|
||||||
|
address: 64 Updown Road
|
||||||
|
city: Dublin
|
||||||
user:
|
user:
|
||||||
value:
|
value:
|
||||||
user:
|
user:
|
||||||
|
@ -804,12 +826,33 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
type: array
|
type: array
|
||||||
|
description: The data retrieved from the query.
|
||||||
items:
|
items:
|
||||||
type: object
|
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.
|
||||||
examples:
|
examples:
|
||||||
query:
|
REST:
|
||||||
$ref: "#/components/examples/query"
|
$ref: "#/components/examples/restResponse"
|
||||||
|
SQL:
|
||||||
|
$ref: "#/components/examples/sqlResponse"
|
||||||
"/tables/{tableId}/rows/search":
|
"/tables/{tableId}/rows/search":
|
||||||
post:
|
post:
|
||||||
summary: Used to search for rows within a table.
|
summary: Used to search for rows within a table.
|
||||||
|
|
|
@ -36,6 +36,44 @@ const query = {
|
||||||
readable: true,
|
readable: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const restResponse = {
|
||||||
|
value: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: "<html lang='en-GB'></html>",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
cursor: "2",
|
||||||
|
},
|
||||||
|
raw: "<html lang='en-GB'></html>",
|
||||||
|
headers: {
|
||||||
|
"content-type": "text/html; charset=ISO-8859-1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const sqlResponse = {
|
||||||
|
value: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
personid: 1,
|
||||||
|
lastname: "Hughes",
|
||||||
|
firstname: "Mike",
|
||||||
|
address: "123 Fake Street",
|
||||||
|
city: "Belfast",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
personid: 2,
|
||||||
|
lastname: "Smith",
|
||||||
|
firstname: "John",
|
||||||
|
address: "64 Updown Road",
|
||||||
|
city: "Dublin",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const querySchema = object({})
|
const querySchema = object({})
|
||||||
|
|
||||||
module.exports = new Resource()
|
module.exports = new Resource()
|
||||||
|
@ -50,6 +88,8 @@ module.exports = new Resource()
|
||||||
queries: [query],
|
queries: [query],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
restResponse,
|
||||||
|
sqlResponse,
|
||||||
})
|
})
|
||||||
.setSchemas({
|
.setSchemas({
|
||||||
query: querySchema,
|
query: querySchema,
|
||||||
|
|
|
@ -1,9 +1,45 @@
|
||||||
exports.search = () => {}
|
const { search } = require("./utils")
|
||||||
|
const { getAllApps } = require("@budibase/backend-core/db")
|
||||||
|
const { updateAppId } = require("@budibase/backend-core/context")
|
||||||
|
const controller = require("../application")
|
||||||
|
|
||||||
exports.create = () => {}
|
async function setResponseApp(ctx) {
|
||||||
|
if (ctx.body && ctx.body.appId && (!ctx.params || !ctx.params.appId)) {
|
||||||
|
ctx.params = { appId: ctx.body.appId }
|
||||||
|
}
|
||||||
|
await controller.fetchAppPackage(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
exports.read = () => {}
|
exports.search = async ctx => {
|
||||||
|
const { name } = ctx.request.body
|
||||||
|
const apps = await getAllApps({ all: true })
|
||||||
|
ctx.body = {
|
||||||
|
applications: search(apps, "name", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exports.update = () => {}
|
exports.create = async ctx => {
|
||||||
|
await controller.create(ctx)
|
||||||
|
await setResponseApp(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
exports.delete = () => {}
|
exports.read = async ctx => {
|
||||||
|
updateAppId(ctx.params.appId)
|
||||||
|
await setResponseApp(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.update = async ctx => {
|
||||||
|
updateAppId(ctx.params.appId)
|
||||||
|
await controller.update(ctx)
|
||||||
|
await setResponseApp(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.delete = async ctx => {
|
||||||
|
updateAppId(ctx.params.appId)
|
||||||
|
// get the app before deleting it
|
||||||
|
await setResponseApp(ctx)
|
||||||
|
const body = ctx.body
|
||||||
|
await controller.delete(ctx)
|
||||||
|
// overwrite the body again
|
||||||
|
ctx.body = body
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
exports.search = () => {}
|
const { searchDocs } = require("./utils")
|
||||||
|
const { DocumentTypes } = require("../../../db/utils")
|
||||||
|
const queryController = require("../query")
|
||||||
|
|
||||||
exports.execute = () => {}
|
exports.search = async ctx => {
|
||||||
|
const { name } = ctx.request.body
|
||||||
|
ctx.body = {
|
||||||
|
queries: await searchDocs(DocumentTypes.QUERY, "name", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.execute = async ctx => {
|
||||||
|
await queryController.executeV2(ctx)
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const rowController = require("../row")
|
const rowController = require("../row")
|
||||||
|
const { addRev } = require("./utils")
|
||||||
|
|
||||||
// makes sure that the user doesn't need to pass in the type, tableId or _id params for
|
// makes sure that the user doesn't need to pass in the type, tableId or _id params for
|
||||||
// the call to be correct
|
// the call to be correct
|
||||||
|
@ -22,14 +23,27 @@ exports.search = async ctx => {
|
||||||
await rowController.search(ctx)
|
await rowController.search(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.create = ctx => {
|
exports.create = async ctx => {
|
||||||
ctx.request.body = fixRow(ctx.request.body, ctx.params)
|
ctx.request.body = fixRow(ctx.request.body, ctx.params)
|
||||||
|
await rowController.save(ctx)
|
||||||
|
ctx.body = { row: ctx.body }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.read = () => {}
|
exports.read = async ctx => {
|
||||||
|
await rowController.find(ctx)
|
||||||
|
ctx.body = { row: ctx.body }
|
||||||
|
}
|
||||||
|
|
||||||
exports.update = async ctx => {
|
exports.update = async ctx => {
|
||||||
ctx.request.body = fixRow(ctx.request.body, ctx.params)
|
ctx.request.body = await addRev(fixRow(ctx.request.body, ctx.params))
|
||||||
|
ctx.body = { row: ctx.body }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.delete = () => {}
|
exports.delete = async ctx => {
|
||||||
|
// set the body as expected, with the _id and _rev fields
|
||||||
|
ctx.request.body = await addRev({ _id: ctx.params.rowId })
|
||||||
|
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
|
||||||
|
ctx.body = { row: ctx.row }
|
||||||
|
}
|
||||||
|
|
47
packages/server/src/api/controllers/public/utils.js
Normal file
47
packages/server/src/api/controllers/public/utils.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
const { getDocParams } = require("@budibase/backend-core/db")
|
||||||
|
|
||||||
|
exports.addRev = async body => {
|
||||||
|
if (!body._id) {
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
const db = getAppDB()
|
||||||
|
const dbDoc = await db.get(body._id)
|
||||||
|
body._rev = dbDoc._rev
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.search = (docs, key, value) => {
|
||||||
|
if (!value || typeof value !== "string") {
|
||||||
|
return docs
|
||||||
|
}
|
||||||
|
value = value.toLowerCase()
|
||||||
|
const filtered = []
|
||||||
|
for (let doc of docs) {
|
||||||
|
if (typeof doc[key] !== "string") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const toTest = doc[key].toLowerCase()
|
||||||
|
if (toTest.startsWith(value)) {
|
||||||
|
filtered.push(doc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a case insensitive search on a document type, using the
|
||||||
|
* provided key and value. This will be a string based search,
|
||||||
|
* using the startsWith function.
|
||||||
|
*/
|
||||||
|
exports.searchDocs = async (docType, key, value) => {
|
||||||
|
const db = getAppDB()
|
||||||
|
const docs = (
|
||||||
|
await db.allDocs(
|
||||||
|
getDocParams(docType, null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(row => row.doc)
|
||||||
|
return exports.search(docs, key, value)
|
||||||
|
}
|
|
@ -46,10 +46,10 @@ function applyRoutes(endpoints, permType, resource, subResource = null) {
|
||||||
addToRouter(endpoints.write)
|
addToRouter(endpoints.write)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
|
|
||||||
applyRoutes(appEndpoints, PermissionTypes.APP, "appId")
|
applyRoutes(appEndpoints, PermissionTypes.APP, "appId")
|
||||||
applyRoutes(tableEndpoints, PermissionTypes.TABLE, "tableId")
|
applyRoutes(tableEndpoints, PermissionTypes.TABLE, "tableId")
|
||||||
applyRoutes(userEndpoints, PermissionTypes.USER, "userId")
|
applyRoutes(userEndpoints, PermissionTypes.USER, "userId")
|
||||||
applyRoutes(queryEndpoints, PermissionTypes.QUERY, "queryId")
|
applyRoutes(queryEndpoints, PermissionTypes.QUERY, "queryId")
|
||||||
|
//applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
|
||||||
|
|
||||||
module.exports = publicRouter
|
module.exports = publicRouter
|
||||||
|
|
|
@ -53,12 +53,34 @@ read.push(new Endpoint("post", "/queries/search", controller.search))
|
||||||
* content:
|
* content:
|
||||||
* application/json:
|
* application/json:
|
||||||
* schema:
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
* type: array
|
* type: array
|
||||||
|
* description: The data retrieved from the query.
|
||||||
* items:
|
* items:
|
||||||
* type: object
|
* 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.
|
||||||
* examples:
|
* examples:
|
||||||
* query:
|
* REST:
|
||||||
* $ref: '#/components/examples/query'
|
* $ref: '#/components/examples/restResponse'
|
||||||
|
* SQL:
|
||||||
|
* $ref: '#/components/examples/sqlResponse'
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
write.push(new Endpoint("post", "/queries/:queryId", controller.execute))
|
write.push(new Endpoint("post", "/queries/:queryId", controller.execute))
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ function request(ctx, request) {
|
||||||
delete request.body
|
delete request.body
|
||||||
}
|
}
|
||||||
if (ctx && ctx.headers) {
|
if (ctx && ctx.headers) {
|
||||||
request.headers.cookie = ctx.headers.cookie
|
request.headers = ctx.headers
|
||||||
}
|
}
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue