1
0
Fork 0
mirror of synced 2024-07-15 03:05:57 +12:00

Merge pull request #8783 from Budibase/refactor/backend-core-import

Backend-core usage refactor + Typescript conversions for server
This commit is contained in:
Michael Drury 2022-11-23 13:04:13 +00:00 committed by GitHub
commit ff64622c5d
155 changed files with 2120 additions and 1968 deletions

View file

@ -171,7 +171,7 @@ export function getGlobalUserParams(globalId: any, otherProps: any = {}) {
/** /**
* Gets parameters for retrieving users, this is a utility function for the getDocParams function. * Gets parameters for retrieving users, this is a utility function for the getDocParams function.
*/ */
export function getUserMetadataParams(userId?: string, otherProps = {}) { export function getUserMetadataParams(userId?: string | null, otherProps = {}) {
return getRowParams(InternalTable.USER_METADATA, userId, otherProps) return getRowParams(InternalTable.USER_METADATA, userId, otherProps)
} }
@ -244,7 +244,7 @@ export function getTemplateParams(
* Generates a new role ID. * Generates a new role ID.
* @returns {string} The new role ID which the role doc can be stored under. * @returns {string} The new role ID which the role doc can be stored under.
*/ */
export function generateRoleID(id: any) { export function generateRoleID(id?: any) {
return `${DocumentType.ROLE}${SEPARATOR}${id || newid()}` return `${DocumentType.ROLE}${SEPARATOR}${id || newid()}`
} }

View file

@ -1,10 +1,13 @@
import { DEFAULT_TENANT_ID } from "../constants" import { DEFAULT_TENANT_ID } from "../constants"
import { doWithDB } from "../db" import {
import { DocumentType, StaticDatabases } from "../db/constants" DocumentType,
import { getAllApps } from "../db/utils" StaticDatabases,
getAllApps,
getGlobalDBName,
doWithDB,
} from "../db"
import environment from "../environment" import environment from "../environment"
import { doInTenant, getTenantIds, getTenantId } from "../tenancy" import { doInTenant, getTenantIds, getTenantId } from "../tenancy"
import { getGlobalDBName } from "../db/tenancy"
import * as context from "../context" import * as context from "../context"
import { DEFINITIONS } from "." import { DEFINITIONS } from "."
import { import {

View file

@ -390,7 +390,7 @@ export const uploadDirectory = async (
return files return files
} }
exports.downloadTarballDirect = async ( export const downloadTarballDirect = async (
url: string, url: string,
path: string, path: string,
headers = {} headers = {}

View file

@ -1,10 +1,5 @@
import { BuiltinPermissionID, PermissionLevel } from "./permissions" import { BuiltinPermissionID, PermissionLevel } from "./permissions"
import { import { generateRoleID, getRoleParams, DocumentType, SEPARATOR } from "../db"
generateRoleID,
getRoleParams,
DocumentType,
SEPARATOR,
} from "../db/utils"
import { getAppDB } from "../context" import { getAppDB } from "../context"
import { doWithDB } from "../db" import { doWithDB } from "../db"
import { Screen, Role as RoleDoc } from "@budibase/types" import { Screen, Role as RoleDoc } from "@budibase/types"
@ -30,20 +25,18 @@ const EXTERNAL_BUILTIN_ROLE_IDS = [
BUILTIN_IDS.PUBLIC, BUILTIN_IDS.PUBLIC,
] ]
export class Role { export class Role implements RoleDoc {
_id: string _id: string
_rev?: string
name: string name: string
permissionId?: string permissionId: string
inherits?: string inherits?: string
permissions = {}
constructor(id: string, name: string) { constructor(id: string, name: string, permissionId: string) {
this._id = id this._id = id
this.name = name this.name = name
}
addPermission(permissionId: string) {
this.permissionId = permissionId this.permissionId = permissionId
return this
} }
addInheritance(inherits: string) { addInheritance(inherits: string) {
@ -53,24 +46,26 @@ export class Role {
} }
const BUILTIN_ROLES = { const BUILTIN_ROLES = {
ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin") ADMIN: new Role(
.addPermission(BuiltinPermissionID.ADMIN) BUILTIN_IDS.ADMIN,
.addInheritance(BUILTIN_IDS.POWER), "Admin",
POWER: new Role(BUILTIN_IDS.POWER, "Power")
.addPermission(BuiltinPermissionID.POWER)
.addInheritance(BUILTIN_IDS.BASIC),
BASIC: new Role(BUILTIN_IDS.BASIC, "Basic")
.addPermission(BuiltinPermissionID.WRITE)
.addInheritance(BUILTIN_IDS.PUBLIC),
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission(
BuiltinPermissionID.PUBLIC
),
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission(
BuiltinPermissionID.ADMIN BuiltinPermissionID.ADMIN
), ).addInheritance(BUILTIN_IDS.POWER),
POWER: new Role(
BUILTIN_IDS.POWER,
"Power",
BuiltinPermissionID.POWER
).addInheritance(BUILTIN_IDS.BASIC),
BASIC: new Role(
BUILTIN_IDS.BASIC,
"Basic",
BuiltinPermissionID.WRITE
).addInheritance(BUILTIN_IDS.PUBLIC),
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public", BuiltinPermissionID.PUBLIC),
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder", BuiltinPermissionID.ADMIN),
} }
export function getBuiltinRoles() { export function getBuiltinRoles(): { [key: string]: RoleDoc } {
return cloneDeep(BUILTIN_ROLES) return cloneDeep(BUILTIN_ROLES)
} }
@ -104,7 +99,7 @@ export function builtinRoleToNumber(id?: string) {
if (!role) { if (!role) {
break break
} }
role = builtins[role.inherits] role = builtins[role.inherits!]
count++ count++
} while (role !== null) } while (role !== null)
return count return count
@ -129,12 +124,12 @@ export async function roleToNumber(id?: string) {
/** /**
* Returns whichever builtin roleID is lower. * Returns whichever builtin roleID is lower.
*/ */
export function lowerBuiltinRoleID(roleId1?: string, roleId2?: string) { export function lowerBuiltinRoleID(roleId1?: string, roleId2?: string): string {
if (!roleId1) { if (!roleId1) {
return roleId2 return roleId2 as string
} }
if (!roleId2) { if (!roleId2) {
return roleId1 return roleId1 as string
} }
return builtinRoleToNumber(roleId1) > builtinRoleToNumber(roleId2) return builtinRoleToNumber(roleId1) > builtinRoleToNumber(roleId2)
? roleId2 ? roleId2

View file

@ -3,7 +3,7 @@ const yargs = require("yargs")
const fs = require("fs") const fs = require("fs")
const { join } = require("path") const { join } = require("path")
require("../src/db").init() require("../src/db").init()
const { doWithDB } = require("@budibase/backend-core/db") const { db: dbCore } = require("@budibase/backend-core")
// load environment // load environment
const env = require("../src/environment") const env = require("../src/environment")
const { const {
@ -48,7 +48,7 @@ yargs
const writeStream = fs.createWriteStream(join(exportPath, "dump.text")) const writeStream = fs.createWriteStream(join(exportPath, "dump.text"))
// perform couch dump // perform couch dump
await doWithDB(appId, async db => { await dbCore.doWithDB(appId, async db => {
return db.dump(writeStream, { return db.dump(writeStream, {
filter: doc => filter: doc =>
!( !(

View file

@ -1,10 +1,10 @@
const { StaticDatabases } = require("@budibase/backend-core/db") import { db as dbCore, tenancy } from "@budibase/backend-core"
const { getGlobalDB } = require("@budibase/backend-core/tenancy") import { BBContext, Document } from "@budibase/types"
const KEYS_DOC = StaticDatabases.GLOBAL.docs.apiKeys const KEYS_DOC = dbCore.StaticDatabases.GLOBAL.docs.apiKeys
async function getBuilderMainDoc() { async function getBuilderMainDoc() {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
try { try {
return await db.get(KEYS_DOC) return await db.get(KEYS_DOC)
} catch (err) { } catch (err) {
@ -15,24 +15,24 @@ async function getBuilderMainDoc() {
} }
} }
async function setBuilderMainDoc(doc) { async function setBuilderMainDoc(doc: Document) {
// make sure to override the ID // make sure to override the ID
doc._id = KEYS_DOC doc._id = KEYS_DOC
const db = getGlobalDB() const db = tenancy.getGlobalDB()
return db.put(doc) return db.put(doc)
} }
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
try { try {
const mainDoc = await getBuilderMainDoc() const mainDoc = await getBuilderMainDoc()
ctx.body = mainDoc.apiKeys ? mainDoc.apiKeys : {} ctx.body = mainDoc.apiKeys ? mainDoc.apiKeys : {}
} catch (err) { } catch (err: any) {
/* istanbul ignore next */ /* istanbul ignore next */
ctx.throw(400, err) ctx.throw(400, err)
} }
} }
exports.update = async function (ctx) { export async function update(ctx: BBContext) {
const key = ctx.params.key const key = ctx.params.key
const value = ctx.request.body.value const value = ctx.request.body.value
@ -47,7 +47,7 @@ exports.update = async function (ctx) {
_id: resp.id, _id: resp.id,
_rev: resp.rev, _rev: resp.rev,
} }
} catch (err) { } catch (err: any) {
/* istanbul ignore next */ /* istanbul ignore next */
ctx.throw(400, err) ctx.throw(400, err)
} }

View file

@ -3,7 +3,7 @@ import { InternalTables } from "../../db/utils"
import { getFullUser } from "../../utilities/users" import { getFullUser } from "../../utilities/users"
import { roles, context } from "@budibase/backend-core" import { roles, context } from "@budibase/backend-core"
import { groups } from "@budibase/pro" import { groups } from "@budibase/pro"
import { ContextUser, User } from "@budibase/types" import { ContextUser, User, Row } from "@budibase/types"
const PUBLIC_ROLE = roles.BUILTIN_ROLE_IDS.PUBLIC const PUBLIC_ROLE = roles.BUILTIN_ROLE_IDS.PUBLIC
@ -43,7 +43,7 @@ export async function fetchSelf(ctx: any) {
try { try {
const userTable = await db.get(InternalTables.USER_METADATA) const userTable = await db.get(InternalTables.USER_METADATA)
// specifically needs to make sure is enriched // specifically needs to make sure is enriched
ctx.body = await outputProcessing(userTable, user) ctx.body = await outputProcessing(userTable, user as Row)
} catch (err: any) { } catch (err: any) {
let response let response
// user didn't exist in app, don't pretend they do // user didn't exist in app, don't pretend they do

View file

@ -1,26 +1,21 @@
const actions = require("../../automations/actions") import actions from "../../automations/actions"
const triggers = require("../../automations/triggers") import triggers from "../../automations/triggers"
const { import {
getAutomationParams, getAutomationParams,
generateAutomationID, generateAutomationID,
DocumentType, DocumentType,
} = require("../../db/utils") } from "../../db/utils"
const { import {
checkForWebhooks, checkForWebhooks,
updateTestHistory, updateTestHistory,
removeDeprecated, removeDeprecated,
} = require("../../automations/utils") } from "../../automations/utils"
const { deleteEntityMetadata } = require("../../utilities") import { deleteEntityMetadata } from "../../utilities"
const { MetadataTypes } = require("../../constants") import { MetadataTypes } from "../../constants"
const { setTestFlag, clearTestFlag } = require("../../utilities/redis") import { setTestFlag, clearTestFlag } from "../../utilities/redis"
const { import { context, cache, events } from "@budibase/backend-core"
getAppDB, import { automations } from "@budibase/pro"
getProdAppDB, import { Automation, BBContext } from "@budibase/types"
doInAppContext,
} = require("@budibase/backend-core/context")
const { events } = require("@budibase/backend-core")
const { app } = require("@budibase/backend-core/cache")
const { automations } = require("@budibase/pro")
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS) const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS)
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS) const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
@ -31,7 +26,7 @@ const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
* * * *
*************************/ *************************/
async function cleanupAutomationMetadata(automationId) { async function cleanupAutomationMetadata(automationId: string) {
await deleteEntityMetadata(MetadataTypes.AUTOMATION_TEST_INPUT, automationId) await deleteEntityMetadata(MetadataTypes.AUTOMATION_TEST_INPUT, automationId)
await deleteEntityMetadata( await deleteEntityMetadata(
MetadataTypes.AUTOMATION_TEST_HISTORY, MetadataTypes.AUTOMATION_TEST_HISTORY,
@ -39,7 +34,7 @@ async function cleanupAutomationMetadata(automationId) {
) )
} }
function cleanAutomationInputs(automation) { function cleanAutomationInputs(automation: Automation) {
if (automation == null) { if (automation == null) {
return automation return automation
} }
@ -63,14 +58,14 @@ function cleanAutomationInputs(automation) {
return automation return automation
} }
exports.create = async function (ctx) { export async function create(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
let automation = ctx.request.body let automation = ctx.request.body
automation.appId = ctx.appId automation.appId = ctx.appId
// call through to update if already exists // call through to update if already exists
if (automation._id && automation._rev) { if (automation._id && automation._rev) {
return exports.update(ctx) return update(ctx)
} }
automation._id = generateAutomationID() automation._id = generateAutomationID()
@ -97,17 +92,23 @@ exports.create = async function (ctx) {
} }
} }
const getNewSteps = (oldAutomation, automation) => { export function getNewSteps(oldAutomation: Automation, automation: Automation) {
const oldStepIds = oldAutomation.definition.steps.map(s => s.id) const oldStepIds = oldAutomation.definition.steps.map(s => s.id)
return automation.definition.steps.filter(s => !oldStepIds.includes(s.id)) return automation.definition.steps.filter(s => !oldStepIds.includes(s.id))
} }
const getDeletedSteps = (oldAutomation, automation) => { export function getDeletedSteps(
oldAutomation: Automation,
automation: Automation
) {
const stepIds = automation.definition.steps.map(s => s.id) const stepIds = automation.definition.steps.map(s => s.id)
return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id)) return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id))
} }
const handleStepEvents = async (oldAutomation, automation) => { export async function handleStepEvents(
oldAutomation: Automation,
automation: Automation
) {
// new steps // new steps
const newSteps = getNewSteps(oldAutomation, automation) const newSteps = getNewSteps(oldAutomation, automation)
for (let step of newSteps) { for (let step of newSteps) {
@ -121,8 +122,8 @@ const handleStepEvents = async (oldAutomation, automation) => {
} }
} }
exports.update = async function (ctx) { export async function update(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
let automation = ctx.request.body let automation = ctx.request.body
automation.appId = ctx.appId automation.appId = ctx.appId
const oldAutomation = await db.get(automation._id) const oldAutomation = await db.get(automation._id)
@ -146,9 +147,8 @@ exports.update = async function (ctx) {
if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) { if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) {
await events.automation.triggerUpdated(automation) await events.automation.triggerUpdated(automation)
await deleteEntityMetadata( await deleteEntityMetadata(
ctx.appId,
MetadataTypes.AUTOMATION_TEST_INPUT, MetadataTypes.AUTOMATION_TEST_INPUT,
automation._id automation._id!
) )
} }
@ -165,8 +165,8 @@ exports.update = async function (ctx) {
} }
} }
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const response = await db.allDocs( const response = await db.allDocs(
getAutomationParams(null, { getAutomationParams(null, {
include_docs: true, include_docs: true,
@ -175,13 +175,13 @@ exports.fetch = async function (ctx) {
ctx.body = response.rows.map(row => row.doc) ctx.body = response.rows.map(row => row.doc)
} }
exports.find = async function (ctx) { export async function find(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
ctx.body = await db.get(ctx.params.id) ctx.body = await db.get(ctx.params.id)
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const automationId = ctx.params.id const automationId = ctx.params.id
const oldAutomation = await db.get(automationId) const oldAutomation = await db.get(automationId)
await checkForWebhooks({ await checkForWebhooks({
@ -193,14 +193,14 @@ exports.destroy = async function (ctx) {
await events.automation.deleted(oldAutomation) await events.automation.deleted(oldAutomation)
} }
exports.logSearch = async function (ctx) { export async function logSearch(ctx: BBContext) {
ctx.body = await automations.logs.logSearch(ctx.request.body) ctx.body = await automations.logs.logSearch(ctx.request.body)
} }
exports.clearLogError = async function (ctx) { export async function clearLogError(ctx: BBContext) {
const { automationId, appId } = ctx.request.body const { automationId, appId } = ctx.request.body
await doInAppContext(appId, async () => { await context.doInAppContext(appId, async () => {
const db = getProdAppDB() const db = context.getProdAppDB()
const metadata = await db.get(DocumentType.APP_METADATA) const metadata = await db.get(DocumentType.APP_METADATA)
if (!automationId) { if (!automationId) {
delete metadata.automationErrors delete metadata.automationErrors
@ -211,20 +211,20 @@ exports.clearLogError = async function (ctx) {
delete metadata.automationErrors[automationId] delete metadata.automationErrors[automationId]
} }
await db.put(metadata) await db.put(metadata)
await app.invalidateAppMetadata(metadata.appId, metadata) await cache.app.invalidateAppMetadata(metadata.appId, metadata)
ctx.body = { message: `Error logs cleared.` } ctx.body = { message: `Error logs cleared.` }
}) })
} }
exports.getActionList = async function (ctx) { export async function getActionList(ctx: BBContext) {
ctx.body = ACTION_DEFS ctx.body = ACTION_DEFS
} }
exports.getTriggerList = async function (ctx) { export async function getTriggerList(ctx: BBContext) {
ctx.body = TRIGGER_DEFS ctx.body = TRIGGER_DEFS
} }
module.exports.getDefinitionList = async function (ctx) { export async function getDefinitionList(ctx: BBContext) {
ctx.body = { ctx.body = {
trigger: TRIGGER_DEFS, trigger: TRIGGER_DEFS,
action: ACTION_DEFS, action: ACTION_DEFS,
@ -237,8 +237,8 @@ module.exports.getDefinitionList = async function (ctx) {
* * * *
*********************/ *********************/
exports.trigger = async function (ctx) { export async function trigger(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
let automation = await db.get(ctx.params.id) let automation = await db.get(ctx.params.id)
await triggers.externalTrigger(automation, { await triggers.externalTrigger(automation, {
...ctx.request.body, ...ctx.request.body,
@ -250,7 +250,7 @@ exports.trigger = async function (ctx) {
} }
} }
function prepareTestInput(input) { function prepareTestInput(input: any) {
// prepare the test parameters // prepare the test parameters
if (input.id && input.row) { if (input.id && input.row) {
input.row._id = input.id input.row._id = input.id
@ -261,8 +261,8 @@ function prepareTestInput(input) {
return input return input
} }
exports.test = async function (ctx) { export async function test(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
let automation = await db.get(ctx.params.id) let automation = await db.get(ctx.params.id)
await setTestFlag(automation._id) await setTestFlag(automation._id)
const testInput = prepareTestInput(ctx.request.body) const testInput = prepareTestInput(ctx.request.body)

View file

@ -1,14 +1,14 @@
const env = require("../../environment") import env from "../../environment"
const { getAllApps, getGlobalDBName } = require("@budibase/backend-core/db") import { db as dbCore, tenancy } from "@budibase/backend-core"
const { getGlobalDB } = require("@budibase/backend-core/tenancy") import { streamFile } from "../../utilities/fileSystem"
const { streamFile } = require("../../utilities/fileSystem") import { stringToReadStream } from "../../utilities"
const { stringToReadStream } = require("../../utilities") import { getDocParams, DocumentType, isDevAppID } from "../../db/utils"
const { getDocParams, DocumentType, isDevAppID } = require("../../db/utils") import { create } from "./application"
const { create } = require("./application") import { join } from "path"
const { join } = require("path") import { App, BBContext, Database } from "@budibase/types"
const sdk = require("../../sdk") import sdk from "../../sdk"
async function createApp(appName, appDirectory) { async function createApp(appName: string, appDirectory: string) {
const ctx = { const ctx = {
request: { request: {
body: { body: {
@ -25,7 +25,7 @@ async function createApp(appName, appDirectory) {
return create(ctx) return create(ctx)
} }
async function getAllDocType(db, docType) { async function getAllDocType(db: Database, docType: string) {
const response = await db.allDocs( const response = await db.allDocs(
getDocParams(docType, null, { getDocParams(docType, null, {
include_docs: true, include_docs: true,
@ -34,19 +34,19 @@ async function getAllDocType(db, docType) {
return response.rows.map(row => row.doc) return response.rows.map(row => row.doc)
} }
exports.exportApps = async ctx => { export async function exportApps(ctx: BBContext) {
if (env.SELF_HOSTED || !env.MULTI_TENANCY) { if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.") ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.")
} }
const apps = await getAllApps({ all: true }) const apps = (await dbCore.getAllApps({ all: true })) as App[]
const globalDBString = await sdk.backups.exportDB(getGlobalDBName(), { const globalDBString = await sdk.backups.exportDB(dbCore.getGlobalDBName(), {
filter: doc => !doc._id.startsWith(DocumentType.USER), filter: (doc: any) => !doc._id.startsWith(DocumentType.USER),
}) })
// only export the dev apps as they will be the latest, the user can republish the apps // only export the dev apps as they will be the latest, the user can republish the apps
// in their self-hosted environment // in their self-hosted environment
let appMetadata = apps let appMetadata = apps
.filter(app => isDevAppID(app.appId || app._id)) .filter((app: App) => isDevAppID(app.appId || app._id))
.map(app => ({ appId: app.appId || app._id, name: app.name })) .map((app: App) => ({ appId: (app.appId || app._id)!, name: app.name }))
const tmpPath = await sdk.backups.exportMultipleApps( const tmpPath = await sdk.backups.exportMultipleApps(
appMetadata, appMetadata,
globalDBString globalDBString
@ -56,25 +56,25 @@ exports.exportApps = async ctx => {
ctx.body = streamFile(tmpPath) ctx.body = streamFile(tmpPath)
} }
async function hasBeenImported() { async function checkHasBeenImported() {
if (!env.SELF_HOSTED || env.MULTI_TENANCY) { if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
return true return true
} }
const apps = await getAllApps({ all: true }) const apps = await dbCore.getAllApps({ all: true })
return apps.length !== 0 return apps.length !== 0
} }
exports.hasBeenImported = async ctx => { export async function hasBeenImported(ctx: BBContext) {
ctx.body = { ctx.body = {
imported: await hasBeenImported(), imported: await checkHasBeenImported(),
} }
} }
exports.importApps = async ctx => { export async function importApps(ctx: BBContext) {
if (!env.SELF_HOSTED || env.MULTI_TENANCY) { if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
ctx.throw(400, "Importing only allowed in self hosted environments.") ctx.throw(400, "Importing only allowed in self hosted environments.")
} }
const beenImported = await hasBeenImported() const beenImported = await checkHasBeenImported()
if (beenImported || !ctx.request.files || !ctx.request.files.importFile) { if (beenImported || !ctx.request.files || !ctx.request.files.importFile) {
ctx.throw( ctx.throw(
400, 400,
@ -90,7 +90,7 @@ exports.importApps = async ctx => {
const globalDbImport = sdk.backups.getGlobalDBFile(tmpPath) const globalDbImport = sdk.backups.getGlobalDBFile(tmpPath)
const appNames = sdk.backups.getListOfAppsInMulti(tmpPath) const appNames = sdk.backups.getListOfAppsInMulti(tmpPath)
const globalDb = getGlobalDB() const globalDb = tenancy.getGlobalDB()
// load the global db first // load the global db first
await globalDb.load(stringToReadStream(globalDbImport)) await globalDb.load(stringToReadStream(globalDbImport))
for (let appName of appNames) { for (let appName of appNames) {

View file

@ -2,8 +2,9 @@ import { DocumentType } from "../../db/utils"
import { Plugin } from "@budibase/types" import { Plugin } from "@budibase/types"
import { db as dbCore, context, tenancy } from "@budibase/backend-core" import { db as dbCore, context, tenancy } from "@budibase/backend-core"
import { getComponentLibraryManifest } from "../../utilities/fileSystem" import { getComponentLibraryManifest } from "../../utilities/fileSystem"
import { BBContext } from "@budibase/types"
exports.fetchAppComponentDefinitions = async function (ctx: any) { export async function fetchAppComponentDefinitions(ctx: BBContext) {
try { try {
const db = context.getAppDB() const db = context.getAppDB()
const app = await db.get(DocumentType.APP_METADATA) const app = await db.get(DocumentType.APP_METADATA)

View file

@ -1,30 +1,29 @@
const { import {
generateDatasourceID, generateDatasourceID,
getDatasourceParams, getDatasourceParams,
getQueryParams, getQueryParams,
DocumentType, DocumentType,
BudibaseInternalDB, BudibaseInternalDB,
getTableParams, getTableParams,
} = require("../../db/utils") } from "../../db/utils"
const { destroy: tableDestroy } = require("./table/internal") import { destroy as tableDestroy } from "./table/internal"
const { BuildSchemaErrors, InvalidColumns } = require("../../constants") import { BuildSchemaErrors, InvalidColumns } from "../../constants"
const { getIntegration } = require("../../integrations") import { getIntegration } from "../../integrations"
const { getDatasourceAndQuery } = require("./row/utils") import { getDatasourceAndQuery } from "./row/utils"
const { invalidateDynamicVariables } = require("../../threads/utils") import { invalidateDynamicVariables } from "../../threads/utils"
const { getAppDB } = require("@budibase/backend-core/context") import { db as dbCore, context, events } from "@budibase/backend-core"
const { events } = require("@budibase/backend-core") import { BBContext, Datasource, Row } from "@budibase/types"
const { db: dbCore } = require("@budibase/backend-core")
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
// Get internal tables // Get internal tables
const db = getAppDB() const db = context.getAppDB()
const internalTables = await db.allDocs( const internalTables = await db.allDocs(
getTableParams(null, { getTableParams(null, {
include_docs: true, include_docs: true,
}) })
) )
const internal = internalTables.rows.reduce((acc, row) => { const internal = internalTables.rows.reduce((acc: any, row: Row) => {
const sourceId = row.doc.sourceId || "bb_internal" const sourceId = row.doc.sourceId || "bb_internal"
acc[sourceId] = acc[sourceId] || [] acc[sourceId] = acc[sourceId] || []
acc[sourceId].push(row.doc) acc[sourceId].push(row.doc)
@ -60,8 +59,8 @@ exports.fetch = async function (ctx) {
ctx.body = [bbInternalDb, ...datasources] ctx.body = [bbInternalDb, ...datasources]
} }
exports.buildSchemaFromDb = async function (ctx) { export async function buildSchemaFromDb(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const datasource = await db.get(ctx.params.datasourceId) const datasource = await db.get(ctx.params.datasourceId)
const tablesFilter = ctx.request.body.tablesFilter const tablesFilter = ctx.request.body.tablesFilter
@ -72,7 +71,9 @@ exports.buildSchemaFromDb = async function (ctx) {
} }
for (let key in tables) { for (let key in tables) {
if ( if (
tablesFilter.some(filter => filter.toLowerCase() === key.toLowerCase()) tablesFilter.some(
(filter: any) => filter.toLowerCase() === key.toLowerCase()
)
) { ) {
datasource.entities[key] = tables[key] datasource.entities[key] = tables[key]
} }
@ -85,7 +86,7 @@ exports.buildSchemaFromDb = async function (ctx) {
const dbResp = await db.put(datasource) const dbResp = await db.put(datasource)
datasource._rev = dbResp.rev datasource._rev = dbResp.rev
const response = { datasource } const response: any = { datasource }
if (error) { if (error) {
response.error = error response.error = error
} }
@ -95,9 +96,9 @@ exports.buildSchemaFromDb = async function (ctx) {
/** /**
* Make sure all datasource entities have a display name selected * Make sure all datasource entities have a display name selected
*/ */
const setDefaultDisplayColumns = datasource => { function setDefaultDisplayColumns(datasource: Datasource) {
// //
for (let entity of Object.values(datasource.entities)) { for (let entity of Object.values(datasource.entities || {})) {
if (entity.primaryDisplay) { if (entity.primaryDisplay) {
continue continue
} }
@ -113,9 +114,12 @@ const setDefaultDisplayColumns = datasource => {
/** /**
* Check for variables that have been updated or removed and invalidate them. * Check for variables that have been updated or removed and invalidate them.
*/ */
const invalidateVariables = async (existingDatasource, updatedDatasource) => { async function invalidateVariables(
const existingVariables = existingDatasource.config.dynamicVariables existingDatasource: Datasource,
const updatedVariables = updatedDatasource.config.dynamicVariables updatedDatasource: Datasource
) {
const existingVariables: any = existingDatasource.config?.dynamicVariables
const updatedVariables: any = updatedDatasource.config?.dynamicVariables
const toInvalidate = [] const toInvalidate = []
if (!existingVariables) { if (!existingVariables) {
@ -127,9 +131,9 @@ const invalidateVariables = async (existingDatasource, updatedDatasource) => {
toInvalidate.push(...existingVariables) toInvalidate.push(...existingVariables)
} else { } else {
// invaldate changed / removed // invaldate changed / removed
existingVariables.forEach(existing => { existingVariables.forEach((existing: any) => {
const unchanged = updatedVariables.find( const unchanged = updatedVariables.find(
updated => (updated: any) =>
existing.name === updated.name && existing.name === updated.name &&
existing.queryId === updated.queryId && existing.queryId === updated.queryId &&
existing.value === updated.value existing.value === updated.value
@ -142,8 +146,8 @@ const invalidateVariables = async (existingDatasource, updatedDatasource) => {
await invalidateDynamicVariables(toInvalidate) await invalidateDynamicVariables(toInvalidate)
} }
exports.update = async function (ctx) { export async function update(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const datasourceId = ctx.params.datasourceId const datasourceId = ctx.params.datasourceId
let datasource = await db.get(datasourceId) let datasource = await db.get(datasourceId)
const auth = datasource.config.auth const auth = datasource.config.auth
@ -171,8 +175,8 @@ exports.update = async function (ctx) {
ctx.body = { datasource } ctx.body = { datasource }
} }
exports.save = async function (ctx) { export async function save(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const plus = ctx.request.body.datasource.plus const plus = ctx.request.body.datasource.plus
const fetchSchema = ctx.request.body.fetchSchema const fetchSchema = ctx.request.body.fetchSchema
@ -202,15 +206,15 @@ exports.save = async function (ctx) {
} }
} }
const response = { datasource } const response: any = { datasource }
if (schemaError) { if (schemaError) {
response.error = schemaError response.error = schemaError
} }
ctx.body = response ctx.body = response
} }
const destroyInternalTablesBySourceId = async datasourceId => { async function destroyInternalTablesBySourceId(datasourceId: string) {
const db = getAppDB() const db = context.getAppDB()
// Get all internal tables // Get all internal tables
const internalTables = await db.allDocs( const internalTables = await db.allDocs(
@ -220,12 +224,15 @@ const destroyInternalTablesBySourceId = async datasourceId => {
) )
// Filter by datasource and return the docs. // Filter by datasource and return the docs.
const datasourceTableDocs = internalTables.rows.reduce((acc, table) => { const datasourceTableDocs = internalTables.rows.reduce(
if (table.doc.sourceId == datasourceId) { (acc: any, table: any) => {
acc.push(table.doc) if (table.doc.sourceId == datasourceId) {
} acc.push(table.doc)
return acc }
}, []) return acc
},
[]
)
// Destroy the tables. // Destroy the tables.
for (const table of datasourceTableDocs) { for (const table of datasourceTableDocs) {
@ -237,8 +244,8 @@ const destroyInternalTablesBySourceId = async datasourceId => {
} }
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const datasourceId = ctx.params.datasourceId const datasourceId = ctx.params.datasourceId
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
@ -249,7 +256,7 @@ exports.destroy = async function (ctx) {
} else { } else {
const queries = await db.allDocs(getQueryParams(datasourceId, null)) const queries = await db.allDocs(getQueryParams(datasourceId, null))
await db.bulkDocs( await db.bulkDocs(
queries.rows.map(row => ({ queries.rows.map((row: any) => ({
_id: row.id, _id: row.id,
_rev: row.value.rev, _rev: row.value.rev,
_deleted: true, _deleted: true,
@ -265,28 +272,28 @@ exports.destroy = async function (ctx) {
ctx.status = 200 ctx.status = 200
} }
exports.find = async function (ctx) { export async function find(ctx: BBContext) {
const database = getAppDB() const database = context.getAppDB()
ctx.body = await database.get(ctx.params.datasourceId) ctx.body = await database.get(ctx.params.datasourceId)
} }
// dynamic query functionality // dynamic query functionality
exports.query = async function (ctx) { export async function query(ctx: BBContext) {
const queryJson = ctx.request.body const queryJson = ctx.request.body
try { try {
ctx.body = await getDatasourceAndQuery(queryJson) ctx.body = await getDatasourceAndQuery(queryJson)
} catch (err) { } catch (err: any) {
ctx.throw(400, err) ctx.throw(400, err)
} }
} }
function getErrorTables(errors, errorType) { function getErrorTables(errors: any, errorType: string) {
return Object.entries(errors) return Object.entries(errors)
.filter(entry => entry[1] === errorType) .filter(entry => entry[1] === errorType)
.map(([name]) => name) .map(([name]) => name)
} }
function updateError(error, newError, tables) { function updateError(error: any, newError: any, tables: string[]) {
if (!error) { if (!error) {
error = "" error = ""
} }
@ -297,7 +304,7 @@ function updateError(error, newError, tables) {
return error return error
} }
const buildSchemaHelper = async datasource => { async function buildSchemaHelper(datasource: Datasource) {
const Connector = await getIntegration(datasource.source) const Connector = await getIntegration(datasource.source)
// Connect to the DB and build the schema // Connect to the DB and build the schema

View file

@ -1,15 +1,20 @@
const newid = require("../../../db/newid") import newid from "../../../db/newid"
const { getAppId } = require("@budibase/backend-core/context") import { context } from "@budibase/backend-core"
/** /**
* This is used to pass around information about the deployment that is occurring * This is used to pass around information about the deployment that is occurring
*/ */
class Deployment { export default class Deployment {
_id: string
verification: any
status?: string
err?: any
constructor(id = null) { constructor(id = null) {
this._id = id || newid() this._id = id || newid()
} }
setVerification(verification) { setVerification(verification: any) {
if (!verification) { if (!verification) {
return return
} }
@ -20,14 +25,14 @@ class Deployment {
return this.verification return this.verification
} }
setStatus(status, err = null) { setStatus(status: string, err?: any) {
this.status = status this.status = status
if (err) { if (err) {
this.err = err this.err = err
} }
} }
fromJSON(json) { fromJSON(json: any) {
if (json.verification) { if (json.verification) {
this.setVerification(json.verification) this.setVerification(json.verification)
} }
@ -37,9 +42,9 @@ class Deployment {
} }
getJSON() { getJSON() {
const obj = { const obj: any = {
_id: this._id, _id: this._id,
appId: getAppId(), appId: context.getAppId(),
status: this.status, status: this.status,
} }
if (this.err) { if (this.err) {
@ -51,5 +56,3 @@ class Deployment {
return obj return obj
} }
} }
module.exports = Deployment

View file

@ -1,10 +1,10 @@
const { EMPTY_LAYOUT } = require("../../constants/layouts") import { EMPTY_LAYOUT } from "../../constants/layouts"
const { generateLayoutID, getScreenParams } = require("../../db/utils") import { generateLayoutID, getScreenParams } from "../../db/utils"
const { getAppDB } = require("@budibase/backend-core/context") import { events, context } from "@budibase/backend-core"
const { events } = require("@budibase/backend-core") import { BBContext } from "@budibase/types"
exports.save = async function (ctx) { export async function save(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
let layout = ctx.request.body let layout = ctx.request.body
if (!layout.props) { if (!layout.props) {
@ -24,8 +24,8 @@ exports.save = async function (ctx) {
ctx.status = 200 ctx.status = 200
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const layoutId = ctx.params.layoutId, const layoutId = ctx.params.layoutId,
layoutRev = ctx.params.layoutRev layoutRev = ctx.params.layoutRev

View file

@ -1,15 +1,16 @@
const { MetadataTypes } = require("../../constants") import { MetadataTypes } from "../../constants"
const { generateMetadataID } = require("../../db/utils") import { generateMetadataID } from "../../db/utils"
const { saveEntityMetadata, deleteEntityMetadata } = require("../../utilities") import { saveEntityMetadata, deleteEntityMetadata } from "../../utilities"
const { getAppDB } = require("@budibase/backend-core/context") import { context } from "@budibase/backend-core"
import { BBContext } from "@budibase/types"
exports.getTypes = async ctx => { export async function getTypes(ctx: BBContext) {
ctx.body = { ctx.body = {
types: MetadataTypes, types: MetadataTypes,
} }
} }
exports.saveMetadata = async ctx => { export async function saveMetadata(ctx: BBContext) {
const { type, entityId } = ctx.params const { type, entityId } = ctx.params
if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) { if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) {
ctx.throw(400, "Cannot save automation history type") ctx.throw(400, "Cannot save automation history type")
@ -17,7 +18,7 @@ exports.saveMetadata = async ctx => {
ctx.body = await saveEntityMetadata(type, entityId, ctx.request.body) ctx.body = await saveEntityMetadata(type, entityId, ctx.request.body)
} }
exports.deleteMetadata = async ctx => { export async function deleteMetadata(ctx: BBContext) {
const { type, entityId } = ctx.params const { type, entityId } = ctx.params
await deleteEntityMetadata(type, entityId) await deleteEntityMetadata(type, entityId)
ctx.body = { ctx.body = {
@ -25,13 +26,13 @@ exports.deleteMetadata = async ctx => {
} }
} }
exports.getMetadata = async ctx => { export async function getMetadata(ctx: BBContext) {
const { type, entityId } = ctx.params const { type, entityId } = ctx.params
const db = getAppDB() const db = context.getAppDB()
const id = generateMetadataID(type, entityId) const id = generateMetadataID(type, entityId)
try { try {
ctx.body = await db.get(id) ctx.body = await db.get(id)
} catch (err) { } catch (err: any) {
if (err.status === 404) { if (err.status === 404) {
ctx.body = {} ctx.body = {}
} else { } else {

View file

@ -1,18 +1,11 @@
const { getBuiltinPermissions } = require("@budibase/backend-core/permissions") import { permissions, roles, context } from "@budibase/backend-core"
const { import { getRoleParams } from "../../db/utils"
isBuiltin, import {
getDBRoleID,
getExternalRoleID,
getBuiltinRoles,
checkForRoleResourceArray,
} = require("@budibase/backend-core/roles")
const { getRoleParams } = require("../../db/utils")
const {
CURRENTLY_SUPPORTED_LEVELS, CURRENTLY_SUPPORTED_LEVELS,
getBasePermissions, getBasePermissions,
} = require("../../utilities/security") } from "../../utilities/security"
const { removeFromArray } = require("../../utilities") import { removeFromArray } from "../../utilities"
const { getAppDB } = require("@budibase/backend-core/context") import { BBContext, Database, Role } from "@budibase/types"
const PermissionUpdateType = { const PermissionUpdateType = {
REMOVE: "remove", REMOVE: "remove",
@ -22,7 +15,7 @@ const PermissionUpdateType = {
const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS
// utility function to stop this repetition - permissions always stored under roles // utility function to stop this repetition - permissions always stored under roles
async function getAllDBRoles(db) { async function getAllDBRoles(db: Database) {
const body = await db.allDocs( const body = await db.allDocs(
getRoleParams(null, { getRoleParams(null, {
include_docs: true, include_docs: true,
@ -32,21 +25,25 @@ async function getAllDBRoles(db) {
} }
async function updatePermissionOnRole( async function updatePermissionOnRole(
appId, appId: string,
{ roleId, resourceId, level }, {
updateType roleId,
resourceId,
level,
}: { roleId: string; resourceId: string; level: string },
updateType: string
) { ) {
const db = getAppDB() const db = context.getAppDB()
const remove = updateType === PermissionUpdateType.REMOVE const remove = updateType === PermissionUpdateType.REMOVE
const isABuiltin = isBuiltin(roleId) const isABuiltin = roles.isBuiltin(roleId)
const dbRoleId = getDBRoleID(roleId) const dbRoleId = roles.getDBRoleID(roleId)
const dbRoles = await getAllDBRoles(db) const dbRoles = await getAllDBRoles(db)
const docUpdates = [] const docUpdates = []
// the permission is for a built in, make sure it exists // the permission is for a built in, make sure it exists
if (isABuiltin && !dbRoles.some(role => role._id === dbRoleId)) { if (isABuiltin && !dbRoles.some(role => role._id === dbRoleId)) {
const builtin = getBuiltinRoles()[roleId] const builtin = roles.getBuiltinRoles()[roleId]
builtin._id = getDBRoleID(builtin._id) builtin._id = roles.getDBRoleID(builtin._id)
dbRoles.push(builtin) dbRoles.push(builtin)
} }
@ -90,41 +87,44 @@ async function updatePermissionOnRole(
} }
const response = await db.bulkDocs(docUpdates) const response = await db.bulkDocs(docUpdates)
return response.map(resp => { return response.map((resp: any) => {
resp._id = getExternalRoleID(resp.id) resp._id = roles.getExternalRoleID(resp.id)
delete resp.id delete resp.id
return resp return resp
}) })
} }
exports.fetchBuiltin = function (ctx) { export function fetchBuiltin(ctx: BBContext) {
ctx.body = Object.values(getBuiltinPermissions()) ctx.body = Object.values(permissions.getBuiltinPermissions())
} }
exports.fetchLevels = function (ctx) { export function fetchLevels(ctx: BBContext) {
// for now only provide the read/write perms externally // for now only provide the read/write perms externally
ctx.body = SUPPORTED_LEVELS ctx.body = SUPPORTED_LEVELS
} }
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const roles = await getAllDBRoles(db) const dbRoles: Role[] = await getAllDBRoles(db)
let permissions = {} let permissions: any = {}
// create an object with structure role ID -> resource ID -> level // create an object with structure role ID -> resource ID -> level
for (let role of roles) { for (let role of dbRoles) {
if (!role.permissions) { if (!role.permissions) {
continue continue
} }
const roleId = getExternalRoleID(role._id) const roleId = roles.getExternalRoleID(role._id)
if (!roleId) {
ctx.throw(400, "Unable to retrieve role")
}
for (let [resource, levelArr] of Object.entries(role.permissions)) { for (let [resource, levelArr] of Object.entries(role.permissions)) {
const levels = Array.isArray(levelArr) ? [levelArr] : levelArr const levels: string[] = Array.isArray(levelArr) ? levelArr : [levelArr]
const perms = {} const perms: Record<string, string> = {}
levels.forEach(level => (perms[level] = roleId)) levels.forEach(level => (perms[level] = roleId!))
permissions[resource] = perms permissions[resource] = perms
} }
} }
// apply the base permissions // apply the base permissions
const finalPermissions = {} const finalPermissions: Record<string, Record<string, string>> = {}
for (let [resource, permission] of Object.entries(permissions)) { for (let [resource, permission] of Object.entries(permissions)) {
const basePerms = getBasePermissions(resource) const basePerms = getBasePermissions(resource)
finalPermissions[resource] = Object.assign(basePerms, permission) finalPermissions[resource] = Object.assign(basePerms, permission)
@ -132,33 +132,36 @@ exports.fetch = async function (ctx) {
ctx.body = finalPermissions ctx.body = finalPermissions
} }
exports.getResourcePerms = async function (ctx) { export async function getResourcePerms(ctx: BBContext) {
const resourceId = ctx.params.resourceId const resourceId = ctx.params.resourceId
const db = getAppDB() const db = context.getAppDB()
const body = await db.allDocs( const body = await db.allDocs(
getRoleParams(null, { getRoleParams(null, {
include_docs: true, include_docs: true,
}) })
) )
const roles = body.rows.map(row => row.doc) const rolesList = body.rows.map(row => row.doc)
let permissions = {} let permissions: Record<string, string> = {}
for (let level of SUPPORTED_LEVELS) { for (let level of SUPPORTED_LEVELS) {
// update the various roleIds in the resource permissions // update the various roleIds in the resource permissions
for (let role of roles) { for (let role of rolesList) {
const rolePerms = checkForRoleResourceArray(role.permissions, resourceId) const rolePerms = roles.checkForRoleResourceArray(
role.permissions,
resourceId
)
if ( if (
rolePerms && rolePerms &&
rolePerms[resourceId] && rolePerms[resourceId] &&
rolePerms[resourceId].indexOf(level) !== -1 rolePerms[resourceId].indexOf(level) !== -1
) { ) {
permissions[level] = getExternalRoleID(role._id) permissions[level] = roles.getExternalRoleID(role._id)!
} }
} }
} }
ctx.body = Object.assign(getBasePermissions(resourceId), permissions) ctx.body = Object.assign(getBasePermissions(resourceId), permissions)
} }
exports.addPermission = async function (ctx) { export async function addPermission(ctx: BBContext) {
ctx.body = await updatePermissionOnRole( ctx.body = await updatePermissionOnRole(
ctx.appId, ctx.appId,
ctx.params, ctx.params,
@ -166,7 +169,7 @@ exports.addPermission = async function (ctx) {
) )
} }
exports.removePermission = async function (ctx) { export async function removePermission(ctx: BBContext) {
ctx.body = await updatePermissionOnRole( ctx.body = await updatePermissionOnRole(
ctx.appId, ctx.appId,
ctx.params, ctx.params,

View file

@ -1,6 +1,5 @@
import { npmUpload, urlUpload, githubUpload, fileUpload } from "./uploaders" import { npmUpload, urlUpload, githubUpload, fileUpload } from "./uploaders"
import { getGlobalDB } from "@budibase/backend-core/tenancy" import { plugins as pluginCore, tenancy } from "@budibase/backend-core"
import { validate } from "@budibase/backend-core/plugins"
import { PluginType, FileType, PluginSource } from "@budibase/types" import { PluginType, FileType, PluginSource } from "@budibase/types"
import env from "../../../environment" import env from "../../../environment"
import { ClientAppSocket } from "../../../websocket" import { ClientAppSocket } from "../../../websocket"
@ -8,7 +7,7 @@ import { db as dbCore } from "@budibase/backend-core"
import { plugins } from "@budibase/pro" import { plugins } from "@budibase/pro"
export async function getPlugins(type?: PluginType) { export async function getPlugins(type?: PluginType) {
const db = getGlobalDB() const db = tenancy.getGlobalDB()
const response = await db.allDocs( const response = await db.allDocs(
dbCore.getPluginParams(null, { dbCore.getPluginParams(null, {
include_docs: true, include_docs: true,
@ -76,7 +75,7 @@ export async function create(ctx: any) {
break break
} }
validate(metadata?.schema) pluginCore.validate(metadata?.schema)
// Only allow components in cloud // Only allow components in cloud
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) { if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
@ -121,7 +120,7 @@ export async function processUploadedPlugin(
source?: PluginSource source?: PluginSource
) { ) {
const { metadata, directory } = await fileUpload(plugin) const { metadata, directory } = await fileUpload(plugin)
validate(metadata?.schema) pluginCore.validate(metadata?.schema)
// Only allow components in cloud // Only allow components in cloud
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) { if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {

View file

@ -1,6 +1,6 @@
import { search as stringSearch, addRev } from "./utils" import { search as stringSearch, addRev } from "./utils"
import { default as controller } from "../table" import * as controller from "../table"
import { Table } from "../../../definitions/common" import { Table } from "@budibase/types"
function fixTable(table: Table, params: any) { function fixTable(table: Table, params: any) {
if (!params || !table) { if (!params || !table) {

View file

@ -5,8 +5,7 @@ import { OpenAPI2 } from "./sources/openapi2"
import { OpenAPI3 } from "./sources/openapi3" import { OpenAPI3 } from "./sources/openapi3"
import { Curl } from "./sources/curl" import { Curl } from "./sources/curl"
// @ts-ignore // @ts-ignore
import { getAppDB } from "@budibase/backend-core/context" import { events, context } from "@budibase/backend-core"
import { events } from "@budibase/backend-core"
import { Datasource, Query } from "@budibase/types" import { Datasource, Query } from "@budibase/types"
interface ImportResult { interface ImportResult {
@ -59,7 +58,7 @@ export class RestImporter {
}) })
// persist queries // persist queries
const db = getAppDB() const db = context.getAppDB()
const response = await db.bulkDocs(queries) const response = await db.bulkDocs(queries)
// create index to seperate queries and errors // create index to seperate queries and errors

View file

@ -1,9 +1,9 @@
const { joiValidator } = require("@budibase/backend-core/auth") import { auth } from "@budibase/backend-core"
const Joi = require("joi") import Joi from "joi"
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("") const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
exports.queryValidation = () => { export function queryValidation() {
return Joi.object({ return Joi.object({
_id: Joi.string(), _id: Joi.string(),
_rev: Joi.string(), _rev: Joi.string(),
@ -25,14 +25,14 @@ exports.queryValidation = () => {
}).unknown(true) }).unknown(true)
} }
exports.generateQueryValidation = () => { export function generateQueryValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(exports.queryValidation()) return auth.joiValidator.body(queryValidation())
} }
exports.generateQueryPreviewValidation = () => { export function generateQueryPreviewValidation() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
_id: OPTIONAL_STRING, _id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING, _rev: OPTIONAL_STRING,
readable: Joi.boolean().optional(), readable: Joi.boolean().optional(),

View file

@ -1,23 +1,21 @@
const { import { roles, context, events } from "@budibase/backend-core"
Role, import {
getRole,
isBuiltin,
getAllRoles,
} = require("@budibase/backend-core/roles")
const {
generateRoleID, generateRoleID,
getUserMetadataParams, getUserMetadataParams,
InternalTables, InternalTables,
} = require("../../db/utils") } from "../../db/utils"
const { getAppDB } = require("@budibase/backend-core/context") import { BBContext, Database } from "@budibase/types"
const { events } = require("@budibase/backend-core")
const UpdateRolesOptions = { const UpdateRolesOptions = {
CREATED: "created", CREATED: "created",
REMOVED: "removed", REMOVED: "removed",
} }
async function updateRolesOnUserTable(db, roleId, updateOption) { async function updateRolesOnUserTable(
db: Database,
roleId: string,
updateOption: string
) {
const table = await db.get(InternalTables.USER_METADATA) const table = await db.get(InternalTables.USER_METADATA)
const schema = table.schema const schema = table.schema
const remove = updateOption === UpdateRolesOptions.REMOVED const remove = updateOption === UpdateRolesOptions.REMOVED
@ -40,27 +38,25 @@ async function updateRolesOnUserTable(db, roleId, updateOption) {
} }
} }
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
ctx.body = await getAllRoles() ctx.body = await roles.getAllRoles()
} }
exports.find = async function (ctx) { export async function find(ctx: BBContext) {
ctx.body = await getRole(ctx.params.roleId) ctx.body = await roles.getRole(ctx.params.roleId)
} }
exports.save = async function (ctx) { export async function save(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
let { _id, name, inherits, permissionId } = ctx.request.body let { _id, name, inherits, permissionId } = ctx.request.body
let isCreate = false let isCreate = false
if (!_id) { if (!_id) {
_id = generateRoleID() _id = generateRoleID()
isCreate = true isCreate = true
} else if (isBuiltin(_id)) { } else if (roles.isBuiltin(_id)) {
ctx.throw(400, "Cannot update builtin roles.") ctx.throw(400, "Cannot update builtin roles.")
} }
const role = new Role(_id, name) const role = new roles.Role(_id, name, permissionId).addInheritance(inherits)
.addPermission(permissionId)
.addInheritance(inherits)
if (ctx.request.body._rev) { if (ctx.request.body._rev) {
role._rev = ctx.request.body._rev role._rev = ctx.request.body._rev
} }
@ -76,17 +72,17 @@ exports.save = async function (ctx) {
ctx.message = `Role '${role.name}' created successfully.` ctx.message = `Role '${role.name}' created successfully.`
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const roleId = ctx.params.roleId const roleId = ctx.params.roleId
const role = await db.get(roleId) const role = await db.get(roleId)
if (isBuiltin(roleId)) { if (roles.isBuiltin(roleId)) {
ctx.throw(400, "Cannot delete builtin role.") ctx.throw(400, "Cannot delete builtin role.")
} }
// first check no users actively attached to role // first check no users actively attached to role
const users = ( const users = (
await db.allDocs( await db.allDocs(
getUserMetadataParams(null, { getUserMetadataParams(undefined, {
include_docs: true, include_docs: true,
}) })
) )

View file

@ -1,40 +1,41 @@
const { getRoutingInfo } = require("../../utilities/routing") import { getRoutingInfo } from "../../utilities/routing"
const { import { roles } from "@budibase/backend-core"
getUserRoleHierarchy, import { BBContext } from "@budibase/types"
BUILTIN_ROLE_IDS,
} = require("@budibase/backend-core/roles")
const URL_SEPARATOR = "/" const URL_SEPARATOR = "/"
function Routing() { class Routing {
this.json = {} json: any
} constructor() {
this.json = {}
Routing.prototype.getTopLevel = function (fullpath) {
if (fullpath.charAt(0) !== URL_SEPARATOR) {
fullpath = URL_SEPARATOR + fullpath
} }
// replace the first value with the home route
return URL_SEPARATOR + fullpath.split(URL_SEPARATOR)[1]
}
Routing.prototype.getScreensProp = function (fullpath) { getTopLevel(fullpath: string) {
const topLevel = this.getTopLevel(fullpath) if (fullpath.charAt(0) !== URL_SEPARATOR) {
if (!this.json[topLevel]) { fullpath = URL_SEPARATOR + fullpath
this.json[topLevel] = {
subpaths: {},
} }
// replace the first value with the home route
return URL_SEPARATOR + fullpath.split(URL_SEPARATOR)[1]
} }
if (!this.json[topLevel].subpaths[fullpath]) {
this.json[topLevel].subpaths[fullpath] = {
screens: {},
}
}
return this.json[topLevel].subpaths[fullpath].screens
}
Routing.prototype.addScreenId = function (fullpath, roleId, screenId) { getScreensProp(fullpath: string) {
this.getScreensProp(fullpath)[roleId] = screenId const topLevel = this.getTopLevel(fullpath)
if (!this.json[topLevel]) {
this.json[topLevel] = {
subpaths: {},
}
}
if (!this.json[topLevel].subpaths[fullpath]) {
this.json[topLevel].subpaths[fullpath] = {
screens: {},
}
}
return this.json[topLevel].subpaths[fullpath].screens
}
addScreenId(fullpath: string, roleId: string, screenId: string) {
this.getScreensProp(fullpath)[roleId] = screenId
}
} }
/** /**
@ -55,26 +56,28 @@ async function getRoutingStructure() {
return { routes: routing.json } return { routes: routing.json }
} }
exports.fetch = async ctx => { export async function fetch(ctx: BBContext) {
ctx.body = await getRoutingStructure() ctx.body = await getRoutingStructure()
} }
exports.clientFetch = async ctx => { export async function clientFetch(ctx: BBContext) {
const routing = await getRoutingStructure() const routing = await getRoutingStructure()
let roleId = ctx.user.role._id let roleId = ctx.user?.role?._id
const roleIds = await getUserRoleHierarchy(roleId) const roleIds = (await roles.getUserRoleHierarchy(roleId, {
for (let topLevel of Object.values(routing.routes)) { idOnly: true,
})) as string[]
for (let topLevel of Object.values(routing.routes) as any) {
for (let subpathKey of Object.keys(topLevel.subpaths)) { for (let subpathKey of Object.keys(topLevel.subpaths)) {
let found = false let found = false
const subpath = topLevel.subpaths[subpathKey] const subpath = topLevel.subpaths[subpathKey]
const roleOptions = Object.keys(subpath.screens) const roleOptions = Object.keys(subpath.screens)
if (roleOptions.length === 1 && !roleOptions[0]) { if (roleOptions.length === 1 && !roleOptions[0]) {
subpath.screenId = subpath.screens[roleOptions[0]] subpath.screenId = subpath.screens[roleOptions[0]]
subpath.roleId = BUILTIN_ROLE_IDS.BASIC subpath.roleId = roles.BUILTIN_ROLE_IDS.BASIC
found = true found = true
} else { } else {
for (let roleId of roleIds) { for (let roleId of roleIds) {
if (roleOptions.indexOf(roleId) !== -1) { if (roleId && roleOptions.indexOf(roleId) !== -1) {
subpath.screenId = subpath.screens[roleId] subpath.screenId = subpath.screens[roleId]
subpath.roleId = roleId subpath.roleId = roleId
found = true found = true

View file

@ -27,12 +27,8 @@ import { breakExternalTableId, isSQL } from "../../../integrations/utils"
import { processObjectSync } from "@budibase/string-templates" import { processObjectSync } from "@budibase/string-templates"
// @ts-ignore // @ts-ignore
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { import { processFormulas, processDates } from "../../../utilities/rowProcessor"
processFormulas, import { context } from "@budibase/backend-core"
processDates,
} from "../../../utilities/rowProcessor/utils"
// @ts-ignore
import { getAppDB } from "@budibase/backend-core/context"
interface ManyRelationship { interface ManyRelationship {
tableId?: string tableId?: string
@ -444,7 +440,7 @@ module External {
// Process some additional data types // Process some additional data types
let finalRowArray = Object.values(finalRows) let finalRowArray = Object.values(finalRows)
finalRowArray = processDates(table, finalRowArray) finalRowArray = processDates(table, finalRowArray)
finalRowArray = processFormulas(table, finalRowArray) finalRowArray = processFormulas(table, finalRowArray) as Row[]
return finalRowArray.map((row: Row) => return finalRowArray.map((row: Row) =>
this.squashRelationshipColumns(table, row, relationships) this.squashRelationshipColumns(table, row, relationships)
@ -673,7 +669,7 @@ module External {
throw "Unable to run without a table name" throw "Unable to run without a table name"
} }
if (!this.datasource) { if (!this.datasource) {
const db = getAppDB() const db = context.getAppDB()
this.datasource = await db.get(datasourceId) this.datasource = await db.get(datasourceId)
if (!this.datasource || !this.datasource.entities) { if (!this.datasource || !this.datasource.entities) {
throw "No tables found, fetch tables before query." throw "No tables found, fetch tables before query."

View file

@ -9,7 +9,7 @@ const {
breakRowIdField, breakRowIdField,
} = require("../../../integrations/utils") } = require("../../../integrations/utils")
const ExternalRequest = require("./ExternalRequest") const ExternalRequest = require("./ExternalRequest")
const { getAppDB } = require("@budibase/backend-core/context") const { context } = require("@budibase/backend-core")
const exporters = require("../view/exporters") const exporters = require("../view/exporters")
const { apiFileReturn } = require("../../../utilities/fileSystem") const { apiFileReturn } = require("../../../utilities/fileSystem")
@ -166,7 +166,7 @@ exports.validate = async () => {
exports.exportRows = async ctx => { exports.exportRows = async ctx => {
const { datasourceId } = breakExternalTableId(ctx.params.tableId) const { datasourceId } = breakExternalTableId(ctx.params.tableId)
const db = getAppDB() const db = context.getAppDB()
const format = ctx.query.format const format = ctx.query.format
const { columns } = ctx.request.body const { columns } = ctx.request.body
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
@ -209,7 +209,7 @@ exports.fetchEnrichedRow = async ctx => {
const id = ctx.params.rowId const id = ctx.params.rowId
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
const { datasourceId, tableName } = breakExternalTableId(tableId) const { datasourceId, tableName } = breakExternalTableId(tableId)
const db = getAppDB() const db = context.getAppDB()
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
if (!datasource || !datasource.entities) { if (!datasource || !datasource.entities) {
ctx.throw(400, "Datasource has not been configured for plus API.") ctx.throw(400, "Datasource has not been configured for plus API.")

View file

@ -6,7 +6,6 @@ const {
DocumentType, DocumentType,
InternalTables, InternalTables,
} = require("../../../db/utils") } = require("../../../db/utils")
const { getDB } = require("@budibase/backend-core/db")
const userController = require("../user") const userController = require("../user")
const { const {
inputProcessing, inputProcessing,
@ -26,7 +25,7 @@ const {
getFromMemoryDoc, getFromMemoryDoc,
} = require("../view/utils") } = require("../view/utils")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
const { getAppDB } = require("@budibase/backend-core/context") const { context, db: dbCore } = require("@budibase/backend-core")
const { finaliseRow, updateRelatedFormula } = require("./staticFormula") const { finaliseRow, updateRelatedFormula } = require("./staticFormula")
const exporters = require("../view/exporters") const exporters = require("../view/exporters")
const { apiFileReturn } = require("../../../utilities/fileSystem") const { apiFileReturn } = require("../../../utilities/fileSystem")
@ -80,7 +79,7 @@ async function getRawTableData(ctx, db, tableId) {
} }
exports.patch = async ctx => { exports.patch = async ctx => {
const db = getAppDB() const db = context.getAppDB()
const inputs = ctx.request.body const inputs = ctx.request.body
const tableId = inputs.tableId const tableId = inputs.tableId
const isUserTable = tableId === InternalTables.USER_METADATA const isUserTable = tableId === InternalTables.USER_METADATA
@ -145,7 +144,7 @@ exports.patch = async ctx => {
} }
exports.save = async function (ctx) { exports.save = async function (ctx) {
const db = getAppDB() const db = context.getAppDB()
let inputs = ctx.request.body let inputs = ctx.request.body
inputs.tableId = ctx.params.tableId inputs.tableId = ctx.params.tableId
@ -188,7 +187,7 @@ exports.fetchView = async ctx => {
return exports.fetch(ctx) return exports.fetch(ctx)
} }
const db = getAppDB() const db = context.getAppDB()
const { calculation, group, field } = ctx.query const { calculation, group, field } = ctx.query
const viewInfo = await getView(db, viewName) const viewInfo = await getView(db, viewName)
let response let response
@ -242,7 +241,7 @@ exports.fetchView = async ctx => {
} }
exports.fetch = async ctx => { exports.fetch = async ctx => {
const db = getAppDB() const db = context.getAppDB()
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
let table = await db.get(tableId) let table = await db.get(tableId)
@ -251,7 +250,7 @@ exports.fetch = async ctx => {
} }
exports.find = async ctx => { exports.find = async ctx => {
const db = getDB(ctx.appId) const db = dbCore.getDB(ctx.appId)
const table = await db.get(ctx.params.tableId) const table = await db.get(ctx.params.tableId)
let row = await findRow(ctx, ctx.params.tableId, ctx.params.rowId) let row = await findRow(ctx, ctx.params.tableId, ctx.params.rowId)
row = await outputProcessing(table, row) row = await outputProcessing(table, row)
@ -259,7 +258,7 @@ exports.find = async ctx => {
} }
exports.destroy = async function (ctx) { exports.destroy = async function (ctx) {
const db = getAppDB() const db = context.getAppDB()
const { _id } = ctx.request.body const { _id } = ctx.request.body
let row = await db.get(_id) let row = await db.get(_id)
let _rev = ctx.request.body._rev || row._rev let _rev = ctx.request.body._rev || row._rev
@ -295,7 +294,7 @@ exports.destroy = async function (ctx) {
} }
exports.bulkDestroy = async ctx => { exports.bulkDestroy = async ctx => {
const db = getAppDB() const db = context.getAppDB()
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
const table = await db.get(tableId) const table = await db.get(tableId)
let { rows } = ctx.request.body let { rows } = ctx.request.body
@ -338,7 +337,7 @@ exports.search = async ctx => {
} }
const { tableId } = ctx.params const { tableId } = ctx.params
const db = getAppDB() const db = context.getAppDB()
const { paginate, query, ...params } = ctx.request.body const { paginate, query, ...params } = ctx.request.body
params.version = ctx.version params.version = ctx.version
params.tableId = tableId params.tableId = tableId
@ -371,7 +370,7 @@ exports.validate = async ctx => {
} }
exports.exportRows = async ctx => { exports.exportRows = async ctx => {
const db = getAppDB() const db = context.getAppDB()
const table = await db.get(ctx.params.tableId) const table = await db.get(ctx.params.tableId)
const rowIds = ctx.request.body.rows const rowIds = ctx.request.body.rows
let format = ctx.query.format let format = ctx.query.format
@ -408,7 +407,7 @@ exports.exportRows = async ctx => {
} }
exports.fetchEnrichedRow = async ctx => { exports.fetchEnrichedRow = async ctx => {
const db = getAppDB() const db = context.getAppDB()
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
const rowId = ctx.params.rowId const rowId = ctx.params.rowId
// need table to work out where links go in row // need table to work out where links go in row

View file

@ -1,15 +1,35 @@
const { SearchIndexes } = require("../../../db/utils") import { SearchIndexes } from "../../../db/utils"
const { removeKeyNumbering } = require("./utils") import { removeKeyNumbering } from "./utils"
const fetch = require("node-fetch") import fetch from "node-fetch"
const { getCouchInfo } = require("@budibase/backend-core/db") import { db as dbCore, context } from "@budibase/backend-core"
const { getAppId } = require("@budibase/backend-core/context") import { SearchFilters, Row } from "@budibase/types"
type SearchParams = {
tableId: string
sort?: string
sortOrder?: string
sortType?: string
limit?: number
bookmark?: string
version?: string
rows?: Row[]
}
/** /**
* Class to build lucene query URLs. * Class to build lucene query URLs.
* Optionally takes a base lucene query object. * Optionally takes a base lucene query object.
*/ */
class QueryBuilder { export class QueryBuilder {
constructor(base) { query: SearchFilters
limit: number
sort?: string
bookmark?: string
sortOrder: string
sortType: string
includeDocs: boolean
version?: string
constructor(base?: SearchFilters) {
this.query = { this.query = {
allOr: false, allOr: false,
string: {}, string: {},
@ -29,49 +49,52 @@ class QueryBuilder {
this.sortOrder = "ascending" this.sortOrder = "ascending"
this.sortType = "string" this.sortType = "string"
this.includeDocs = true this.includeDocs = true
this.version = null
} }
setVersion(version) { setVersion(version?: string) {
this.version = version if (version != null) {
this.version = version
}
return this return this
} }
setTable(tableId) { setTable(tableId: string) {
this.query.equal.tableId = tableId this.query.equal!.tableId = tableId
return this return this
} }
setLimit(limit) { setLimit(limit?: number) {
if (limit != null) { if (limit != null) {
this.limit = limit this.limit = limit
} }
return this return this
} }
setSort(sort) { setSort(sort?: string) {
if (sort != null) { if (sort != null) {
this.sort = sort this.sort = sort
} }
return this return this
} }
setSortOrder(sortOrder) { setSortOrder(sortOrder?: string) {
if (sortOrder != null) { if (sortOrder != null) {
this.sortOrder = sortOrder this.sortOrder = sortOrder
} }
return this return this
} }
setSortType(sortType) { setSortType(sortType?: string) {
if (sortType != null) { if (sortType != null) {
this.sortType = sortType this.sortType = sortType
} }
return this return this
} }
setBookmark(bookmark) { setBookmark(bookmark?: string) {
this.bookmark = bookmark if (bookmark != null) {
this.bookmark = bookmark
}
return this return this
} }
@ -80,61 +103,61 @@ class QueryBuilder {
return this return this
} }
addString(key, partial) { addString(key: string, partial: string) {
this.query.string[key] = partial this.query.string![key] = partial
return this return this
} }
addFuzzy(key, fuzzy) { addFuzzy(key: string, fuzzy: string) {
this.query.fuzzy[key] = fuzzy this.query.fuzzy![key] = fuzzy
return this return this
} }
addRange(key, low, high) { addRange(key: string, low: string | number, high: string | number) {
this.query.range = { this.query.range![key] = {
low, low,
high, high,
} }
return this return this
} }
addEqual(key, value) { addEqual(key: string, value: any) {
this.query.equal[key] = value this.query.equal![key] = value
return this return this
} }
addNotEqual(key, value) { addNotEqual(key: string, value: any) {
this.query.notEqual[key] = value this.query.notEqual![key] = value
return this return this
} }
addEmpty(key, value) { addEmpty(key: string, value: any) {
this.query.empty[key] = value this.query.empty![key] = value
return this return this
} }
addNotEmpty(key, value) { addNotEmpty(key: string, value: any) {
this.query.notEmpty[key] = value this.query.notEmpty![key] = value
return this return this
} }
addOneOf(key, value) { addOneOf(key: string, value: any) {
this.query.oneOf[key] = value this.query.oneOf![key] = value
return this return this
} }
addContains(key, value) { addContains(key: string, value: any) {
this.query.contains[key] = value this.query.contains![key] = value
return this return this
} }
addNotContains(key, value) { addNotContains(key: string, value: any) {
this.query.notContains[key] = value this.query.notContains![key] = value
return this return this
} }
addContainsAny(key, value) { addContainsAny(key: string, value: any) {
this.query.containsAny[key] = value this.query.containsAny![key] = value
return this return this
} }
@ -145,7 +168,7 @@ class QueryBuilder {
* @param options The preprocess options * @param options The preprocess options
* @returns {string|*} * @returns {string|*}
*/ */
preprocess(value, { escape, lowercase, wrap, type } = {}) { preprocess(value: any, { escape, lowercase, wrap, type }: any = {}) {
const hasVersion = !!this.version const hasVersion = !!this.version
// Determine if type needs wrapped // Determine if type needs wrapped
const originalType = typeof value const originalType = typeof value
@ -173,12 +196,12 @@ class QueryBuilder {
let query = allOr ? "" : "*:*" let query = allOr ? "" : "*:*"
const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true } const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true }
let tableId let tableId
if (this.query.equal.tableId) { if (this.query.equal!.tableId) {
tableId = this.query.equal.tableId tableId = this.query.equal!.tableId
delete this.query.equal.tableId delete this.query.equal!.tableId
} }
const equal = (key, value) => { const equal = (key: string, value: any) => {
// 0 evaluates to false, which means we would return all rows if we don't check it // 0 evaluates to false, which means we would return all rows if we don't check it
if (!value && value !== 0) { if (!value && value !== 0) {
return null return null
@ -186,7 +209,7 @@ class QueryBuilder {
return `${key}:${builder.preprocess(value, allPreProcessingOpts)}` return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
} }
const contains = (key, value, mode = "AND") => { const contains = (key: string, value: any, mode = "AND") => {
if (Array.isArray(value) && value.length === 0) { if (Array.isArray(value) && value.length === 0) {
return null return null
} }
@ -202,16 +225,17 @@ class QueryBuilder {
return `${key}:(${statement})` return `${key}:(${statement})`
} }
const notContains = (key, value) => { const notContains = (key: string, value: any) => {
// @ts-ignore
const allPrefix = allOr === "" ? "*:* AND" : "" const allPrefix = allOr === "" ? "*:* AND" : ""
return allPrefix + "NOT " + contains(key, value) return allPrefix + "NOT " + contains(key, value)
} }
const containsAny = (key, value) => { const containsAny = (key: string, value: any) => {
return contains(key, value, "OR") return contains(key, value, "OR")
} }
const oneOf = (key, value) => { const oneOf = (key: string, value: any) => {
if (!Array.isArray(value)) { if (!Array.isArray(value)) {
if (typeof value === "string") { if (typeof value === "string") {
value = value.split(",") value = value.split(",")
@ -229,7 +253,7 @@ class QueryBuilder {
return `${key}:(${orStatement})` return `${key}:(${orStatement})`
} }
function build(structure, queryFn) { function build(structure: any, queryFn: any) {
for (let [key, value] of Object.entries(structure)) { for (let [key, value] of Object.entries(structure)) {
// check for new format - remove numbering if needed // check for new format - remove numbering if needed
key = removeKeyNumbering(key) key = removeKeyNumbering(key)
@ -249,7 +273,7 @@ class QueryBuilder {
// Construct the actual lucene search query string from JSON structure // Construct the actual lucene search query string from JSON structure
if (this.query.string) { if (this.query.string) {
build(this.query.string, (key, value) => { build(this.query.string, (key: string, value: any) => {
if (!value) { if (!value) {
return null return null
} }
@ -262,7 +286,7 @@ class QueryBuilder {
}) })
} }
if (this.query.range) { if (this.query.range) {
build(this.query.range, (key, value) => { build(this.query.range, (key: string, value: any) => {
if (!value) { if (!value) {
return null return null
} }
@ -278,7 +302,7 @@ class QueryBuilder {
}) })
} }
if (this.query.fuzzy) { if (this.query.fuzzy) {
build(this.query.fuzzy, (key, value) => { build(this.query.fuzzy, (key: string, value: any) => {
if (!value) { if (!value) {
return null return null
} }
@ -294,7 +318,7 @@ class QueryBuilder {
build(this.query.equal, equal) build(this.query.equal, equal)
} }
if (this.query.notEqual) { if (this.query.notEqual) {
build(this.query.notEqual, (key, value) => { build(this.query.notEqual, (key: string, value: any) => {
if (!value) { if (!value) {
return null return null
} }
@ -302,10 +326,10 @@ class QueryBuilder {
}) })
} }
if (this.query.empty) { if (this.query.empty) {
build(this.query.empty, key => `!${key}:["" TO *]`) build(this.query.empty, (key: string) => `!${key}:["" TO *]`)
} }
if (this.query.notEmpty) { if (this.query.notEmpty) {
build(this.query.notEmpty, key => `${key}:["" TO *]`) build(this.query.notEmpty, (key: string) => `${key}:["" TO *]`)
} }
if (this.query.oneOf) { if (this.query.oneOf) {
build(this.query.oneOf, oneOf) build(this.query.oneOf, oneOf)
@ -329,7 +353,7 @@ class QueryBuilder {
} }
buildSearchBody() { buildSearchBody() {
let body = { let body: any = {
q: this.buildSearchQuery(), q: this.buildSearchQuery(),
limit: Math.min(this.limit, 200), limit: Math.min(this.limit, 200),
include_docs: this.includeDocs, include_docs: this.includeDocs,
@ -346,17 +370,14 @@ class QueryBuilder {
} }
async run() { async run() {
const appId = getAppId() const appId = context.getAppId()
const { url, cookie } = getCouchInfo() const { url, cookie } = dbCore.getCouchInfo()
const fullPath = `${url}/${appId}/_design/database/_search/${SearchIndexes.ROWS}` const fullPath = `${url}/${appId}/_design/database/_search/${SearchIndexes.ROWS}`
const body = this.buildSearchBody() const body = this.buildSearchBody()
return await runQuery(fullPath, body, cookie) return await runQuery(fullPath, body, cookie)
} }
} }
// exported for unit testing
exports.QueryBuilder = QueryBuilder
/** /**
* Executes a lucene search query. * Executes a lucene search query.
* @param url The query URL * @param url The query URL
@ -364,7 +385,7 @@ exports.QueryBuilder = QueryBuilder
* @param cookie The auth cookie for CouchDB * @param cookie The auth cookie for CouchDB
* @returns {Promise<{rows: []}>} * @returns {Promise<{rows: []}>}
*/ */
const runQuery = async (url, body, cookie) => { const runQuery = async (url: string, body: any, cookie: string) => {
const response = await fetch(url, { const response = await fetch(url, {
body: JSON.stringify(body), body: JSON.stringify(body),
method: "POST", method: "POST",
@ -374,11 +395,11 @@ const runQuery = async (url, body, cookie) => {
}) })
const json = await response.json() const json = await response.json()
let output = { let output: any = {
rows: [], rows: [],
} }
if (json.rows != null && json.rows.length > 0) { if (json.rows != null && json.rows.length > 0) {
output.rows = json.rows.map(row => row.doc) output.rows = json.rows.map((row: any) => row.doc)
} }
if (json.bookmark) { if (json.bookmark) {
output.bookmark = json.bookmark output.bookmark = json.bookmark
@ -402,7 +423,7 @@ const runQuery = async (url, body, cookie) => {
* rows {array|null} Current results in the recursive search * rows {array|null} Current results in the recursive search
* @returns {Promise<*[]|*>} * @returns {Promise<*[]|*>}
*/ */
const recursiveSearch = async (query, params) => { async function recursiveSearch(query: any, params: any): Promise<any> {
const bookmark = params.bookmark const bookmark = params.bookmark
const rows = params.rows || [] const rows = params.rows || []
if (rows.length >= params.limit) { if (rows.length >= params.limit) {
@ -450,7 +471,10 @@ const recursiveSearch = async (query, params) => {
* bookmark {string} The bookmark to resume from * bookmark {string} The bookmark to resume from
* @returns {Promise<{hasNextPage: boolean, rows: *[]}>} * @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
*/ */
exports.paginatedSearch = async (query, params) => { export async function paginatedSearch(
query: SearchFilters,
params: SearchParams
) {
let limit = params.limit let limit = params.limit
if (limit == null || isNaN(limit) || limit < 0) { if (limit == null || isNaN(limit) || limit < 0) {
limit = 50 limit = 50
@ -496,7 +520,7 @@ exports.paginatedSearch = async (query, params) => {
* limit {number} The desired number of results * limit {number} The desired number of results
* @returns {Promise<{rows: *}>} * @returns {Promise<{rows: *}>}
*/ */
exports.fullSearch = async (query, params) => { export async function fullSearch(query: SearchFilters, params: SearchParams) {
let limit = params.limit let limit = params.limit
if (limit == null || isNaN(limit) || limit < 0) { if (limit == null || isNaN(limit) || limit < 0) {
limit = 1000 limit = 1000

View file

@ -1,13 +1,14 @@
const { getRowParams } = require("../../../db/utils") import { getRowParams } from "../../../db/utils"
const { import {
outputProcessing, outputProcessing,
processAutoColumn, processAutoColumn,
processFormulas, processFormulas,
} = require("../../../utilities/rowProcessor") } from "../../../utilities/rowProcessor"
const { FieldTypes, FormulaTypes } = require("../../../constants") import { FieldTypes, FormulaTypes } from "../../../constants"
import { context } from "@budibase/backend-core"
import { Table, Row } from "@budibase/types"
const { isEqual } = require("lodash") const { isEqual } = require("lodash")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
const { getAppDB } = require("@budibase/backend-core/context")
/** /**
* This function runs through a list of enriched rows, looks at the rows which * This function runs through a list of enriched rows, looks at the rows which
@ -15,22 +16,22 @@ const { getAppDB } = require("@budibase/backend-core/context")
* updated. * updated.
* NOTE: this will only for affect static formulas. * NOTE: this will only for affect static formulas.
*/ */
exports.updateRelatedFormula = async (table, enrichedRows) => { exports.updateRelatedFormula = async (table: Table, enrichedRows: Row[]) => {
const db = getAppDB() const db = context.getAppDB()
// no formula to update, we're done // no formula to update, we're done
if (!table.relatedFormula) { if (!table.relatedFormula) {
return return
} }
let promises = [] let promises: Promise<any>[] = []
for (let enrichedRow of Array.isArray(enrichedRows) for (let enrichedRow of Array.isArray(enrichedRows)
? enrichedRows ? enrichedRows
: [enrichedRows]) { : [enrichedRows]) {
// the related rows by tableId // the related rows by tableId
let relatedRows = {} let relatedRows: Record<string, Row[]> = {}
for (let [key, field] of Object.entries(enrichedRow)) { for (let [key, field] of Object.entries(enrichedRow)) {
const columnDefinition = table.schema[key] const columnDefinition = table.schema[key]
if (columnDefinition && columnDefinition.type === FieldTypes.LINK) { if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
const relatedTableId = columnDefinition.tableId const relatedTableId = columnDefinition.tableId!
if (!relatedRows[relatedTableId]) { if (!relatedRows[relatedTableId]) {
relatedRows[relatedTableId] = [] relatedRows[relatedTableId] = []
} }
@ -38,7 +39,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
} }
} }
for (let tableId of table.relatedFormula) { for (let tableId of table.relatedFormula) {
let relatedTable let relatedTable: Table
try { try {
// no rows to update, skip // no rows to update, skip
if (!relatedRows[tableId] || relatedRows[tableId].length === 0) { if (!relatedRows[tableId] || relatedRows[tableId].length === 0) {
@ -48,7 +49,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
} catch (err) { } catch (err) {
// no error scenario, table doesn't seem to exist anymore, ignore // no error scenario, table doesn't seem to exist anymore, ignore
} }
for (let column of Object.values(relatedTable.schema)) { for (let column of Object.values(relatedTable!.schema)) {
// needs updated in related rows // needs updated in related rows
if ( if (
column.type === FieldTypes.FORMULA && column.type === FieldTypes.FORMULA &&
@ -57,7 +58,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
// re-enrich rows for all the related, don't update the related formula for them // re-enrich rows for all the related, don't update the related formula for them
promises = promises.concat( promises = promises.concat(
relatedRows[tableId].map(related => relatedRows[tableId].map(related =>
exports.finaliseRow(relatedTable, related, { finaliseRow(relatedTable, related, {
updateFormula: false, updateFormula: false,
}) })
) )
@ -70,8 +71,8 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
await Promise.all(promises) await Promise.all(promises)
} }
exports.updateAllFormulasInTable = async table => { export async function updateAllFormulasInTable(table: Table) {
const db = getAppDB() const db = context.getAppDB()
// start by getting the raw rows (which will be written back to DB after update) // start by getting the raw rows (which will be written back to DB after update)
let rows = ( let rows = (
await db.allDocs( await db.allDocs(
@ -88,7 +89,9 @@ exports.updateAllFormulasInTable = async table => {
const updatedRows = [] const updatedRows = []
for (let row of rows) { for (let row of rows) {
// find the enriched row, if found process the formulas // find the enriched row, if found process the formulas
const enrichedRow = enrichedRows.find(enriched => enriched._id === row._id) const enrichedRow = enrichedRows.find(
(enriched: any) => enriched._id === row._id
)
if (enrichedRow) { if (enrichedRow) {
const processed = processFormulas(table, cloneDeep(row), { const processed = processFormulas(table, cloneDeep(row), {
dynamic: false, dynamic: false,
@ -109,12 +112,14 @@ exports.updateAllFormulasInTable = async table => {
* row. The reason we need to return the enriched row is that the automation row created trigger * row. The reason we need to return the enriched row is that the automation row created trigger
* expects the row to be totally enriched/contain all relationships. * expects the row to be totally enriched/contain all relationships.
*/ */
exports.finaliseRow = async ( export async function finaliseRow(
table, table: Table,
row, row: Row,
{ oldTable, updateFormula } = { updateFormula: true } { oldTable, updateFormula }: { oldTable?: Table; updateFormula: boolean } = {
) => { updateFormula: true,
const db = getAppDB() }
) {
const db = context.getAppDB()
row.type = "row" row.type = "row"
// process the row before return, to include relationships // process the row before return, to include relationships
let enrichedRow = await outputProcessing(table, cloneDeep(row), { let enrichedRow = await outputProcessing(table, cloneDeep(row), {
@ -131,7 +136,7 @@ exports.finaliseRow = async (
if (oldTable && !isEqual(oldTable, table)) { if (oldTable && !isEqual(oldTable, table)) {
try { try {
await db.put(table) await db.put(table)
} catch (err) { } catch (err: any) {
if (err.status === 409) { if (err.status === 409) {
const updatedTable = await db.get(table._id) const updatedTable = await db.get(table._id)
let response = processAutoColumn(null, updatedTable, row, { let response = processAutoColumn(null, updatedTable, row, {

View file

@ -1,33 +1,32 @@
import { InternalTables } from "../../../db/utils"
import * as userController from "../user"
import { FieldTypes } from "../../../constants"
import { context } from "@budibase/backend-core"
import { makeExternalQuery } from "../../../integrations/base/query"
import { BBContext, Row, Table } from "@budibase/types"
export { removeKeyNumbering } from "../../../integrations/base/utils"
const validateJs = require("validate.js") const validateJs = require("validate.js")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
const { InternalTables } = require("../../../db/utils")
const userController = require("../user")
const { FieldTypes } = require("../../../constants")
const { getAppDB } = require("@budibase/backend-core/context")
const { makeExternalQuery } = require("../../../integrations/base/query")
const { removeKeyNumbering } = require("../../../integrations/base/utils")
validateJs.extend(validateJs.validators.datetime, { validateJs.extend(validateJs.validators.datetime, {
parse: function (value) { parse: function (value: string) {
return new Date(value).getTime() return new Date(value).getTime()
}, },
// Input is a unix timestamp // Input is a unix timestamp
format: function (value) { format: function (value: string) {
return new Date(value).toISOString() return new Date(value).toISOString()
}, },
}) })
exports.removeKeyNumbering = removeKeyNumbering export async function getDatasourceAndQuery(json: any) {
exports.getDatasourceAndQuery = async json => {
const datasourceId = json.endpoint.datasourceId const datasourceId = json.endpoint.datasourceId
const db = getAppDB() const db = context.getAppDB()
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
return makeExternalQuery(datasource, json) return makeExternalQuery(datasource, json)
} }
exports.findRow = async (ctx, tableId, rowId) => { export async function findRow(ctx: BBContext, tableId: string, rowId: string) {
const db = getAppDB() const db = context.getAppDB()
let row let row
// TODO remove special user case in future // TODO remove special user case in future
if (tableId === InternalTables.USER_METADATA) { if (tableId === InternalTables.USER_METADATA) {
@ -45,12 +44,20 @@ exports.findRow = async (ctx, tableId, rowId) => {
return row return row
} }
exports.validate = async ({ tableId, row, table }) => { export async function validate({
tableId,
row,
table,
}: {
tableId?: string
row: Row
table: Table
}) {
if (!table) { if (!table) {
const db = getAppDB() const db = context.getAppDB()
table = await db.get(tableId) table = await db.get(tableId)
} }
const errors = {} const errors: any = {}
for (let fieldName of Object.keys(table.schema)) { for (let fieldName of Object.keys(table.schema)) {
const constraints = cloneDeep(table.schema[fieldName].constraints) const constraints = cloneDeep(table.schema[fieldName].constraints)
const type = table.schema[fieldName].type const type = table.schema[fieldName].type
@ -70,7 +77,7 @@ exports.validate = async ({ tableId, row, table }) => {
if (!Array.isArray(row[fieldName])) { if (!Array.isArray(row[fieldName])) {
row[fieldName] = row[fieldName].split(",") row[fieldName] = row[fieldName].split(",")
} }
row[fieldName].map(val => { row[fieldName].map((val: any) => {
if ( if (
!constraints.inclusion.includes(val) && !constraints.inclusion.includes(val) &&
constraints.inclusion.length !== 0 constraints.inclusion.length !== 0

View file

@ -7,9 +7,9 @@ import {
roles, roles,
} from "@budibase/backend-core" } from "@budibase/backend-core"
import { updateAppPackage } from "./application" import { updateAppPackage } from "./application"
import { Plugin, ScreenProps } from "@budibase/types" import { Plugin, ScreenProps, BBContext } from "@budibase/types"
exports.fetch = async (ctx: any) => { export async function fetch(ctx: BBContext) {
const db = context.getAppDB() const db = context.getAppDB()
const screens = ( const screens = (
@ -20,13 +20,17 @@ exports.fetch = async (ctx: any) => {
) )
).rows.map((el: any) => el.doc) ).rows.map((el: any) => el.doc)
const roleId = ctx.user?.role?._id as string
if (!roleId) {
ctx.throw("Unable to retrieve users role ID.")
}
ctx.body = await new roles.AccessController().checkScreensAccess( ctx.body = await new roles.AccessController().checkScreensAccess(
screens, screens,
ctx.user.role._id roleId
) )
} }
exports.save = async (ctx: any) => { export async function save(ctx: BBContext) {
const db = context.getAppDB() const db = context.getAppDB()
let screen = ctx.request.body let screen = ctx.request.body
@ -92,7 +96,7 @@ exports.save = async (ctx: any) => {
} }
} }
exports.destroy = async (ctx: any) => { export async function destroy(ctx: BBContext) {
const db = context.getAppDB() const db = context.getAppDB()
const id = ctx.params.screenId const id = ctx.params.screenId
const screen = await db.get(id) const screen = await db.get(id)
@ -106,7 +110,7 @@ exports.destroy = async (ctx: any) => {
ctx.status = 200 ctx.status = 200
} }
const findPlugins = (component: ScreenProps, foundPlugins: string[]) => { function findPlugins(component: ScreenProps, foundPlugins: string[]) {
if (!component) { if (!component) {
return return
} }

View file

@ -1,11 +0,0 @@
const ScriptRunner = require("../../utilities/scriptRunner")
exports.execute = async function (ctx) {
const { script, context } = ctx.request.body
const runner = new ScriptRunner(script, context)
ctx.body = runner.execute()
}
exports.save = async function (ctx) {
ctx.throw(501, "Not currently implemented")
}

View file

@ -0,0 +1,12 @@
import ScriptRunner from "../../utilities/scriptRunner"
import { BBContext } from "@budibase/types"
export async function execute(ctx: BBContext) {
const { script, context } = ctx.request.body
const runner = new ScriptRunner(script, context)
ctx.body = runner.execute()
}
export async function save(ctx: BBContext) {
ctx.throw(501, "Not currently implemented")
}

View file

@ -17,13 +17,9 @@ const { clientLibraryPath } = require("../../../utilities")
const { upload, deleteFiles } = require("../../../utilities/fileSystem") const { upload, deleteFiles } = require("../../../utilities/fileSystem")
const { attachmentsRelativeURL } = require("../../../utilities") const { attachmentsRelativeURL } = require("../../../utilities")
const { DocumentType } = require("../../../db/utils") const { DocumentType } = require("../../../db/utils")
const { getAppDB, getAppId } = require("@budibase/backend-core/context") const { context, objectStore, utils } = require("@budibase/backend-core")
const { setCookie, clearCookie } = require("@budibase/backend-core/utils")
const AWS = require("aws-sdk") const AWS = require("aws-sdk")
const fs = require("fs") const fs = require("fs")
const {
downloadTarballDirect,
} = require("../../../utilities/fileSystem/utilities")
async function prepareUpload({ s3Key, bucket, metadata, file }: any) { async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
const response = await upload({ const response = await upload({
@ -48,7 +44,7 @@ export const toggleBetaUiFeature = async function (ctx: any) {
const cookieName = `beta:${ctx.params.feature}` const cookieName = `beta:${ctx.params.feature}`
if (ctx.cookies.get(cookieName)) { if (ctx.cookies.get(cookieName)) {
clearCookie(ctx, cookieName) utils.clearCookie(ctx, cookieName)
ctx.body = { ctx.body = {
message: `${ctx.params.feature} disabled`, message: `${ctx.params.feature} disabled`,
} }
@ -61,11 +57,11 @@ export const toggleBetaUiFeature = async function (ctx: any) {
if (!fs.existsSync(builderPath)) { if (!fs.existsSync(builderPath)) {
fs.mkdirSync(builderPath) fs.mkdirSync(builderPath)
} }
await downloadTarballDirect( await objectStore.downloadTarballDirect(
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz", "https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
builderPath builderPath
) )
setCookie(ctx, {}, cookieName) utils.setCookie(ctx, {}, cookieName)
ctx.body = { ctx.body = {
message: `${ctx.params.feature} enabled`, message: `${ctx.params.feature} enabled`,
@ -103,9 +99,9 @@ export const deleteObjects = async function (ctx: any) {
} }
export const serveApp = async function (ctx: any) { export const serveApp = async function (ctx: any) {
const db = getAppDB({ skip_setup: true }) const db = context.getAppDB({ skip_setup: true })
const appInfo = await db.get(DocumentType.APP_METADATA) const appInfo = await db.get(DocumentType.APP_METADATA)
let appId = getAppId() let appId = context.getAppId()
if (!env.isJest()) { if (!env.isJest()) {
const App = require("./templates/BudibaseApp.svelte").default const App = require("./templates/BudibaseApp.svelte").default
@ -134,11 +130,11 @@ export const serveApp = async function (ctx: any) {
} }
export const serveBuilderPreview = async function (ctx: any) { export const serveBuilderPreview = async function (ctx: any) {
const db = getAppDB({ skip_setup: true }) const db = context.getAppDB({ skip_setup: true })
const appInfo = await db.get(DocumentType.APP_METADATA) const appInfo = await db.get(DocumentType.APP_METADATA)
if (!env.isJest()) { if (!env.isJest()) {
let appId = getAppId() let appId = context.getAppId()
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`) const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
ctx.body = await processString(previewHbs, { ctx.body = await processString(previewHbs, {
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx), clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
@ -156,7 +152,7 @@ export const serveClientLibrary = async function (ctx: any) {
} }
export const getSignedUploadURL = async function (ctx: any) { export const getSignedUploadURL = async function (ctx: any) {
const database = getAppDB() const database = context.getAppDB()
// Ensure datasource is valid // Ensure datasource is valid
let datasource let datasource

View file

@ -1,13 +1,14 @@
const { FieldTypes, FormulaTypes } = require("../../../constants") import { FieldTypes, FormulaTypes } from "../../../constants"
const { clearColumns } = require("./utils") import { clearColumns } from "./utils"
const { doesContainStrings } = require("@budibase/string-templates") import { doesContainStrings } from "@budibase/string-templates"
const { cloneDeep } = require("lodash/fp") import { cloneDeep } from "lodash/fp"
const { isEqual, uniq } = require("lodash") import { isEqual, uniq } from "lodash"
const { updateAllFormulasInTable } = require("../row/staticFormula") import { updateAllFormulasInTable } from "../row/staticFormula"
const { getAppDB } = require("@budibase/backend-core/context") import { context } from "@budibase/backend-core"
const sdk = require("../../../sdk") import { FieldSchema, Table } from "@budibase/types"
import sdk from "../../../sdk"
function isStaticFormula(column) { function isStaticFormula(column: FieldSchema) {
return ( return (
column.type === FieldTypes.FORMULA && column.type === FieldTypes.FORMULA &&
column.formulaType === FormulaTypes.STATIC column.formulaType === FormulaTypes.STATIC
@ -18,8 +19,8 @@ function isStaticFormula(column) {
* This retrieves the formula columns from a table schema that use a specified column name * This retrieves the formula columns from a table schema that use a specified column name
* in the formula. * in the formula.
*/ */
function getFormulaThatUseColumn(table, columnNames) { function getFormulaThatUseColumn(table: Table, columnNames: string[] | string) {
let formula = [] let formula: string[] = []
columnNames = Array.isArray(columnNames) ? columnNames : [columnNames] columnNames = Array.isArray(columnNames) ? columnNames : [columnNames]
for (let column of Object.values(table.schema)) { for (let column of Object.values(table.schema)) {
// not a static formula, or doesn't contain a relationship // not a static formula, or doesn't contain a relationship
@ -38,7 +39,10 @@ function getFormulaThatUseColumn(table, columnNames) {
* This functions checks for when a related table, column or related column is deleted, if any * This functions checks for when a related table, column or related column is deleted, if any
* tables need to have the formula column removed. * tables need to have the formula column removed.
*/ */
async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) { async function checkIfFormulaNeedsCleared(
table: Table,
{ oldTable, deletion }: { oldTable?: Table; deletion?: boolean }
) {
// start by retrieving all tables, remove the current table from the list // start by retrieving all tables, remove the current table from the list
const tables = (await sdk.tables.getAllInternalTables()).filter( const tables = (await sdk.tables.getAllInternalTables()).filter(
tbl => tbl._id !== table._id tbl => tbl._id !== table._id
@ -49,11 +53,14 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
) )
// remove any formula columns that used related columns // remove any formula columns that used related columns
for (let removed of removedColumns) { for (let removed of removedColumns) {
let tableToUse = table let tableToUse: Table | undefined = table
// if relationship, get the related table // if relationship, get the related table
if (removed.type === FieldTypes.LINK) { if (removed.type === FieldTypes.LINK) {
tableToUse = tables.find(table => table._id === removed.tableId) tableToUse = tables.find(table => table._id === removed.tableId)
} }
if (!tableToUse) {
continue
}
const columnsToDelete = getFormulaThatUseColumn(tableToUse, removed.name) const columnsToDelete = getFormulaThatUseColumn(tableToUse, removed.name)
if (columnsToDelete.length > 0) { if (columnsToDelete.length > 0) {
await clearColumns(table, columnsToDelete) await clearColumns(table, columnsToDelete)
@ -71,11 +78,11 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
// look to see if the column was used in a relationship formula, // look to see if the column was used in a relationship formula,
// relationships won't be used for this // relationships won't be used for this
if (relatedTable && relatedColumns && removed.type !== FieldTypes.LINK) { if (relatedTable && relatedColumns && removed.type !== FieldTypes.LINK) {
let relatedFormulaToRemove = [] let relatedFormulaToRemove: string[] = []
for (let column of relatedColumns) { for (let column of relatedColumns) {
relatedFormulaToRemove = relatedFormulaToRemove.concat( relatedFormulaToRemove = relatedFormulaToRemove.concat(
getFormulaThatUseColumn(relatedTable, [ getFormulaThatUseColumn(relatedTable, [
column.fieldName, column.fieldName!,
removed.name, removed.name,
]) ])
) )
@ -95,13 +102,14 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
* specifically only for static formula. * specifically only for static formula.
*/ */
async function updateRelatedFormulaLinksOnTables( async function updateRelatedFormulaLinksOnTables(
table, table: Table,
{ deletion } = { deletion: false } { deletion }: { deletion?: boolean } = {}
) { ) {
const db = getAppDB() const tableId: string = table._id!
const db = context.getAppDB()
// start by retrieving all tables, remove the current table from the list // start by retrieving all tables, remove the current table from the list
const tables = (await sdk.tables.getAllInternalTables()).filter( const tables = (await sdk.tables.getAllInternalTables()).filter(
tbl => tbl._id !== table._id tbl => tbl._id !== tableId
) )
// clone the tables, so we can compare at end // clone the tables, so we can compare at end
const initialTables = cloneDeep(tables) const initialTables = cloneDeep(tables)
@ -114,7 +122,7 @@ async function updateRelatedFormulaLinksOnTables(
if (!otherTable.relatedFormula) { if (!otherTable.relatedFormula) {
continue continue
} }
const index = otherTable.relatedFormula.indexOf(table._id) const index = otherTable.relatedFormula.indexOf(tableId)
if (index !== -1) { if (index !== -1) {
otherTable.relatedFormula.splice(index, 1) otherTable.relatedFormula.splice(index, 1)
} }
@ -133,11 +141,11 @@ async function updateRelatedFormulaLinksOnTables(
if ( if (
relatedTable && relatedTable &&
(!relatedTable.relatedFormula || (!relatedTable.relatedFormula ||
!relatedTable.relatedFormula.includes(table._id)) !relatedTable.relatedFormula.includes(tableId))
) { ) {
relatedTable.relatedFormula = relatedTable.relatedFormula relatedTable.relatedFormula = relatedTable.relatedFormula
? [...relatedTable.relatedFormula, table._id] ? [...relatedTable.relatedFormula, tableId]
: [table._id] : [tableId]
} }
} }
} }
@ -150,7 +158,10 @@ async function updateRelatedFormulaLinksOnTables(
} }
} }
async function checkIfFormulaUpdated(table, { oldTable }) { async function checkIfFormulaUpdated(
table: Table,
{ oldTable }: { oldTable?: Table }
) {
// look to see if any formula values have changed // look to see if any formula values have changed
const shouldUpdate = Object.values(table.schema).find( const shouldUpdate = Object.values(table.schema).find(
column => column =>
@ -165,7 +176,10 @@ async function checkIfFormulaUpdated(table, { oldTable }) {
} }
} }
exports.runStaticFormulaChecks = async (table, { oldTable, deletion }) => { export async function runStaticFormulaChecks(
table: Table,
{ oldTable, deletion }: { oldTable?: Table; deletion?: boolean }
) {
await updateRelatedFormulaLinksOnTables(table, { deletion }) await updateRelatedFormulaLinksOnTables(table, { deletion })
await checkIfFormulaNeedsCleared(table, { oldTable, deletion }) await checkIfFormulaNeedsCleared(table, { oldTable, deletion })
if (!deletion) { if (!deletion) {

View file

@ -1,38 +1,47 @@
const { import {
buildExternalTableId, buildExternalTableId,
breakExternalTableId, breakExternalTableId,
} = require("../../../integrations/utils") } from "../../../integrations/utils"
const { import {
generateForeignKey, generateForeignKey,
generateJunctionTableName, generateJunctionTableName,
foreignKeyStructure, foreignKeyStructure,
hasTypeChanged, hasTypeChanged,
} = require("./utils") } from "./utils"
const { import {
DataSourceOperation, DataSourceOperation,
FieldTypes, FieldTypes,
RelationshipTypes, RelationshipTypes,
} = require("../../../constants") } from "../../../constants"
const { makeExternalQuery } = require("../../../integrations/base/query") import { makeExternalQuery } from "../../../integrations/base/query"
import csvParser from "../../../utilities/csvParser"
import { handleRequest } from "../row/external"
import { events, context } from "@budibase/backend-core"
import {
Datasource,
Table,
QueryJson,
Operation,
RenameColumn,
FieldSchema,
BBContext,
TableRequest,
} from "@budibase/types"
import sdk from "../../../sdk"
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
const csvParser = require("../../../utilities/csvParser")
const { handleRequest } = require("../row/external")
const { getAppDB } = require("@budibase/backend-core/context")
const { events } = require("@budibase/backend-core")
const sdk = require("../../../sdk")
async function makeTableRequest( async function makeTableRequest(
datasource, datasource: Datasource,
operation, operation: Operation,
table, table: Table,
tables, tables: Record<string, Table>,
oldTable = null, oldTable?: Table,
renamed = null renamed?: RenameColumn
) { ) {
const json = { const json: QueryJson = {
endpoint: { endpoint: {
datasourceId: datasource._id, datasourceId: datasource._id!,
entityId: table._id, entityId: table._id!,
operation, operation,
}, },
meta: { meta: {
@ -41,15 +50,19 @@ async function makeTableRequest(
table, table,
} }
if (oldTable) { if (oldTable) {
json.meta.table = oldTable json.meta!.table = oldTable
} }
if (renamed) { if (renamed) {
json.meta.renamed = renamed json.meta!.renamed = renamed
} }
return makeExternalQuery(datasource, json) return makeExternalQuery(datasource, json)
} }
function cleanupRelationships(table, tables, oldTable = null) { function cleanupRelationships(
table: Table,
tables: Record<string, Table>,
oldTable?: Table
) {
const tableToIterate = oldTable ? oldTable : table const tableToIterate = oldTable ? oldTable : table
// clean up relationships in couch table schemas // clean up relationships in couch table schemas
for (let [key, schema] of Object.entries(tableToIterate.schema)) { for (let [key, schema] of Object.entries(tableToIterate.schema)) {
@ -78,7 +91,7 @@ function cleanupRelationships(table, tables, oldTable = null) {
} }
} }
function getDatasourceId(table) { function getDatasourceId(table: Table) {
if (!table) { if (!table) {
throw "No table supplied" throw "No table supplied"
} }
@ -88,7 +101,7 @@ function getDatasourceId(table) {
return breakExternalTableId(table._id).datasourceId return breakExternalTableId(table._id).datasourceId
} }
function otherRelationshipType(type) { function otherRelationshipType(type?: string) {
if (type === RelationshipTypes.MANY_TO_MANY) { if (type === RelationshipTypes.MANY_TO_MANY) {
return RelationshipTypes.MANY_TO_MANY return RelationshipTypes.MANY_TO_MANY
} }
@ -97,13 +110,21 @@ function otherRelationshipType(type) {
: RelationshipTypes.ONE_TO_MANY : RelationshipTypes.ONE_TO_MANY
} }
function generateManyLinkSchema(datasource, column, table, relatedTable) { function generateManyLinkSchema(
datasource: Datasource,
column: FieldSchema,
table: Table,
relatedTable: Table
): Table {
if (!table.primary || !relatedTable.primary) {
throw new Error("Unable to generate many link schema, no primary keys")
}
const primary = table.name + table.primary[0] const primary = table.name + table.primary[0]
const relatedPrimary = relatedTable.name + relatedTable.primary[0] const relatedPrimary = relatedTable.name + relatedTable.primary[0]
const jcTblName = generateJunctionTableName(column, table, relatedTable) const jcTblName = generateJunctionTableName(column, table, relatedTable)
// first create the new table // first create the new table
const junctionTable = { const junctionTable = {
_id: buildExternalTableId(datasource._id, jcTblName), _id: buildExternalTableId(datasource._id!, jcTblName),
name: jcTblName, name: jcTblName,
primary: [primary, relatedPrimary], primary: [primary, relatedPrimary],
constrained: [primary, relatedPrimary], constrained: [primary, relatedPrimary],
@ -125,7 +146,15 @@ function generateManyLinkSchema(datasource, column, table, relatedTable) {
return junctionTable return junctionTable
} }
function generateLinkSchema(column, table, relatedTable, type) { function generateLinkSchema(
column: FieldSchema,
table: Table,
relatedTable: Table,
type: string
) {
if (!table.primary || !relatedTable.primary) {
throw new Error("Unable to generate link schema, no primary keys")
}
const isOneSide = type === RelationshipTypes.ONE_TO_MANY const isOneSide = type === RelationshipTypes.ONE_TO_MANY
const primary = isOneSide ? relatedTable.primary[0] : table.primary[0] const primary = isOneSide ? relatedTable.primary[0] : table.primary[0]
// generate a foreign key // generate a foreign key
@ -136,7 +165,12 @@ function generateLinkSchema(column, table, relatedTable, type) {
return foreignKey return foreignKey
} }
function generateRelatedSchema(linkColumn, table, relatedTable, columnName) { function generateRelatedSchema(
linkColumn: FieldSchema,
table: Table,
relatedTable: Table,
columnName: string
) {
// generate column for other table // generate column for other table
const relatedSchema = cloneDeep(linkColumn) const relatedSchema = cloneDeep(linkColumn)
// swap them from the main link // swap them from the main link
@ -159,21 +193,21 @@ function generateRelatedSchema(linkColumn, table, relatedTable, columnName) {
table.schema[columnName] = relatedSchema table.schema[columnName] = relatedSchema
} }
function isRelationshipSetup(column) { function isRelationshipSetup(column: FieldSchema) {
return column.foreignKey || column.through return column.foreignKey || column.through
} }
exports.save = async function (ctx) { export async function save(ctx: BBContext) {
const table = ctx.request.body const table: TableRequest = ctx.request.body
const { _rename: renamed } = table const renamed = table?._rename
// can't do this right now // can't do this right now
delete table.dataImport delete table.dataImport
const datasourceId = getDatasourceId(ctx.request.body) const datasourceId = getDatasourceId(ctx.request.body)!
// table doesn't exist already, note that it is created // table doesn't exist already, note that it is created
if (!table._id) { if (!table._id) {
table.created = true table.created = true
} }
let tableToSave = { let tableToSave: TableRequest = {
type: "table", type: "table",
_id: buildExternalTableId(datasourceId, table.name), _id: buildExternalTableId(datasourceId, table.name),
...table, ...table,
@ -188,10 +222,10 @@ exports.save = async function (ctx) {
ctx.throw(400, "A column type has changed.") ctx.throw(400, "A column type has changed.")
} }
const db = getAppDB() const db = context.getAppDB()
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
const oldTables = cloneDeep(datasource.entities) const oldTables = cloneDeep(datasource.entities)
const tables = datasource.entities const tables: Record<string, Table> = datasource.entities
const extraTablesToUpdate = [] const extraTablesToUpdate = []
@ -203,8 +237,11 @@ exports.save = async function (ctx) {
const relatedTable = Object.values(tables).find( const relatedTable = Object.values(tables).find(
table => table._id === schema.tableId table => table._id === schema.tableId
) )
const relatedColumnName = schema.fieldName if (!relatedTable) {
const relationType = schema.relationshipType continue
}
const relatedColumnName = schema.fieldName!
const relationType = schema.relationshipType!
if (relationType === RelationshipTypes.MANY_TO_MANY) { if (relationType === RelationshipTypes.MANY_TO_MANY) {
const junctionTable = generateManyLinkSchema( const junctionTable = generateManyLinkSchema(
datasource, datasource,
@ -244,9 +281,7 @@ exports.save = async function (ctx) {
cleanupRelationships(tableToSave, tables, oldTable) cleanupRelationships(tableToSave, tables, oldTable)
const operation = oldTable const operation = oldTable ? Operation.UPDATE_TABLE : Operation.CREATE_TABLE
? DataSourceOperation.UPDATE_TABLE
: DataSourceOperation.CREATE_TABLE
await makeTableRequest( await makeTableRequest(
datasource, datasource,
operation, operation,
@ -258,9 +293,7 @@ exports.save = async function (ctx) {
// update any extra tables (like foreign keys in other tables) // update any extra tables (like foreign keys in other tables)
for (let extraTable of extraTablesToUpdate) { for (let extraTable of extraTablesToUpdate) {
const oldExtraTable = oldTables[extraTable.name] const oldExtraTable = oldTables[extraTable.name]
let op = oldExtraTable let op = oldExtraTable ? Operation.UPDATE_TABLE : Operation.CREATE_TABLE
? DataSourceOperation.UPDATE_TABLE
: DataSourceOperation.CREATE_TABLE
await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable) await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable)
} }
@ -280,18 +313,20 @@ exports.save = async function (ctx) {
return tableToSave return tableToSave
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const tableToDelete = await sdk.tables.getTable(ctx.params.tableId) const tableToDelete: TableRequest = await sdk.tables.getTable(
ctx.params.tableId
)
if (!tableToDelete || !tableToDelete.created) { if (!tableToDelete || !tableToDelete.created) {
ctx.throw(400, "Cannot delete tables which weren't created in Budibase.") ctx.throw(400, "Cannot delete tables which weren't created in Budibase.")
} }
const datasourceId = getDatasourceId(tableToDelete) const datasourceId = getDatasourceId(tableToDelete)
const db = getAppDB() const db = context.getAppDB()
const datasource = await db.get(datasourceId) const datasource = await db.get(datasourceId)
const tables = datasource.entities const tables = datasource.entities
const operation = DataSourceOperation.DELETE_TABLE const operation = Operation.DELETE_TABLE
await makeTableRequest(datasource, operation, tableToDelete, tables) await makeTableRequest(datasource, operation, tableToDelete, tables)
cleanupRelationships(tableToDelete, tables) cleanupRelationships(tableToDelete, tables)
@ -302,7 +337,7 @@ exports.destroy = async function (ctx) {
return tableToDelete return tableToDelete
} }
exports.bulkImport = async function (ctx) { export async function bulkImport(ctx: BBContext) {
const table = await sdk.tables.getTable(ctx.params.tableId) const table = await sdk.tables.getTable(ctx.params.tableId)
const { dataImport } = ctx.request.body const { dataImport } = ctx.request.body
if (!dataImport || !dataImport.schema || !dataImport.csvString) { if (!dataImport || !dataImport.schema || !dataImport.csvString) {

View file

@ -1,13 +1,13 @@
const internal = require("./internal") import * as internal from "./internal"
const external = require("./external") import * as external from "./external"
const csvParser = require("../../../utilities/csvParser") import csvParser from "../../../utilities/csvParser"
const { isExternalTable, isSQL } = require("../../../integrations/utils") import { isExternalTable, isSQL } from "../../../integrations/utils"
const { getDatasourceParams } = require("../../../db/utils") import { getDatasourceParams } from "../../../db/utils"
const { getAppDB } = require("@budibase/backend-core/context") import { context, events } from "@budibase/backend-core"
const { events } = require("@budibase/backend-core") import { Table, BBContext } from "@budibase/types"
const sdk = require("../../../sdk") import sdk from "../../../sdk"
function pickApi({ tableId, table }) { function pickApi({ tableId, table }: { tableId?: string; table?: Table }) {
if (table && !tableId) { if (table && !tableId) {
tableId = table._id tableId = table._id
} }
@ -20,8 +20,8 @@ function pickApi({ tableId, table }) {
} }
// covers both internal and external // covers both internal and external
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const internal = await sdk.tables.getAllInternalTables() const internal = await sdk.tables.getAllInternalTables()
@ -34,7 +34,7 @@ exports.fetch = async function (ctx) {
const external = externalTables.rows.flatMap(tableDoc => { const external = externalTables.rows.flatMap(tableDoc => {
let entities = tableDoc.doc.entities let entities = tableDoc.doc.entities
if (entities) { if (entities) {
return Object.values(entities).map(entity => ({ return Object.values(entities).map((entity: any) => ({
...entity, ...entity,
type: "external", type: "external",
sourceId: tableDoc.doc._id, sourceId: tableDoc.doc._id,
@ -48,12 +48,12 @@ exports.fetch = async function (ctx) {
ctx.body = [...internal, ...external] ctx.body = [...internal, ...external]
} }
exports.find = async function (ctx) { export async function find(ctx: BBContext) {
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
ctx.body = await sdk.tables.getTable(tableId) ctx.body = await sdk.tables.getTable(tableId)
} }
exports.save = async function (ctx) { export async function save(ctx: BBContext) {
const appId = ctx.appId const appId = ctx.appId
const table = ctx.request.body const table = ctx.request.body
const importFormat = const importFormat =
@ -74,7 +74,7 @@ exports.save = async function (ctx) {
ctx.body = savedTable ctx.body = savedTable
} }
exports.destroy = async function (ctx) { export async function destroy(ctx: BBContext) {
const appId = ctx.appId const appId = ctx.appId
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
const deletedTable = await pickApi({ tableId }).destroy(ctx) const deletedTable = await pickApi({ tableId }).destroy(ctx)
@ -86,7 +86,7 @@ exports.destroy = async function (ctx) {
ctx.body = { message: `Table ${tableId} deleted.` } ctx.body = { message: `Table ${tableId} deleted.` }
} }
exports.bulkImport = async function (ctx) { export async function bulkImport(ctx: BBContext) {
const tableId = ctx.params.tableId const tableId = ctx.params.tableId
await pickApi({ tableId }).bulkImport(ctx) await pickApi({ tableId }).bulkImport(ctx)
// right now we don't trigger anything for bulk import because it // right now we don't trigger anything for bulk import because it
@ -96,7 +96,7 @@ exports.bulkImport = async function (ctx) {
ctx.body = { message: `Bulk rows created.` } ctx.body = { message: `Bulk rows created.` }
} }
exports.validateCSVSchema = async function (ctx) { export async function validateCSVSchema(ctx: BBContext) {
// tableId being specified means its an import to an existing table // tableId being specified means its an import to an existing table
const { csvString, schema = {}, tableId } = ctx.request.body const { csvString, schema = {}, tableId } = ctx.request.body
let existingTable let existingTable

View file

@ -133,7 +133,7 @@ export async function save(ctx: any) {
tableToSave._rev = result.rev tableToSave._rev = result.rev
} }
// has to run after, make sure it has _id // has to run after, make sure it has _id
await runStaticFormulaChecks(tableToSave, { oldTable, deletion: null }) await runStaticFormulaChecks(tableToSave, { oldTable, deletion: false })
return tableToSave return tableToSave
} }
@ -176,7 +176,6 @@ export async function destroy(ctx: any) {
// has to run after, make sure it has _id // has to run after, make sure it has _id
await runStaticFormulaChecks(tableToDelete, { await runStaticFormulaChecks(tableToDelete, {
oldTable: null,
deletion: true, deletion: true,
}) })
await cleanupAttachments(tableToDelete, { await cleanupAttachments(tableToDelete, {

View file

@ -13,28 +13,28 @@ import {
} from "../../../constants" } from "../../../constants"
import { getViews, saveView } from "../view/utils" import { getViews, saveView } from "../view/utils"
import viewTemplate from "../view/viewBuilder" import viewTemplate from "../view/viewBuilder"
const { getAppDB } = require("@budibase/backend-core/context")
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { quotas } from "@budibase/pro" import { quotas } from "@budibase/pro"
import { events } from "@budibase/backend-core" import { events, context } from "@budibase/backend-core"
import { Database } from "@budibase/types"
export async function clearColumns(table: any, columnNames: any) { export async function clearColumns(table: any, columnNames: any) {
const db = getAppDB() const db: Database = context.getAppDB()
const rows = await db.allDocs( const rows = await db.allDocs(
getRowParams(table._id, null, { getRowParams(table._id, null, {
include_docs: true, include_docs: true,
}) })
) )
return db.bulkDocs( return (await db.bulkDocs(
rows.rows.map(({ doc }: any) => { rows.rows.map(({ doc }: any) => {
columnNames.forEach((colName: any) => delete doc[colName]) columnNames.forEach((colName: any) => delete doc[colName])
return doc return doc
}) })
) )) as { id: string; _rev?: string }[]
} }
export async function checkForColumnUpdates(oldTable: any, updatedTable: any) { export async function checkForColumnUpdates(oldTable: any, updatedTable: any) {
const db = getAppDB() const db = context.getAppDB()
let updatedRows = [] let updatedRows = []
const rename = updatedTable._rename const rename = updatedTable._rename
let deletedColumns: any = [] let deletedColumns: any = []
@ -133,7 +133,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
return table return table
} }
const db = getAppDB() const db = context.getAppDB()
// Populate the table with rows imported from CSV in a bulk update // Populate the table with rows imported from CSV in a bulk update
const data = await transform({ const data = await transform({
...dataImport, ...dataImport,
@ -150,7 +150,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
} }
export async function handleSearchIndexes(table: any) { export async function handleSearchIndexes(table: any) {
const db = getAppDB() const db = context.getAppDB()
// create relevant search indexes // create relevant search indexes
if (table.indexes && table.indexes.length > 0) { if (table.indexes && table.indexes.length > 0) {
const currentIndexes = await db.getIndexes() const currentIndexes = await db.getIndexes()
@ -214,7 +214,7 @@ class TableSaveFunctions {
rows: any rows: any
constructor({ user, oldTable, dataImport }: any) { constructor({ user, oldTable, dataImport }: any) {
this.db = getAppDB() this.db = context.getAppDB()
this.user = user this.user = user
this.oldTable = oldTable this.oldTable = oldTable
this.dataImport = dataImport this.dataImport = dataImport
@ -338,7 +338,7 @@ export function generateJunctionTableName(
return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}` return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`
} }
export function foreignKeyStructure(keyName: any, meta = null) { export function foreignKeyStructure(keyName: any, meta?: any) {
const structure: any = { const structure: any = {
type: FieldTypes.NUMBER, type: FieldTypes.NUMBER,
constraints: {}, constraints: {},

View file

@ -1,17 +1,20 @@
const fetch = require("node-fetch") import nodeFetch from "node-fetch"
const { downloadTemplate } = require("../../utilities/fileSystem") import { downloadTemplate as dlTemplate } from "../../utilities/fileSystem"
const env = require("../../environment") import env from "../../environment"
import { BBContext } from "@budibase/types"
// development flag, can be used to test against templates exported locally // development flag, can be used to test against templates exported locally
const DEFAULT_TEMPLATES_BUCKET = const DEFAULT_TEMPLATES_BUCKET =
"prod-budi-templates.s3-eu-west-1.amazonaws.com" "prod-budi-templates.s3-eu-west-1.amazonaws.com"
exports.fetch = async function (ctx) { export async function fetch(ctx: BBContext) {
let type = env.TEMPLATE_REPOSITORY let type = env.TEMPLATE_REPOSITORY
let response, let response,
error = false error = false
try { try {
response = await fetch(`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`) response = await nodeFetch(
`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`
)
if (response.status !== 200) { if (response.status !== 200) {
error = true error = true
} }
@ -29,10 +32,10 @@ exports.fetch = async function (ctx) {
// can't currently test this, have to ignore from coverage // can't currently test this, have to ignore from coverage
/* istanbul ignore next */ /* istanbul ignore next */
exports.downloadTemplate = async function (ctx) { export async function downloadTemplate(ctx: BBContext) {
const { type, name } = ctx.params const { type, name } = ctx.params
await downloadTemplate(type, name) await dlTemplate(type, name)
ctx.body = { ctx.body = {
message: `template ${type}:${name} downloaded successfully.`, message: `template ${type}:${name} downloaded successfully.`,

View file

@ -1,23 +1,22 @@
const { import {
generateUserMetadataID, generateUserMetadataID,
getUserMetadataParams, getUserMetadataParams,
generateUserFlagID, generateUserFlagID,
} = require("../../db/utils") } from "../../db/utils"
const { InternalTables } = require("../../db/utils") import { InternalTables } from "../../db/utils"
const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global") import { getGlobalUsers, getRawGlobalUser } from "../../utilities/global"
const { getFullUser } = require("../../utilities/users") import { getFullUser } from "../../utilities/users"
const { isEqual } = require("lodash") import { isEqual } from "lodash"
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") import {
const { context,
getDevelopmentAppID, constants,
getProdAppIDs, roles as rolesCore,
dbExists, db as dbCore,
} = require("@budibase/backend-core/db") } from "@budibase/backend-core"
const { UserStatus } = require("@budibase/backend-core/constants") import { BBContext, User } from "@budibase/types"
const { getAppDB, doInAppContext } = require("@budibase/backend-core/context")
async function rawMetadata() { async function rawMetadata() {
const db = getAppDB() const db = context.getAppDB()
return ( return (
await db.allDocs( await db.allDocs(
getUserMetadataParams(null, { getUserMetadataParams(null, {
@ -27,9 +26,9 @@ async function rawMetadata() {
).rows.map(row => row.doc) ).rows.map(row => row.doc)
} }
function combineMetadataAndUser(user, metadata) { function combineMetadataAndUser(user: any, metadata: any) {
// skip users with no access // skip users with no access
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) { if (user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC) {
return null return null
} }
delete user._rev delete user._rev
@ -55,9 +54,9 @@ function combineMetadataAndUser(user, metadata) {
return null return null
} }
exports.syncGlobalUsers = async () => { export async function syncGlobalUsers() {
// sync user metadata // sync user metadata
const db = getAppDB() const db = context.getAppDB()
const [users, metadata] = await Promise.all([getGlobalUsers(), rawMetadata()]) const [users, metadata] = await Promise.all([getGlobalUsers(), rawMetadata()])
const toWrite = [] const toWrite = []
for (let user of users) { for (let user of users) {
@ -69,13 +68,13 @@ exports.syncGlobalUsers = async () => {
await db.bulkDocs(toWrite) await db.bulkDocs(toWrite)
} }
exports.syncUser = async function (ctx) { export async function syncUser(ctx: BBContext) {
let deleting = false, let deleting = false,
user user: User | any
const userId = ctx.params.id const userId = ctx.params.id
try { try {
user = await getRawGlobalUser(userId) user = await getRawGlobalUser(userId)
} catch (err) { } catch (err: any) {
if (err && err.status === 404) { if (err && err.status === 404) {
user = {} user = {}
deleting = true deleting = true
@ -92,21 +91,21 @@ exports.syncUser = async function (ctx) {
let prodAppIds let prodAppIds
// if they are a builder then get all production app IDs // if they are a builder then get all production app IDs
if ((user.builder && user.builder.global) || deleting) { if ((user.builder && user.builder.global) || deleting) {
prodAppIds = await getProdAppIDs() prodAppIds = await dbCore.getProdAppIDs()
} else { } else {
prodAppIds = Object.entries(roles) prodAppIds = Object.entries(roles)
.filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC) .filter(entry => entry[1] !== rolesCore.BUILTIN_ROLE_IDS.PUBLIC)
.map(([appId]) => appId) .map(([appId]) => appId)
} }
for (let prodAppId of prodAppIds) { for (let prodAppId of prodAppIds) {
const roleId = roles[prodAppId] const roleId = roles[prodAppId]
const devAppId = getDevelopmentAppID(prodAppId) const devAppId = dbCore.getDevelopmentAppID(prodAppId)
for (let appId of [prodAppId, devAppId]) { for (let appId of [prodAppId, devAppId]) {
if (!(await dbExists(appId))) { if (!(await dbCore.dbExists(appId))) {
continue continue
} }
await doInAppContext(appId, async () => { await context.doInAppContext(appId, async () => {
const db = getAppDB() const db = context.getAppDB()
const metadataId = generateUserMetadataID(userId) const metadataId = generateUserMetadataID(userId)
let metadata let metadata
try { try {
@ -127,8 +126,8 @@ exports.syncUser = async function (ctx) {
? combineMetadataAndUser(user, metadata) ? combineMetadataAndUser(user, metadata)
: { : {
...metadata, ...metadata,
status: UserStatus.INACTIVE, status: constants.UserStatus.INACTIVE,
metadata: BUILTIN_ROLE_IDS.PUBLIC, metadata: rolesCore.BUILTIN_ROLE_IDS.PUBLIC,
} }
// if its null then there was no updates required // if its null then there was no updates required
if (combined) { if (combined) {
@ -142,10 +141,9 @@ exports.syncUser = async function (ctx) {
} }
} }
exports.fetchMetadata = async function (ctx) { export async function fetchMetadata(ctx: BBContext) {
const database = getAppDB()
const global = await getGlobalUsers() const global = await getGlobalUsers()
const metadata = await rawMetadata(database) const metadata = await rawMetadata()
const users = [] const users = []
for (let user of global) { for (let user of global) {
// find the metadata that matches up to the global ID // find the metadata that matches up to the global ID
@ -162,18 +160,18 @@ exports.fetchMetadata = async function (ctx) {
ctx.body = users ctx.body = users
} }
exports.updateSelfMetadata = async function (ctx) { export async function updateSelfMetadata(ctx: BBContext) {
// overwrite the ID with current users // overwrite the ID with current users
ctx.request.body._id = ctx.user._id ctx.request.body._id = ctx.user?._id
// make sure no stale rev // make sure no stale rev
delete ctx.request.body._rev delete ctx.request.body._rev
// make sure no csrf token // make sure no csrf token
delete ctx.request.body.csrfToken delete ctx.request.body.csrfToken
await exports.updateMetadata(ctx) await updateMetadata(ctx)
} }
exports.updateMetadata = async function (ctx) { export async function updateMetadata(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const user = ctx.request.body const user = ctx.request.body
// this isn't applicable to the user // this isn't applicable to the user
delete user.roles delete user.roles
@ -184,8 +182,8 @@ exports.updateMetadata = async function (ctx) {
ctx.body = await db.put(metadata) ctx.body = await db.put(metadata)
} }
exports.destroyMetadata = async function (ctx) { export async function destroyMetadata(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
try { try {
const dbUser = await db.get(ctx.params.id) const dbUser = await db.get(ctx.params.id)
await db.remove(dbUser._id, dbUser._rev) await db.remove(dbUser._id, dbUser._rev)
@ -197,18 +195,18 @@ exports.destroyMetadata = async function (ctx) {
} }
} }
exports.findMetadata = async function (ctx) { export async function findMetadata(ctx: BBContext) {
ctx.body = await getFullUser(ctx, ctx.params.id) ctx.body = await getFullUser(ctx, ctx.params.id)
} }
exports.setFlag = async function (ctx) { export async function setFlag(ctx: BBContext) {
const userId = ctx.user._id const userId = ctx.user?._id
const { flag, value } = ctx.request.body const { flag, value } = ctx.request.body
if (!flag) { if (!flag) {
ctx.throw(400, "Must supply a 'flag' field in request body.") ctx.throw(400, "Must supply a 'flag' field in request body.")
} }
const flagDocId = generateUserFlagID(userId) const flagDocId = generateUserFlagID(userId!)
const db = getAppDB() const db = context.getAppDB()
let doc let doc
try { try {
doc = await db.get(flagDocId) doc = await db.get(flagDocId)
@ -220,10 +218,10 @@ exports.setFlag = async function (ctx) {
ctx.body = { message: "Flag set successfully" } ctx.body = { message: "Flag set successfully" }
} }
exports.getFlags = async function (ctx) { export async function getFlags(ctx: BBContext) {
const userId = ctx.user._id const userId = ctx.user?._id
const docId = generateUserFlagID(userId) const docId = generateUserFlagID(userId!)
const db = getAppDB() const db = context.getAppDB()
let doc let doc
try { try {
doc = await db.get(docId) doc = await db.get(docId)

View file

@ -1,4 +1,6 @@
exports.csv = function (headers, rows) { import { Row } from "@budibase/types"
export function csv(headers: string[], rows: Row[]) {
let csv = headers.map(key => `"${key}"`).join(",") let csv = headers.map(key => `"${key}"`).join(",")
for (let row of rows) { for (let row of rows) {
@ -16,11 +18,11 @@ exports.csv = function (headers, rows) {
return csv return csv
} }
exports.json = function (headers, rows) { export function json(headers: string[], rows: Row[]) {
return JSON.stringify(rows, undefined, 2) return JSON.stringify(rows, undefined, 2)
} }
exports.ExportFormats = { export const ExportFormats = {
CSV: "csv", CSV: "csv",
JSON: "json", JSON: "json",
} }

View file

@ -1,21 +1,29 @@
const viewTemplate = require("./viewBuilder") import viewTemplate from "./viewBuilder"
const { apiFileReturn } = require("../../../utilities/fileSystem") import { apiFileReturn } from "../../../utilities/fileSystem"
const exporters = require("./exporters") import * as exporters from "./exporters"
const { saveView, getView, getViews, deleteView } = require("./utils") import { deleteView, getView, getViews, saveView } from "./utils"
const { fetchView } = require("../row") import { fetchView } from "../row"
const { FieldTypes } = require("../../../constants") import { FieldTypes } from "../../../constants"
const { getAppDB } = require("@budibase/backend-core/context") import { context, events } from "@budibase/backend-core"
const { events } = require("@budibase/backend-core") import { DocumentType } from "../../../db/utils"
const { DocumentType } = require("../../../db/utils") import sdk from "../../../sdk"
const { cloneDeep, isEqual } = require("lodash") import {
const sdk = require("../../../sdk") BBContext,
Row,
Table,
TableExportFormat,
TableSchema,
View,
} from "@budibase/types"
exports.fetch = async ctx => { const { cloneDeep, isEqual } = require("lodash")
export async function fetch(ctx: BBContext) {
ctx.body = await getViews() ctx.body = await getViews()
} }
exports.save = async ctx => { export async function save(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const { originalName, ...viewToSave } = ctx.request.body const { originalName, ...viewToSave } = ctx.request.body
const view = viewTemplate(viewToSave) const view = viewTemplate(viewToSave)
const viewName = viewToSave.name const viewName = viewToSave.name
@ -47,7 +55,7 @@ exports.save = async ctx => {
} }
} }
const calculationEvents = async (existingView, newView) => { export async function calculationEvents(existingView: View, newView: View) {
const existingCalculation = existingView && existingView.calculation const existingCalculation = existingView && existingView.calculation
const newCalculation = newView && newView.calculation const newCalculation = newView && newView.calculation
@ -68,7 +76,7 @@ const calculationEvents = async (existingView, newView) => {
} }
} }
const filterEvents = async (existingView, newView) => { export async function filterEvents(existingView: View, newView: View) {
const hasExistingFilters = !!( const hasExistingFilters = !!(
existingView && existingView &&
existingView.filters && existingView.filters &&
@ -93,7 +101,7 @@ const filterEvents = async (existingView, newView) => {
} }
} }
const handleViewEvents = async (existingView, newView) => { async function handleViewEvents(existingView: View, newView: View) {
if (!existingView) { if (!existingView) {
await events.view.created(newView) await events.view.created(newView)
} else { } else {
@ -103,8 +111,8 @@ const handleViewEvents = async (existingView, newView) => {
await filterEvents(existingView, newView) await filterEvents(existingView, newView)
} }
exports.destroy = async ctx => { export async function destroy(ctx: BBContext) {
const db = getAppDB() const db = context.getAppDB()
const viewName = decodeURI(ctx.params.viewName) const viewName = decodeURI(ctx.params.viewName)
const view = await deleteView(viewName) const view = await deleteView(viewName)
const table = await db.get(view.meta.tableId) const table = await db.get(view.meta.tableId)
@ -115,11 +123,11 @@ exports.destroy = async ctx => {
ctx.body = view ctx.body = view
} }
exports.exportView = async ctx => { export async function exportView(ctx: BBContext) {
const viewName = decodeURI(ctx.query.view) const viewName = decodeURI(ctx.query.view as string)
const view = await getView(viewName) const view = await getView(viewName)
const format = ctx.query.format const format = ctx.query.format as string
if (!format || !Object.values(exporters.ExportFormats).includes(format)) { if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
ctx.throw(400, "Format must be specified, either csv or json") ctx.throw(400, "Format must be specified, either csv or json")
} }
@ -130,6 +138,7 @@ exports.exportView = async ctx => {
ctx.query = { ctx.query = {
group: view.meta.groupBy, group: view.meta.groupBy,
calculation: view.meta.calculation, calculation: view.meta.calculation,
// @ts-ignore
stats: !!view.meta.field, stats: !!view.meta.field,
field: view.meta.field, field: view.meta.field,
} }
@ -140,11 +149,11 @@ exports.exportView = async ctx => {
} }
await fetchView(ctx) await fetchView(ctx)
let rows = ctx.body let rows = ctx.body as Row[]
let schema = view && view.meta && view.meta.schema let schema: TableSchema = view && view.meta && view.meta.schema
const tableId = ctx.params.tableId || view.meta.tableId const tableId = ctx.params.tableId || view.meta.tableId
const table = await sdk.tables.getTable(tableId) const table: Table = await sdk.tables.getTable(tableId)
if (!schema) { if (!schema) {
schema = table.schema schema = table.schema
} }
@ -175,15 +184,15 @@ exports.exportView = async ctx => {
// Export part // Export part
let headers = Object.keys(schema) let headers = Object.keys(schema)
const exporter = exporters[format] const exporter = format === "csv" ? exporters.csv : exporters.json
const filename = `${viewName}.${format}` const filename = `${viewName}.${format}`
// send down the file // send down the file
ctx.attachment(filename) ctx.attachment(filename)
ctx.body = apiFileReturn(exporter(headers, rows)) ctx.body = apiFileReturn(exporter(headers, rows))
if (viewName.startsWith(DocumentType.TABLE)) { if (viewName.startsWith(DocumentType.TABLE)) {
await events.table.exported(table, format) await events.table.exported(table, format as TableExportFormat)
} else { } else {
await events.view.exported(table, format) await events.view.exported(table, format as TableExportFormat)
} }
} }

View file

@ -1,16 +1,17 @@
const { import {
ViewName, ViewName,
generateMemoryViewID, generateMemoryViewID,
getMemoryViewParams, getMemoryViewParams,
DocumentType, DocumentType,
SEPARATOR, SEPARATOR,
} = require("../../../db/utils") } from "../../../db/utils"
const env = require("../../../environment") import env from "../../../environment"
const { getAppDB } = require("@budibase/backend-core/context") import { context } from "@budibase/backend-core"
const viewBuilder = require("./viewBuilder") import viewBuilder from "./viewBuilder"
import { Database } from "@budibase/types"
exports.getView = async viewName => { export async function getView(viewName: string) {
const db = getAppDB() const db = context.getAppDB()
if (env.SELF_HOSTED) { if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
return designDoc.views[viewName] return designDoc.views[viewName]
@ -23,7 +24,7 @@ exports.getView = async viewName => {
try { try {
const viewDoc = await db.get(generateMemoryViewID(viewName)) const viewDoc = await db.get(generateMemoryViewID(viewName))
return viewDoc.view return viewDoc.view
} catch (err) { } catch (err: any) {
// Return null when PouchDB doesn't found the view // Return null when PouchDB doesn't found the view
if (err.status === 404) { if (err.status === 404) {
return null return null
@ -34,14 +35,15 @@ exports.getView = async viewName => {
} }
} }
exports.getViews = async () => { export async function getViews() {
const db = getAppDB() const db = context.getAppDB()
const response = [] const response = []
if (env.SELF_HOSTED) { if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
for (let name of Object.keys(designDoc.views)) { for (let name of Object.keys(designDoc.views)) {
// Only return custom views, not built ins // Only return custom views, not built ins
if (Object.values(ViewName).indexOf(name) !== -1) { const viewNames = Object.values(ViewName) as string[]
if (viewNames.indexOf(name) !== -1) {
continue continue
} }
response.push({ response.push({
@ -67,8 +69,12 @@ exports.getViews = async () => {
return response return response
} }
exports.saveView = async (originalName, viewName, viewTemplate) => { export async function saveView(
const db = getAppDB() originalName: string | null,
viewName: string,
viewTemplate: any
) {
const db = context.getAppDB()
if (env.SELF_HOSTED) { if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
designDoc.views = { designDoc.views = {
@ -83,7 +89,7 @@ exports.saveView = async (originalName, viewName, viewTemplate) => {
} else { } else {
const id = generateMemoryViewID(viewName) const id = generateMemoryViewID(viewName)
const originalId = originalName ? generateMemoryViewID(originalName) : null const originalId = originalName ? generateMemoryViewID(originalName) : null
const viewDoc = { const viewDoc: any = {
_id: id, _id: id,
view: viewTemplate, view: viewTemplate,
name: viewName, name: viewName,
@ -105,8 +111,8 @@ exports.saveView = async (originalName, viewName, viewTemplate) => {
} }
} }
exports.deleteView = async viewName => { export async function deleteView(viewName: string) {
const db = getAppDB() const db = context.getAppDB()
if (env.SELF_HOSTED) { if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
const view = designDoc.views[viewName] const view = designDoc.views[viewName]
@ -121,7 +127,7 @@ exports.deleteView = async viewName => {
} }
} }
exports.migrateToInMemoryView = async (db, viewName) => { export async function migrateToInMemoryView(db: Database, viewName: string) {
// delete the view initially // delete the view initially
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
// run the view back through the view builder to update it // run the view back through the view builder to update it
@ -131,7 +137,7 @@ exports.migrateToInMemoryView = async (db, viewName) => {
await exports.saveView(db, null, viewName, view) await exports.saveView(db, null, viewName, view)
} }
exports.migrateToDesignView = async (db, viewName) => { export async function migrateToDesignView(db: Database, viewName: string) {
let view = await db.get(generateMemoryViewID(viewName)) let view = await db.get(generateMemoryViewID(viewName))
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
designDoc.views[viewName] = viewBuilder(view.view.meta) designDoc.views[viewName] = viewBuilder(view.view.meta)
@ -139,7 +145,7 @@ exports.migrateToDesignView = async (db, viewName) => {
await db.remove(view._id, view._rev) await db.remove(view._id, view._rev)
} }
exports.getFromDesignDoc = async (db, viewName) => { export async function getFromDesignDoc(db: Database, viewName: string) {
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
let view = designDoc.views[viewName] let view = designDoc.views[viewName]
if (view == null) { if (view == null) {
@ -148,7 +154,7 @@ exports.getFromDesignDoc = async (db, viewName) => {
return view return view
} }
exports.getFromMemoryDoc = async (db, viewName) => { export async function getFromMemoryDoc(db: Database, viewName: string) {
let view = await db.get(generateMemoryViewID(viewName)) let view = await db.get(generateMemoryViewID(viewName))
if (view) { if (view) {
view = view.view view = view.view

View file

@ -1,20 +1,15 @@
const Router = require("@koa/router") import Router from "@koa/router"
const { import { errors, auth } from "@budibase/backend-core"
buildAuthMiddleware, import currentApp from "../middleware/currentapp"
auditLog, import zlib from "zlib"
buildTenancyMiddleware, import { mainRoutes, staticRoutes, publicRoutes } from "./routes"
} = require("@budibase/backend-core/auth") import pkg from "../../package.json"
const { errors } = require("@budibase/backend-core") import env from "../environment"
const currentApp = require("../middleware/currentapp") import { middleware as pro } from "@budibase/pro"
export { shutdown } from "./routes/public"
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib")
const { mainRoutes, staticRoutes, publicRoutes } = require("./routes")
const pkg = require("../../package.json")
const env = require("../environment")
const { middleware: pro } = require("@budibase/pro")
const { shutdown } = require("./routes/public")
const router = new Router() export const router: Router = new Router()
router.get("/health", ctx => (ctx.status = 200)) router.get("/health", ctx => (ctx.status = 200))
router.get("/version", ctx => (ctx.body = pkg.version)) router.get("/version", ctx => (ctx.body = pkg.version))
@ -42,7 +37,7 @@ router
// re-direct before any middlewares occur // re-direct before any middlewares occur
.redirect("/", "/builder") .redirect("/", "/builder")
.use( .use(
buildAuthMiddleware(null, { auth.buildAuthMiddleware(null, {
publicAllowed: true, publicAllowed: true,
}) })
) )
@ -50,19 +45,20 @@ router
// the server can be public anywhere, so nowhere should throw errors // the server can be public anywhere, so nowhere should throw errors
// if the tenancy has not been set, it'll have to be discovered at application layer // if the tenancy has not been set, it'll have to be discovered at application layer
.use( .use(
buildTenancyMiddleware(null, null, { auth.buildTenancyMiddleware(null, null, {
noTenancyRequired: true, noTenancyRequired: true,
}) })
) )
.use(pro.licensing()) .use(pro.licensing())
// @ts-ignore
.use(currentApp) .use(currentApp)
.use(auditLog) .use(auth.auditLog)
// error handling middleware // error handling middleware
router.use(async (ctx, next) => { router.use(async (ctx, next) => {
try { try {
await next() await next()
} catch (err) { } catch (err: any) {
ctx.status = err.status || err.statusCode || 500 ctx.status = err.status || err.statusCode || 500
const error = errors.getPublicError(err) const error = errors.getPublicError(err)
ctx.body = { ctx.body = {
@ -91,6 +87,3 @@ router.use(publicRoutes.allowedMethods())
// WARNING - static routes will catch everything else after them this must be last // WARNING - static routes will catch everything else after them this must be last
router.use(staticRoutes.routes()) router.use(staticRoutes.routes())
router.use(staticRoutes.allowedMethods()) router.use(staticRoutes.allowedMethods())
module.exports.router = router
module.exports.shutdown = shutdown

View file

@ -1,9 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/analytics")
const router = new Router()
router.get("/api/bbtel", controller.isEnabled)
router.post("/api/bbtel/ping", controller.ping)
module.exports = router

View file

@ -0,0 +1,9 @@
import Router from "@koa/router"
import * as controller from "../controllers/analytics"
const router: Router = new Router()
router.get("/api/bbtel", controller.isEnabled)
router.post("/api/bbtel/ping", controller.ping)
export = router

View file

@ -1,12 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/apikeys")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
router
.get("/api/keys", authorized(BUILDER), controller.fetch)
.put("/api/keys/:key", authorized(BUILDER), controller.update)
module.exports = router

View file

@ -0,0 +1,12 @@
import Router from "@koa/router"
import * as controller from "../controllers/apikeys"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router
.get("/api/keys", authorized(permissions.BUILDER), controller.fetch)
.put("/api/keys/:key", authorized(permissions.BUILDER), controller.update)
export = router

View file

@ -1,16 +1,20 @@
import Router from "@koa/router" import Router from "@koa/router"
import * as controller from "../controllers/application" import * as controller from "../controllers/application"
import authorized from "../../middleware/authorized" import authorized from "../../middleware/authorized"
import { BUILDER } from "@budibase/backend-core/permissions" import { permissions } from "@budibase/backend-core"
import { applicationValidator } from "./utils/validators" import { applicationValidator } from "./utils/validators"
const router = new Router() const router: Router = new Router()
router router
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync) .post(
"/api/applications/:appId/sync",
authorized(permissions.BUILDER),
controller.sync
)
.post( .post(
"/api/applications", "/api/applications",
authorized(BUILDER), authorized(permissions.BUILDER),
applicationValidator(), applicationValidator(),
controller.create controller.create
) )
@ -19,20 +23,24 @@ router
.get("/api/applications/:appId/appPackage", controller.fetchAppPackage) .get("/api/applications/:appId/appPackage", controller.fetchAppPackage)
.put( .put(
"/api/applications/:appId", "/api/applications/:appId",
authorized(BUILDER), authorized(permissions.BUILDER),
applicationValidator({ isCreate: false }), applicationValidator({ isCreate: false }),
controller.update controller.update
) )
.post( .post(
"/api/applications/:appId/client/update", "/api/applications/:appId/client/update",
authorized(BUILDER), authorized(permissions.BUILDER),
controller.updateClient controller.updateClient
) )
.post( .post(
"/api/applications/:appId/client/revert", "/api/applications/:appId/client/revert",
authorized(BUILDER), authorized(permissions.BUILDER),
controller.revertClient controller.revertClient
) )
.delete("/api/applications/:appId", authorized(BUILDER), controller.destroy) .delete(
"/api/applications/:appId",
authorized(permissions.BUILDER),
controller.destroy
)
export default router export = router

View file

@ -1,8 +1,8 @@
import Router from "@koa/router" import Router from "@koa/router"
import * as controller from "../controllers/auth" import * as controller from "../controllers/auth"
const router = new Router() const router: Router = new Router()
router.get("/api/self", controller.fetchSelf) router.get("/api/self", controller.fetchSelf)
export default router export = router

View file

@ -1,85 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/automation")
const authorized = require("../../middleware/authorized")
const {
BUILDER,
PermissionLevel,
PermissionType,
} = require("@budibase/backend-core/permissions")
const { bodyResource, paramResource } = require("../../middleware/resourceId")
const {
middleware: appInfoMiddleware,
AppType,
} = require("../../middleware/appInfo")
const { automationValidator } = require("./utils/validators")
const router = new Router()
router
.get(
"/api/automations/trigger/list",
authorized(BUILDER),
controller.getTriggerList
)
.get(
"/api/automations/action/list",
authorized(BUILDER),
controller.getActionList
)
.get(
"/api/automations/definitions/list",
authorized(BUILDER),
controller.getDefinitionList
)
.get("/api/automations", authorized(BUILDER), controller.fetch)
.get(
"/api/automations/:id",
paramResource("id"),
authorized(BUILDER),
controller.find
)
.put(
"/api/automations",
bodyResource("_id"),
authorized(BUILDER),
automationValidator(true),
controller.update
)
.post(
"/api/automations",
authorized(BUILDER),
automationValidator(false),
controller.create
)
.post(
"/api/automations/logs/search",
authorized(BUILDER),
controller.logSearch
)
.delete(
"/api/automations/logs",
authorized(BUILDER),
controller.clearLogError
)
.delete(
"/api/automations/:id/:rev",
paramResource("id"),
authorized(BUILDER),
controller.destroy
)
.post(
"/api/automations/:id/trigger",
appInfoMiddleware({ appType: AppType.PROD }),
paramResource("id"),
authorized(PermissionType.AUTOMATION, PermissionLevel.EXECUTE),
controller.trigger
)
.post(
"/api/automations/:id/test",
appInfoMiddleware({ appType: AppType.DEV }),
paramResource("id"),
authorized(PermissionType.AUTOMATION, PermissionLevel.EXECUTE),
controller.test
)
module.exports = router

View file

@ -0,0 +1,87 @@
import Router from "@koa/router"
import * as controller from "../controllers/automation"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import { bodyResource, paramResource } from "../../middleware/resourceId"
import {
middleware as appInfoMiddleware,
AppType,
} from "../../middleware/appInfo"
import { automationValidator } from "./utils/validators"
const router: Router = new Router()
router
.get(
"/api/automations/trigger/list",
authorized(permissions.BUILDER),
controller.getTriggerList
)
.get(
"/api/automations/action/list",
authorized(permissions.BUILDER),
controller.getActionList
)
.get(
"/api/automations/definitions/list",
authorized(permissions.BUILDER),
controller.getDefinitionList
)
.get("/api/automations", authorized(permissions.BUILDER), controller.fetch)
.get(
"/api/automations/:id",
paramResource("id"),
authorized(permissions.BUILDER),
controller.find
)
.put(
"/api/automations",
bodyResource("_id"),
authorized(permissions.BUILDER),
automationValidator(true),
controller.update
)
.post(
"/api/automations",
authorized(permissions.BUILDER),
automationValidator(false),
controller.create
)
.post(
"/api/automations/logs/search",
authorized(permissions.BUILDER),
controller.logSearch
)
.delete(
"/api/automations/logs",
authorized(permissions.BUILDER),
controller.clearLogError
)
.delete(
"/api/automations/:id/:rev",
paramResource("id"),
authorized(permissions.BUILDER),
controller.destroy
)
.post(
"/api/automations/:id/trigger",
appInfoMiddleware({ appType: AppType.PROD }),
paramResource("id"),
authorized(
permissions.PermissionType.AUTOMATION,
permissions.PermissionLevel.EXECUTE
),
controller.trigger
)
.post(
"/api/automations/:id/test",
appInfoMiddleware({ appType: AppType.DEV }),
paramResource("id"),
authorized(
permissions.PermissionType.AUTOMATION,
permissions.PermissionLevel.EXECUTE
),
controller.test
)
export = router

View file

@ -1,10 +1,14 @@
import Router from "@koa/router" import Router from "@koa/router"
import * as controller from "../controllers/backup" import * as controller from "../controllers/backup"
import authorized from "../../middleware/authorized" import authorized from "../../middleware/authorized"
import { BUILDER } from "@budibase/backend-core/permissions" import { permissions } from "@budibase/backend-core"
const router = new Router() const router: Router = new Router()
router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump) router.get(
"/api/backups/export",
authorized(permissions.BUILDER),
controller.exportAppDump
)
export default router export = router

View file

@ -1,14 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/cloud")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
router
.get("/api/cloud/export", authorized(BUILDER), controller.exportApps)
// has to be public, only run if apps don't exist
.post("/api/cloud/import", controller.importApps)
.get("/api/cloud/import/complete", controller.hasBeenImported)
module.exports = router

View file

@ -0,0 +1,18 @@
import Router from "@koa/router"
import * as controller from "../controllers/cloud"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router
.get(
"/api/cloud/export",
authorized(permissions.BUILDER),
controller.exportApps
)
// has to be public, only run if apps don't exist
.post("/api/cloud/import", controller.importApps)
.get("/api/cloud/import/complete", controller.hasBeenImported)
export = router

View file

@ -1,14 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/component")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
router.get(
"/api/:appId/components/definitions",
authorized(BUILDER),
controller.fetchAppComponentDefinitions
)
module.exports = router

View file

@ -0,0 +1,14 @@
import Router from "@koa/router"
import * as controller from "../controllers/component"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router.get(
"/api/:appId/components/definitions",
authorized(permissions.BUILDER),
controller.fetchAppComponentDefinitions
)
export = router

View file

@ -1,51 +0,0 @@
const Router = require("@koa/router")
const datasourceController = require("../controllers/datasource")
const authorized = require("../../middleware/authorized")
const {
BUILDER,
PermissionLevel,
PermissionType,
} = require("@budibase/backend-core/permissions")
const {
datasourceValidator,
datasourceQueryValidator,
} = require("./utils/validators")
const router = new Router()
router
.get("/api/datasources", authorized(BUILDER), datasourceController.fetch)
.get(
"/api/datasources/:datasourceId",
authorized(PermissionType.TABLE, PermissionLevel.READ),
datasourceController.find
)
.put(
"/api/datasources/:datasourceId",
authorized(PermissionType.TABLE, PermissionLevel.READ),
datasourceController.update
)
.post(
"/api/datasources/query",
authorized(PermissionType.TABLE, PermissionLevel.READ),
datasourceQueryValidator(),
datasourceController.query
)
.post(
"/api/datasources/:datasourceId/schema",
authorized(BUILDER),
datasourceController.buildSchemaFromDb
)
.post(
"/api/datasources",
authorized(BUILDER),
datasourceValidator(),
datasourceController.save
)
.delete(
"/api/datasources/:datasourceId/:revId",
authorized(BUILDER),
datasourceController.destroy
)
module.exports = router

View file

@ -0,0 +1,60 @@
import Router from "@koa/router"
import * as datasourceController from "../controllers/datasource"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import {
datasourceValidator,
datasourceQueryValidator,
} from "./utils/validators"
const router: Router = new Router()
router
.get(
"/api/datasources",
authorized(permissions.BUILDER),
datasourceController.fetch
)
.get(
"/api/datasources/:datasourceId",
authorized(
permissions.PermissionType.TABLE,
permissions.PermissionLevel.READ
),
datasourceController.find
)
.put(
"/api/datasources/:datasourceId",
authorized(
permissions.PermissionType.TABLE,
permissions.PermissionLevel.READ
),
datasourceController.update
)
.post(
"/api/datasources/query",
authorized(
permissions.PermissionType.TABLE,
permissions.PermissionLevel.READ
),
datasourceQueryValidator(),
datasourceController.query
)
.post(
"/api/datasources/:datasourceId/schema",
authorized(permissions.BUILDER),
datasourceController.buildSchemaFromDb
)
.post(
"/api/datasources",
authorized(permissions.BUILDER),
datasourceValidator(),
datasourceController.save
)
.delete(
"/api/datasources/:datasourceId/:revId",
authorized(permissions.BUILDER),
datasourceController.destroy
)
export = router

View file

@ -1,17 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/deploy")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
router
.get("/api/deployments", authorized(BUILDER), controller.fetchDeployments)
.get(
"/api/deploy/:deploymentId",
authorized(BUILDER),
controller.deploymentProgress
)
.post("/api/deploy", authorized(BUILDER), controller.deployApp)
module.exports = router

View file

@ -0,0 +1,21 @@
import Router from "@koa/router"
import * as controller from "../controllers/deploy"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router
.get(
"/api/deployments",
authorized(permissions.BUILDER),
controller.fetchDeployments
)
.get(
"/api/deploy/:deploymentId",
authorized(permissions.BUILDER),
controller.deploymentProgress
)
.post("/api/deploy", authorized(permissions.BUILDER), controller.deployApp)
export = router

View file

@ -1,26 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/dev")
const env = require("../../environment")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
function redirectPath(path) {
router
.get(`/api/${path}/:devPath(.*)`, controller.buildRedirectGet(path))
.post(`/api/${path}/:devPath(.*)`, controller.buildRedirectPost(path))
.delete(`/api/${path}/:devPath(.*)`, controller.buildRedirectDelete(path))
}
if (env.isDev() || env.isTest()) {
redirectPath("global")
redirectPath("system")
}
router
.get("/api/dev/version", authorized(BUILDER), controller.getBudibaseVersion)
.delete("/api/dev/:appId/lock", authorized(BUILDER), controller.clearLock)
.post("/api/dev/:appId/revert", authorized(BUILDER), controller.revert)
module.exports = router

View file

@ -0,0 +1,38 @@
import Router from "@koa/router"
import * as controller from "../controllers/dev"
import env from "../../environment"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
function redirectPath(path: string) {
router
.get(`/api/${path}/:devPath(.*)`, controller.buildRedirectGet(path))
.post(`/api/${path}/:devPath(.*)`, controller.buildRedirectPost(path))
.delete(`/api/${path}/:devPath(.*)`, controller.buildRedirectDelete(path))
}
if (env.isDev() || env.isTest()) {
redirectPath("global")
redirectPath("system")
}
router
.get(
"/api/dev/version",
authorized(permissions.BUILDER),
controller.getBudibaseVersion
)
.delete(
"/api/dev/:appId/lock",
authorized(permissions.BUILDER),
controller.clearLock
)
.post(
"/api/dev/:appId/revert",
authorized(permissions.BUILDER),
controller.revert
)
export = router

View file

@ -1,12 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/integration")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
router
.get("/api/integrations", authorized(BUILDER), controller.fetch)
.get("/api/integrations/:type", authorized(BUILDER), controller.find)
module.exports = router

View file

@ -0,0 +1,16 @@
import Router from "@koa/router"
import controller from "../controllers/integration"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router
.get("/api/integrations", authorized(permissions.BUILDER), controller.fetch)
.get(
"/api/integrations/:type",
authorized(permissions.BUILDER),
controller.find
)
export = router

View file

@ -1,16 +0,0 @@
const Router = require("@koa/router")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const controller = require("../controllers/layout")
const router = new Router()
router
.post("/api/layouts", authorized(BUILDER), controller.save)
.delete(
"/api/layouts/:layoutId/:layoutRev",
authorized(BUILDER),
controller.destroy
)
module.exports = router

View file

@ -0,0 +1,16 @@
import Router from "@koa/router"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import * as controller from "../controllers/layout"
const router: Router = new Router()
router
.post("/api/layouts", authorized(permissions.BUILDER), controller.save)
.delete(
"/api/layouts/:layoutId/:layoutRev",
authorized(permissions.BUILDER),
controller.destroy
)
export = router

View file

@ -1,38 +1,38 @@
const Router = require("@koa/router") import Router from "@koa/router"
const controller = require("../controllers/metadata") import * as controller from "../controllers/metadata"
const { import {
middleware: appInfoMiddleware, middleware as appInfoMiddleware,
AppType, AppType,
} = require("../../middleware/appInfo") } from "../../middleware/appInfo"
const authorized = require("../../middleware/authorized") import authorized from "../../middleware/authorized"
const { BUILDER } = require("@budibase/backend-core/permissions") import { permissions } from "@budibase/backend-core"
const router = new Router() const router: Router = new Router()
router router
.post( .post(
"/api/metadata/:type/:entityId", "/api/metadata/:type/:entityId",
authorized(BUILDER), authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }), appInfoMiddleware({ appType: AppType.DEV }),
controller.saveMetadata controller.saveMetadata
) )
.delete( .delete(
"/api/metadata/:type/:entityId", "/api/metadata/:type/:entityId",
authorized(BUILDER), authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }), appInfoMiddleware({ appType: AppType.DEV }),
controller.deleteMetadata controller.deleteMetadata
) )
.get( .get(
"/api/metadata/type", "/api/metadata/type",
authorized(BUILDER), authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }), appInfoMiddleware({ appType: AppType.DEV }),
controller.getTypes controller.getTypes
) )
.get( .get(
"/api/metadata/:type/:entityId", "/api/metadata/:type/:entityId",
authorized(BUILDER), authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }), appInfoMiddleware({ appType: AppType.DEV }),
controller.getMetadata controller.getMetadata
) )
module.exports = router export = router

View file

@ -1,14 +0,0 @@
const Router = require("@koa/router")
const migrationsController = require("../controllers/migrations")
const router = new Router()
const { internalApi } = require("@budibase/backend-core/auth")
router
.post("/api/migrations/run", internalApi, migrationsController.migrate)
.get(
"/api/migrations/definitions",
internalApi,
migrationsController.fetchDefinitions
)
module.exports = router

View file

@ -0,0 +1,14 @@
import Router from "@koa/router"
import * as migrationsController from "../controllers/migrations"
import { auth } from "@budibase/backend-core"
const router: Router = new Router()
router
.post("/api/migrations/run", auth.internalApi, migrationsController.migrate)
.get(
"/api/migrations/definitions",
auth.internalApi,
migrationsController.fetchDefinitions
)
export = router

View file

@ -1,33 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/permission")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const { permissionValidator } = require("./utils/validators")
const router = new Router()
router
.get("/api/permission/builtin", authorized(BUILDER), controller.fetchBuiltin)
.get("/api/permission/levels", authorized(BUILDER), controller.fetchLevels)
.get("/api/permission", authorized(BUILDER), controller.fetch)
.get(
"/api/permission/:resourceId",
authorized(BUILDER),
controller.getResourcePerms
)
// adding a specific role/level for the resource overrides the underlying access control
.post(
"/api/permission/:roleId/:resourceId/:level",
authorized(BUILDER),
permissionValidator(),
controller.addPermission
)
// deleting the level defaults it back the underlying access control for the resource
.delete(
"/api/permission/:roleId/:resourceId/:level",
authorized(BUILDER),
permissionValidator(),
controller.removePermission
)
module.exports = router

View file

@ -0,0 +1,41 @@
import Router from "@koa/router"
import * as controller from "../controllers/permission"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import { permissionValidator } from "./utils/validators"
const router: Router = new Router()
router
.get(
"/api/permission/builtin",
authorized(permissions.BUILDER),
controller.fetchBuiltin
)
.get(
"/api/permission/levels",
authorized(permissions.BUILDER),
controller.fetchLevels
)
.get("/api/permission", authorized(permissions.BUILDER), controller.fetch)
.get(
"/api/permission/:resourceId",
authorized(permissions.BUILDER),
controller.getResourcePerms
)
// adding a specific role/level for the resource overrides the underlying access control
.post(
"/api/permission/:roleId/:resourceId/:level",
authorized(permissions.BUILDER),
permissionValidator(),
controller.addPermission
)
// deleting the level defaults it back the underlying access control for the resource
.delete(
"/api/permission/:roleId/:resourceId/:level",
authorized(permissions.BUILDER),
permissionValidator(),
controller.removePermission
)
export = router

View file

@ -1,14 +1,22 @@
import Router from "@koa/router" import Router from "@koa/router"
import * as controller from "../controllers/plugin" import * as controller from "../controllers/plugin"
import authorized from "../../middleware/authorized" import authorized from "../../middleware/authorized"
import { BUILDER } from "@budibase/backend-core/permissions" import { permissions } from "@budibase/backend-core"
const router = new Router() const router: Router = new Router()
router router
.post("/api/plugin/upload", authorized(BUILDER), controller.upload) .post(
.post("/api/plugin", authorized(BUILDER), controller.create) "/api/plugin/upload",
.get("/api/plugin", authorized(BUILDER), controller.fetch) authorized(permissions.BUILDER),
.delete("/api/plugin/:pluginId", authorized(BUILDER), controller.destroy) controller.upload
)
.post("/api/plugin", authorized(permissions.BUILDER), controller.create)
.get("/api/plugin", authorized(permissions.BUILDER), controller.fetch)
.delete(
"/api/plugin/:pluginId",
authorized(permissions.BUILDER),
controller.destroy
)
export default router export default router

View file

@ -1,22 +1,19 @@
const Router = require("@koa/router") import Router from "@koa/router"
const queryController = require("../controllers/query") import * as queryController from "../controllers/query"
const authorized = require("../../middleware/authorized") import authorized from "../../middleware/authorized"
const { import { permissions } from "@budibase/backend-core"
PermissionLevel, import {
PermissionType,
BUILDER,
} = require("@budibase/backend-core/permissions")
const {
bodyResource, bodyResource,
bodySubResource, bodySubResource,
paramResource, paramResource,
} = require("../../middleware/resourceId") } from "../../middleware/resourceId"
const { import {
generateQueryPreviewValidation, generateQueryPreviewValidation,
generateQueryValidation, generateQueryValidation,
} = require("../controllers/query/validation") } from "../controllers/query/validation"
const { BUILDER, PermissionType, PermissionLevel } = permissions
const router = new Router() const router: Router = new Router()
router router
.get("/api/queries", authorized(BUILDER), queryController.fetch) .get("/api/queries", authorized(BUILDER), queryController.fetch)
@ -48,17 +45,17 @@ router
authorized(PermissionType.QUERY, PermissionLevel.WRITE), authorized(PermissionType.QUERY, PermissionLevel.WRITE),
queryController.executeV1 queryController.executeV1
) )
.post(
"/api/v2/queries/:queryId",
paramResource("queryId"),
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
queryController.executeV2
)
.delete( .delete(
"/api/queries/:queryId/:revId", "/api/queries/:queryId/:revId",
paramResource("queryId"), paramResource("queryId"),
authorized(BUILDER), authorized(BUILDER),
queryController.destroy queryController.destroy
) )
.post(
"/api/v2/queries/:queryId",
paramResource("queryId"),
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
queryController.executeV2 as any
)
module.exports = router export = router

View file

@ -1,15 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/role")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const { roleValidator } = require("./utils/validators")
const router = new Router()
router
.post("/api/roles", authorized(BUILDER), roleValidator(), controller.save)
.get("/api/roles", authorized(BUILDER), controller.fetch)
.get("/api/roles/:roleId", authorized(BUILDER), controller.find)
.delete("/api/roles/:roleId/:rev", authorized(BUILDER), controller.destroy)
module.exports = router

View file

@ -0,0 +1,24 @@
import Router from "@koa/router"
import * as controller from "../controllers/role"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import { roleValidator } from "./utils/validators"
const router: Router = new Router()
router
.post(
"/api/roles",
authorized(permissions.BUILDER),
roleValidator(),
controller.save
)
.get("/api/roles", authorized(permissions.BUILDER), controller.fetch)
.get("/api/roles/:roleId", authorized(permissions.BUILDER), controller.find)
.delete(
"/api/roles/:roleId/:rev",
authorized(permissions.BUILDER),
controller.destroy
)
export = router

View file

@ -1,14 +0,0 @@
const Router = require("@koa/router")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const controller = require("../controllers/routing")
const router = new Router()
router
// gets correct structure for user role
.get("/api/routing/client", controller.clientFetch)
// gets the full structure, not just the correct screen ID for user role
.get("/api/routing", authorized(BUILDER), controller.fetch)
module.exports = router

View file

@ -0,0 +1,14 @@
import Router from "@koa/router"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import * as controller from "../controllers/routing"
const router: Router = new Router()
router
// gets correct structure for user role
.get("/api/routing/client", controller.clientFetch)
// gets the full structure, not just the correct screen ID for user role
.get("/api/routing", authorized(permissions.BUILDER), controller.fetch)
export = router

View file

@ -2,13 +2,11 @@ import Router from "@koa/router"
import * as rowController from "../controllers/row" import * as rowController from "../controllers/row"
import authorized from "../../middleware/authorized" import authorized from "../../middleware/authorized"
import { paramResource, paramSubResource } from "../../middleware/resourceId" import { paramResource, paramSubResource } from "../../middleware/resourceId"
const { import { permissions } from "@budibase/backend-core"
PermissionLevel,
PermissionType,
} = require("@budibase/backend-core/permissions")
const { internalSearchValidator } = require("./utils/validators") const { internalSearchValidator } = require("./utils/validators")
const { PermissionType, PermissionLevel } = permissions
const router = new Router() const router: Router = new Router()
router router
/** /**

View file

@ -1,18 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/screen")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const { screenValidator } = require("./utils/validators")
const router = new Router()
router
.get("/api/screens", authorized(BUILDER), controller.fetch)
.post("/api/screens", authorized(BUILDER), screenValidator(), controller.save)
.delete(
"/api/screens/:screenId/:screenRev",
authorized(BUILDER),
controller.destroy
)
module.exports = router

View file

@ -0,0 +1,23 @@
import Router from "@koa/router"
import * as controller from "../controllers/screen"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import { screenValidator } from "./utils/validators"
const router: Router = new Router()
router
.get("/api/screens", authorized(permissions.BUILDER), controller.fetch)
.post(
"/api/screens",
authorized(permissions.BUILDER),
screenValidator(),
controller.save
)
.delete(
"/api/screens/:screenId/:screenRev",
authorized(permissions.BUILDER),
controller.destroy
)
export = router

View file

@ -1,10 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/script")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
router.post("/api/script", authorized(BUILDER), controller.save)
module.exports = router

View file

@ -0,0 +1,10 @@
import Router from "@koa/router"
import * as controller from "../controllers/script"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router.post("/api/script", authorized(permissions.BUILDER), controller.save)
export = router

View file

@ -2,15 +2,12 @@ import Router from "@koa/router"
import * as controller from "../controllers/static" import * as controller from "../controllers/static"
import { budibaseTempDir } from "../../utilities/budibaseDir" import { budibaseTempDir } from "../../utilities/budibaseDir"
import authorized from "../../middleware/authorized" import authorized from "../../middleware/authorized"
import { import { permissions } from "@budibase/backend-core"
BUILDER,
PermissionType,
PermissionLevel,
} from "@budibase/backend-core/permissions"
import * as env from "../../environment" import * as env from "../../environment"
import { paramResource } from "../../middleware/resourceId" import { paramResource } from "../../middleware/resourceId"
const { BUILDER, PermissionType, PermissionLevel } = permissions
const router = new Router() const router: Router = new Router()
/* istanbul ignore next */ /* istanbul ignore next */
router.param("file", async (file: any, ctx: any, next: any) => { router.param("file", async (file: any, ctx: any, next: any) => {
@ -65,4 +62,4 @@ router
controller.getSignedUploadURL controller.getSignedUploadURL
) )
export default router export = router

View file

@ -1,15 +1,12 @@
const Router = require("@koa/router") import Router from "@koa/router"
const tableController = require("../controllers/table") import * as tableController from "../controllers/table"
const authorized = require("../../middleware/authorized") import authorized from "../../middleware/authorized"
const { paramResource, bodyResource } = require("../../middleware/resourceId") import { paramResource, bodyResource } from "../../middleware/resourceId"
const { import { permissions } from "@budibase/backend-core"
BUILDER, import { tableValidator } from "./utils/validators"
PermissionLevel, const { BUILDER, PermissionLevel, PermissionType } = permissions
PermissionType,
} = require("@budibase/backend-core/permissions")
const { tableValidator } = require("./utils/validators")
const router = new Router() const router: Router = new Router()
router router
/** /**
@ -193,4 +190,4 @@ router
tableController.bulkImport tableController.bulkImport
) )
module.exports = router export = router

View file

@ -1,16 +0,0 @@
const Router = require("@koa/router")
const controller = require("../controllers/templates")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
const router = new Router()
router
.get("/api/templates", authorized(BUILDER), controller.fetch)
.get(
"/api/templates/:type/:name",
authorized(BUILDER),
controller.downloadTemplate
)
module.exports = router

View file

@ -0,0 +1,16 @@
import Router from "@koa/router"
import * as controller from "../controllers/templates"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router
.get("/api/templates", authorized(permissions.BUILDER), controller.fetch)
.get(
"/api/templates/:type/:name",
authorized(permissions.BUILDER),
controller.downloadTemplate
)
export = router

View file

@ -1,4 +1,12 @@
jest.mock("../../../utilities/fileSystem/utilities") jest.mock("@budibase/backend-core", () => {
const core = jest.requireActual("@budibase/backend-core")
return {
...core,
objectStore: {
budibaseTempDir: core.objectStore.budibaseTempDir,
},
}
})
const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")

View file

@ -1,6 +1,7 @@
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const { roles } = require("@budibase/backend-core")
const setup = require("./utilities") const setup = require("./utilities")
const { basicRow } = setup.structures const { basicRow } = setup.structures
const { BUILTIN_ROLE_IDS } = roles
const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC
const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC

View file

@ -1,10 +1,8 @@
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const { roles, events, permissions } = require("@budibase/backend-core")
const {
BuiltinPermissionID,
} = require("@budibase/backend-core/permissions")
const setup = require("./utilities") const setup = require("./utilities")
const { basicRole } = setup.structures const { basicRole } = setup.structures
const { events } = require("@budibase/backend-core") const { BUILTIN_ROLE_IDS } = roles
const { BuiltinPermissionID } = permissions
describe("/roles", () => { describe("/roles", () => {
let request = setup.getRequest() let request = setup.getRequest()

View file

@ -1,7 +1,8 @@
const setup = require("./utilities") const setup = require("./utilities")
const { basicScreen } = setup.structures const { basicScreen } = setup.structures
const { checkBuilderEndpoint, runInProd } = require("./utilities/TestFunctions") const { checkBuilderEndpoint, runInProd } = require("./utilities/TestFunctions")
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const { roles } = require("@budibase/backend-core")
const { BUILTIN_ROLE_IDS } = roles
const route = "/test" const route = "/test"

View file

@ -1,8 +1,7 @@
const { outputProcessing } = require("../../../utilities/rowProcessor") const { outputProcessing } = require("../../../utilities/rowProcessor")
const setup = require("./utilities") const setup = require("./utilities")
const { basicRow } = setup.structures const { basicRow } = setup.structures
const { doInAppContext } = require("@budibase/backend-core/context") const { context, tenancy } = require("@budibase/backend-core")
const { doInTenant } = require("@budibase/backend-core/tenancy")
const { const {
quotas, quotas,
} = require("@budibase/pro") } = require("@budibase/pro")
@ -447,7 +446,7 @@ describe("/rows", () => {
describe("fetchEnrichedRows", () => { describe("fetchEnrichedRows", () => {
it("should allow enriching some linked rows", async () => { it("should allow enriching some linked rows", async () => {
const { table, firstRow, secondRow } = await doInTenant( const { table, firstRow, secondRow } = await tenancy.doInTenant(
setup.structures.TENANT_ID, setup.structures.TENANT_ID,
async () => { async () => {
const table = await config.createLinkedTable() const table = await config.createLinkedTable()
@ -507,7 +506,7 @@ describe("/rows", () => {
}) })
// the environment needs configured for this // the environment needs configured for this
await setup.switchToSelfHosted(async () => { await setup.switchToSelfHosted(async () => {
doInAppContext(config.getAppId(), async () => { context.doInAppContext(config.getAppId(), async () => {
const enriched = await outputProcessing(table, [row]) const enriched = await outputProcessing(table, [row])
expect(enriched[0].attachment[0].url).toBe( expect(enriched[0].attachment[0].url).toBe(
`/prod-budi-app-assets/${config.getAppId()}/attachments/test/thing.csv` `/prod-budi-app-assets/${config.getAppId()}/attachments/test/thing.csv`

View file

@ -1,6 +1,7 @@
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const { roles } = require("@budibase/backend-core")
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions") const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
const setup = require("./utilities") const setup = require("./utilities")
const { BUILTIN_ROLE_IDS } = roles
jest.mock("../../../utilities/workerRequests", () => ({ jest.mock("../../../utilities/workerRequests", () => ({
getGlobalUsers: jest.fn(() => { getGlobalUsers: jest.fn(() => {

View file

@ -1,10 +1,8 @@
import * as rowController from "../../../controllers/row" import * as rowController from "../../../controllers/row"
import * as appController from "../../../controllers/application" import * as appController from "../../../controllers/application"
import { AppStatus } from "../../../../db/utils" import { AppStatus } from "../../../../db/utils"
import { BUILTIN_ROLE_IDS } from "@budibase/backend-core/roles" import { roles, tenancy, context } from "@budibase/backend-core"
import { doInTenant } from "@budibase/backend-core/tenancy"
import { TENANT_ID } from "../../../../tests/utilities/structures" import { TENANT_ID } from "../../../../tests/utilities/structures"
import { getAppDB, doInAppContext } from "@budibase/backend-core/context"
import * as env from "../../../../environment" import * as env from "../../../../environment"
class Request { class Request {
@ -21,7 +19,7 @@ class Request {
} }
function runRequest(appId: any, controlFunc: any, request?: any) { function runRequest(appId: any, controlFunc: any, request?: any) {
return doInAppContext(appId, async () => { return context.doInAppContext(appId, async () => {
return controlFunc(request) return controlFunc(request)
}) })
} }
@ -33,7 +31,7 @@ export const getAllTableRows = async (config: any) => {
} }
export const clearAllApps = async (tenantId = TENANT_ID) => { export const clearAllApps = async (tenantId = TENANT_ID) => {
await doInTenant(tenantId, async () => { await tenancy.doInTenant(tenantId, async () => {
const req: any = { query: { status: AppStatus.DEV }, user: { tenantId } } const req: any = { query: { status: AppStatus.DEV }, user: { tenantId } }
await appController.fetch(req) await appController.fetch(req)
const apps = req.body const apps = req.body
@ -51,7 +49,7 @@ export const clearAllApps = async (tenantId = TENANT_ID) => {
export const clearAllAutomations = async (config: any) => { export const clearAllAutomations = async (config: any) => {
const automations = await config.getAllAutomations() const automations = await config.getAllAutomations()
for (let auto of automations) { for (let auto of automations) {
await doInAppContext(config.appId, async () => { await context.doInAppContext(config.appId, async () => {
await config.deleteAutomation(auto) await config.deleteAutomation(auto)
}) })
} }
@ -110,7 +108,7 @@ export const checkPermissionsEndpoint = async ({
.expect(200) .expect(200)
let failHeader let failHeader
if (failRole === BUILTIN_ROLE_IDS.PUBLIC) { if (failRole === roles.BUILTIN_ROLE_IDS.PUBLIC) {
failHeader = config.publicHeaders({ prodApp: true }) failHeader = config.publicHeaders({ prodApp: true })
} else { } else {
failHeader = await config.login({ failHeader = await config.login({
@ -127,7 +125,7 @@ export const checkPermissionsEndpoint = async ({
} }
export const getDB = () => { export const getDB = () => {
return getAppDB() return context.getAppDB()
} }
export const testAutomation = async (config: any, automation: any) => { export const testAutomation = async (config: any, automation: any) => {

View file

@ -1,12 +1,10 @@
const Router = require("@koa/router") import Router from "@koa/router"
const controller = require("../controllers/user") import * as controller from "../controllers/user"
const authorized = require("../../middleware/authorized") import authorized from "../../middleware/authorized"
const { import { permissions } from "@budibase/backend-core"
PermissionLevel, const { PermissionType, PermissionLevel } = permissions
PermissionType,
} = require("@budibase/backend-core/permissions")
const router = new Router() const router: Router = new Router()
router router
.get( .get(
@ -50,4 +48,4 @@ router
controller.getFlags controller.getFlags
) )
module.exports = router export = router

View file

@ -1,20 +1,16 @@
const { joiValidator } = require("@budibase/backend-core/auth") import { auth, permissions } from "@budibase/backend-core"
const { DataSourceOperation } = require("../../../constants") import { DataSourceOperation } from "../../../constants"
const { import { WebhookActionType } from "@budibase/types"
BuiltinPermissionID, import Joi from "joi"
PermissionLevel,
} = require("@budibase/backend-core/permissions")
const { WebhookActionType } = require("@budibase/types")
const Joi = require("joi")
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("") const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
const OPTIONAL_NUMBER = Joi.number().optional().allow(null) const OPTIONAL_NUMBER = Joi.number().optional().allow(null)
const OPTIONAL_BOOLEAN = Joi.boolean().optional().allow(null) const OPTIONAL_BOOLEAN = Joi.boolean().optional().allow(null)
const APP_NAME_REGEX = /^[\w\s]+$/ const APP_NAME_REGEX = /^[\w\s]+$/
exports.tableValidator = () => { export function tableValidator() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
_id: OPTIONAL_STRING, _id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING, _rev: OPTIONAL_STRING,
type: OPTIONAL_STRING.valid("table", "internal", "external"), type: OPTIONAL_STRING.valid("table", "internal", "external"),
@ -26,16 +22,16 @@ exports.tableValidator = () => {
}).unknown(true)) }).unknown(true))
} }
exports.nameValidator = () => { export function nameValidator() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
name: OPTIONAL_STRING, name: OPTIONAL_STRING,
})) }))
} }
exports.datasourceValidator = () => { export function datasourceValidator() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
_id: Joi.string(), _id: Joi.string(),
_rev: Joi.string(), _rev: Joi.string(),
type: OPTIONAL_STRING.allow("datasource_plus"), type: OPTIONAL_STRING.allow("datasource_plus"),
@ -64,9 +60,9 @@ function filterObject() {
}).unknown(true) }).unknown(true)
} }
exports.internalSearchValidator = () => { export function internalSearchValidator() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
tableId: OPTIONAL_STRING, tableId: OPTIONAL_STRING,
query: filterObject(), query: filterObject(),
limit: OPTIONAL_NUMBER, limit: OPTIONAL_NUMBER,
@ -78,8 +74,8 @@ exports.internalSearchValidator = () => {
})) }))
} }
exports.externalSearchValidator = () => { export function externalSearchValidator() {
return joiValidator.body( return auth.joiValidator.body(
Joi.object({ Joi.object({
query: filterObject(), query: filterObject(),
paginate: Joi.boolean().optional(), paginate: Joi.boolean().optional(),
@ -96,9 +92,9 @@ exports.externalSearchValidator = () => {
) )
} }
exports.datasourceQueryValidator = () => { export function datasourceQueryValidator() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
endpoint: Joi.object({ endpoint: Joi.object({
datasourceId: Joi.string().required(), datasourceId: Joi.string().required(),
operation: Joi.string().required().valid(...Object.values(DataSourceOperation)), operation: Joi.string().required().valid(...Object.values(DataSourceOperation)),
@ -117,9 +113,9 @@ exports.datasourceQueryValidator = () => {
})) }))
} }
exports.webhookValidator = () => { export function webhookValidator() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
live: Joi.bool(), live: Joi.bool(),
_id: OPTIONAL_STRING, _id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING, _rev: OPTIONAL_STRING,
@ -132,15 +128,15 @@ exports.webhookValidator = () => {
}).unknown(true)) }).unknown(true))
} }
exports.roleValidator = () => { export function roleValidator() {
const permLevelArray = Object.values(PermissionLevel) const permLevelArray = Object.values(permissions.PermissionLevel)
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
_id: OPTIONAL_STRING, _id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING, _rev: OPTIONAL_STRING,
name: Joi.string().required(), name: Joi.string().required(),
// this is the base permission ID (for now a built in) // this is the base permission ID (for now a built in)
permissionId: Joi.string().valid(...Object.values(BuiltinPermissionID)).required(), permissionId: Joi.string().valid(...Object.values(permissions.BuiltinPermissionID)).required(),
permissions: Joi.object() permissions: Joi.object()
.pattern(/.*/, [Joi.string().valid(...permLevelArray)]) .pattern(/.*/, [Joi.string().valid(...permLevelArray)])
.optional(), .optional(),
@ -148,19 +144,19 @@ exports.roleValidator = () => {
}).unknown(true)) }).unknown(true))
} }
exports.permissionValidator = () => { export function permissionValidator() {
const permLevelArray = Object.values(PermissionLevel) const permLevelArray = Object.values(permissions.PermissionLevel)
// prettier-ignore // prettier-ignore
return joiValidator.params(Joi.object({ return auth.joiValidator.params(Joi.object({
level: Joi.string().valid(...permLevelArray).required(), level: Joi.string().valid(...permLevelArray).required(),
resourceId: Joi.string(), resourceId: Joi.string(),
roleId: Joi.string(), roleId: Joi.string(),
}).unknown(true)) }).unknown(true))
} }
exports.screenValidator = () => { export function screenValidator() {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
name: Joi.string().required(), name: Joi.string().required(),
showNavigation: OPTIONAL_BOOLEAN, showNavigation: OPTIONAL_BOOLEAN,
width: OPTIONAL_STRING, width: OPTIONAL_STRING,
@ -181,7 +177,7 @@ exports.screenValidator = () => {
}).unknown(true)) }).unknown(true))
} }
function generateStepSchema(allowStepTypes) { function generateStepSchema(allowStepTypes: string[]) {
// prettier-ignore // prettier-ignore
return Joi.object({ return Joi.object({
stepId: Joi.string().required(), stepId: Joi.string().required(),
@ -196,9 +192,9 @@ function generateStepSchema(allowStepTypes) {
}).unknown(true) }).unknown(true)
} }
exports.automationValidator = (existing = false) => { export function automationValidator(existing = false) {
// prettier-ignore // prettier-ignore
return joiValidator.body(Joi.object({ return auth.joiValidator.body(Joi.object({
_id: existing ? Joi.string().required() : OPTIONAL_STRING, _id: existing ? Joi.string().required() : OPTIONAL_STRING,
_rev: existing ? Joi.string().required() : OPTIONAL_STRING, _rev: existing ? Joi.string().required() : OPTIONAL_STRING,
name: Joi.string().required(), name: Joi.string().required(),
@ -210,9 +206,9 @@ exports.automationValidator = (existing = false) => {
}).unknown(true)) }).unknown(true))
} }
exports.applicationValidator = (opts = { isCreate: true }) => { export function applicationValidator(opts = { isCreate: true }) {
// prettier-ignore // prettier-ignore
const base = { const base: any = {
_id: OPTIONAL_STRING, _id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING, _rev: OPTIONAL_STRING,
url: OPTIONAL_STRING, url: OPTIONAL_STRING,
@ -230,7 +226,7 @@ exports.applicationValidator = (opts = { isCreate: true }) => {
base.name = appNameValidator.optional() base.name = appNameValidator.optional()
} }
return joiValidator.body( return auth.joiValidator.body(
Joi.object({ Joi.object({
_id: OPTIONAL_STRING, _id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING, _rev: OPTIONAL_STRING,

View file

@ -1,31 +0,0 @@
const Router = require("@koa/router")
const viewController = require("../controllers/view")
const rowController = require("../controllers/row")
const authorized = require("../../middleware/authorized")
const { paramResource } = require("../../middleware/resourceId")
const {
BUILDER,
PermissionType,
PermissionLevel,
} = require("@budibase/backend-core/permissions")
const router = new Router()
router
.get("/api/views/export", authorized(BUILDER), viewController.exportView)
.get(
"/api/views/:viewName",
paramResource("viewName"),
authorized(PermissionType.VIEW, PermissionLevel.READ),
rowController.fetchView
)
.get("/api/views", authorized(BUILDER), viewController.fetch)
.delete(
"/api/views/:viewName",
paramResource("viewName"),
authorized(BUILDER),
viewController.destroy
)
.post("/api/views", authorized(BUILDER), viewController.save)
module.exports = router

View file

@ -0,0 +1,34 @@
import Router from "@koa/router"
import * as viewController from "../controllers/view"
import * as rowController from "../controllers/row"
import authorized from "../../middleware/authorized"
import { paramResource } from "../../middleware/resourceId"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router
.get(
"/api/views/export",
authorized(permissions.BUILDER),
viewController.exportView
)
.get(
"/api/views/:viewName",
paramResource("viewName"),
authorized(
permissions.PermissionType.VIEW,
permissions.PermissionLevel.READ
),
rowController.fetchView
)
.get("/api/views", authorized(permissions.BUILDER), viewController.fetch)
.delete(
"/api/views/:viewName",
paramResource("viewName"),
authorized(permissions.BUILDER),
viewController.destroy
)
.post("/api/views", authorized(permissions.BUILDER), viewController.save)
export = router

View file

@ -5,7 +5,7 @@ import { permissions } from "@budibase/backend-core"
import { webhookValidator } from "./utils/validators" import { webhookValidator } from "./utils/validators"
const BUILDER = permissions.BUILDER const BUILDER = permissions.BUILDER
const router = new Router() const router: Router = new Router()
router router
.get("/api/webhooks", authorized(BUILDER), controller.fetch) .get("/api/webhooks", authorized(BUILDER), controller.fetch)
@ -24,4 +24,4 @@ router
// this shouldn't have authorisation, right now its always public // this shouldn't have authorisation, right now its always public
.post("/api/webhooks/trigger/:instance/:id", controller.trigger) .post("/api/webhooks/trigger/:instance/:id", controller.trigger)
export default router export = router

View file

@ -19,10 +19,9 @@ const http = require("http")
const api = require("./api") const api = require("./api")
const automations = require("./automations/index") const automations = require("./automations/index")
const Sentry = require("@sentry/node") const Sentry = require("@sentry/node")
const { logAlert } = require("@budibase/backend-core/logging")
const { Thread } = require("./threads") const { Thread } = require("./threads")
import redis from "./utilities/redis" import * as redis from "./utilities/redis"
import { events } from "@budibase/backend-core" import { events, logging } from "@budibase/backend-core"
import { initialise as initialiseWebsockets } from "./websocket" import { initialise as initialiseWebsockets } from "./websocket"
import { startup } from "./startup" import { startup } from "./startup"
@ -93,7 +92,7 @@ process.on("uncaughtException", err => {
return return
} }
errCode = -1 errCode = -1
logAlert("Uncaught exception.", err) logging.logAlert("Uncaught exception.", err)
shutdown() shutdown()
}) })

View file

@ -8,8 +8,7 @@ const { automationQueue } = require("./bullboard")
const { checkTestFlag } = require("../utilities/redis") const { checkTestFlag } = require("../utilities/redis")
const utils = require("./utils") const utils = require("./utils")
const env = require("../environment") const env = require("../environment")
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context") const { context, db: dbCore } = require("@budibase/backend-core")
const { getAllApps } = require("@budibase/backend-core/db")
const TRIGGER_DEFINITIONS = definitions const TRIGGER_DEFINITIONS = definitions
const JOB_OPTS = { const JOB_OPTS = {
@ -18,7 +17,7 @@ const JOB_OPTS = {
} }
async function getAllAutomations() { async function getAllAutomations() {
const db = getAppDB() const db = context.getAppDB()
let automations = await db.allDocs( let automations = await db.allDocs(
getAutomationParams(null, { include_docs: true }) getAutomationParams(null, { include_docs: true })
) )
@ -30,7 +29,7 @@ async function queueRelevantRowAutomations(event, eventType) {
throw `No appId specified for ${eventType} - check event emitters.` throw `No appId specified for ${eventType} - check event emitters.`
} }
doInAppContext(event.appId, async () => { context.doInAppContext(event.appId, async () => {
let automations = await getAllAutomations() let automations = await getAllAutomations()
// filter down to the correct event type // filter down to the correct event type
@ -122,9 +121,9 @@ exports.rebootTrigger = async () => {
} }
// iterate through all production apps, find the reboot crons // iterate through all production apps, find the reboot crons
// and trigger events for them // and trigger events for them
const appIds = await getAllApps({ dev: false, idsOnly: true }) const appIds = await dbCore.getAllApps({ dev: false, idsOnly: true })
for (let prodAppId of appIds) { for (let prodAppId of appIds) {
await doInAppContext(prodAppId, async () => { await context.doInAppContext(prodAppId, async () => {
let automations = await getAllAutomations() let automations = await getAllAutomations()
let rebootEvents = [] let rebootEvents = []
for (let automation of automations) { for (let automation of automations) {

View file

@ -1,6 +1,4 @@
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const { objectStore, roles, constants } = require("@budibase/backend-core")
const { UserStatus } = require("@budibase/backend-core/constants")
const { objectStore } = require("@budibase/backend-core")
const FilterTypes = { const FilterTypes = {
STRING: "string", STRING: "string",
@ -139,7 +137,7 @@ exports.USERS_TABLE_SCHEMA = {
constraints: { constraints: {
type: exports.FieldTypes.STRING, type: exports.FieldTypes.STRING,
presence: false, presence: false,
inclusion: Object.values(BUILTIN_ROLE_IDS), inclusion: Object.values(roles.BUILTIN_ROLE_IDS),
}, },
}, },
status: { status: {
@ -149,7 +147,7 @@ exports.USERS_TABLE_SCHEMA = {
constraints: { constraints: {
type: exports.FieldTypes.STRING, type: exports.FieldTypes.STRING,
presence: false, presence: false,
inclusion: Object.values(UserStatus), inclusion: Object.values(constants.UserStatus),
}, },
}, },
}, },

Some files were not shown because too many files have changed in this diff Show more