1
0
Fork 0
mirror of synced 2024-06-02 18:44:54 +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 2296ae3c39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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.
*/
export function getUserMetadataParams(userId?: string, otherProps = {}) {
export function getUserMetadataParams(userId?: string | null, otherProps = {}) {
return getRowParams(InternalTable.USER_METADATA, userId, otherProps)
}
@ -244,7 +244,7 @@ export function getTemplateParams(
* Generates a new role ID.
* @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()}`
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,15 +1,20 @@
const newid = require("../../../db/newid")
const { getAppId } = require("@budibase/backend-core/context")
import newid from "../../../db/newid"
import { context } from "@budibase/backend-core"
/**
* 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) {
this._id = id || newid()
}
setVerification(verification) {
setVerification(verification: any) {
if (!verification) {
return
}
@ -20,14 +25,14 @@ class Deployment {
return this.verification
}
setStatus(status, err = null) {
setStatus(status: string, err?: any) {
this.status = status
if (err) {
this.err = err
}
}
fromJSON(json) {
fromJSON(json: any) {
if (json.verification) {
this.setVerification(json.verification)
}
@ -37,9 +42,9 @@ class Deployment {
}
getJSON() {
const obj = {
const obj: any = {
_id: this._id,
appId: getAppId(),
appId: context.getAppId(),
status: this.status,
}
if (this.err) {
@ -51,5 +56,3 @@ class Deployment {
return obj
}
}
module.exports = Deployment

View file

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

View file

@ -1,15 +1,16 @@
const { MetadataTypes } = require("../../constants")
const { generateMetadataID } = require("../../db/utils")
const { saveEntityMetadata, deleteEntityMetadata } = require("../../utilities")
const { getAppDB } = require("@budibase/backend-core/context")
import { MetadataTypes } from "../../constants"
import { generateMetadataID } from "../../db/utils"
import { saveEntityMetadata, deleteEntityMetadata } from "../../utilities"
import { context } from "@budibase/backend-core"
import { BBContext } from "@budibase/types"
exports.getTypes = async ctx => {
export async function getTypes(ctx: BBContext) {
ctx.body = {
types: MetadataTypes,
}
}
exports.saveMetadata = async ctx => {
export async function saveMetadata(ctx: BBContext) {
const { type, entityId } = ctx.params
if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) {
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)
}
exports.deleteMetadata = async ctx => {
export async function deleteMetadata(ctx: BBContext) {
const { type, entityId } = ctx.params
await deleteEntityMetadata(type, entityId)
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 db = getAppDB()
const db = context.getAppDB()
const id = generateMetadataID(type, entityId)
try {
ctx.body = await db.get(id)
} catch (err) {
} catch (err: any) {
if (err.status === 404) {
ctx.body = {}
} else {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,13 +1,14 @@
const { getRowParams } = require("../../../db/utils")
const {
import { getRowParams } from "../../../db/utils"
import {
outputProcessing,
processAutoColumn,
processFormulas,
} = require("../../../utilities/rowProcessor")
const { FieldTypes, FormulaTypes } = require("../../../constants")
} from "../../../utilities/rowProcessor"
import { FieldTypes, FormulaTypes } from "../../../constants"
import { context } from "@budibase/backend-core"
import { Table, Row } from "@budibase/types"
const { isEqual } = require("lodash")
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
@ -15,22 +16,22 @@ const { getAppDB } = require("@budibase/backend-core/context")
* updated.
* NOTE: this will only for affect static formulas.
*/
exports.updateRelatedFormula = async (table, enrichedRows) => {
const db = getAppDB()
exports.updateRelatedFormula = async (table: Table, enrichedRows: Row[]) => {
const db = context.getAppDB()
// no formula to update, we're done
if (!table.relatedFormula) {
return
}
let promises = []
let promises: Promise<any>[] = []
for (let enrichedRow of Array.isArray(enrichedRows)
? enrichedRows
: [enrichedRows]) {
// the related rows by tableId
let relatedRows = {}
let relatedRows: Record<string, Row[]> = {}
for (let [key, field] of Object.entries(enrichedRow)) {
const columnDefinition = table.schema[key]
if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
const relatedTableId = columnDefinition.tableId
const relatedTableId = columnDefinition.tableId!
if (!relatedRows[relatedTableId]) {
relatedRows[relatedTableId] = []
}
@ -38,7 +39,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
}
}
for (let tableId of table.relatedFormula) {
let relatedTable
let relatedTable: Table
try {
// no rows to update, skip
if (!relatedRows[tableId] || relatedRows[tableId].length === 0) {
@ -48,7 +49,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
} catch (err) {
// 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
if (
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
promises = promises.concat(
relatedRows[tableId].map(related =>
exports.finaliseRow(relatedTable, related, {
finaliseRow(relatedTable, related, {
updateFormula: false,
})
)
@ -70,8 +71,8 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
await Promise.all(promises)
}
exports.updateAllFormulasInTable = async table => {
const db = getAppDB()
export async function updateAllFormulasInTable(table: Table) {
const db = context.getAppDB()
// start by getting the raw rows (which will be written back to DB after update)
let rows = (
await db.allDocs(
@ -88,7 +89,9 @@ exports.updateAllFormulasInTable = async table => {
const updatedRows = []
for (let row of rows) {
// 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) {
const processed = processFormulas(table, cloneDeep(row), {
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
* expects the row to be totally enriched/contain all relationships.
*/
exports.finaliseRow = async (
table,
row,
{ oldTable, updateFormula } = { updateFormula: true }
) => {
const db = getAppDB()
export async function finaliseRow(
table: Table,
row: Row,
{ oldTable, updateFormula }: { oldTable?: Table; updateFormula: boolean } = {
updateFormula: true,
}
) {
const db = context.getAppDB()
row.type = "row"
// process the row before return, to include relationships
let enrichedRow = await outputProcessing(table, cloneDeep(row), {
@ -131,7 +136,7 @@ exports.finaliseRow = async (
if (oldTable && !isEqual(oldTable, table)) {
try {
await db.put(table)
} catch (err) {
} catch (err: any) {
if (err.status === 409) {
const updatedTable = await db.get(table._id)
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 { 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, {
parse: function (value) {
parse: function (value: string) {
return new Date(value).getTime()
},
// Input is a unix timestamp
format: function (value) {
format: function (value: string) {
return new Date(value).toISOString()
},
})
exports.removeKeyNumbering = removeKeyNumbering
exports.getDatasourceAndQuery = async json => {
export async function getDatasourceAndQuery(json: any) {
const datasourceId = json.endpoint.datasourceId
const db = getAppDB()
const db = context.getAppDB()
const datasource = await db.get(datasourceId)
return makeExternalQuery(datasource, json)
}
exports.findRow = async (ctx, tableId, rowId) => {
const db = getAppDB()
export async function findRow(ctx: BBContext, tableId: string, rowId: string) {
const db = context.getAppDB()
let row
// TODO remove special user case in future
if (tableId === InternalTables.USER_METADATA) {
@ -45,12 +44,20 @@ exports.findRow = async (ctx, tableId, rowId) => {
return row
}
exports.validate = async ({ tableId, row, table }) => {
export async function validate({
tableId,
row,
table,
}: {
tableId?: string
row: Row
table: Table
}) {
if (!table) {
const db = getAppDB()
const db = context.getAppDB()
table = await db.get(tableId)
}
const errors = {}
const errors: any = {}
for (let fieldName of Object.keys(table.schema)) {
const constraints = cloneDeep(table.schema[fieldName].constraints)
const type = table.schema[fieldName].type
@ -70,7 +77,7 @@ exports.validate = async ({ tableId, row, table }) => {
if (!Array.isArray(row[fieldName])) {
row[fieldName] = row[fieldName].split(",")
}
row[fieldName].map(val => {
row[fieldName].map((val: any) => {
if (
!constraints.inclusion.includes(val) &&
constraints.inclusion.length !== 0

View file

@ -7,9 +7,9 @@ import {
roles,
} from "@budibase/backend-core"
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 screens = (
@ -20,13 +20,17 @@ exports.fetch = async (ctx: any) => {
)
).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(
screens,
ctx.user.role._id
roleId
)
}
exports.save = async (ctx: any) => {
export async function save(ctx: BBContext) {
const db = context.getAppDB()
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 id = ctx.params.screenId
const screen = await db.get(id)
@ -106,7 +110,7 @@ exports.destroy = async (ctx: any) => {
ctx.status = 200
}
const findPlugins = (component: ScreenProps, foundPlugins: string[]) => {
function findPlugins(component: ScreenProps, foundPlugins: string[]) {
if (!component) {
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 { attachmentsRelativeURL } = require("../../../utilities")
const { DocumentType } = require("../../../db/utils")
const { getAppDB, getAppId } = require("@budibase/backend-core/context")
const { setCookie, clearCookie } = require("@budibase/backend-core/utils")
const { context, objectStore, utils } = require("@budibase/backend-core")
const AWS = require("aws-sdk")
const fs = require("fs")
const {
downloadTarballDirect,
} = require("../../../utilities/fileSystem/utilities")
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
const response = await upload({
@ -48,7 +44,7 @@ export const toggleBetaUiFeature = async function (ctx: any) {
const cookieName = `beta:${ctx.params.feature}`
if (ctx.cookies.get(cookieName)) {
clearCookie(ctx, cookieName)
utils.clearCookie(ctx, cookieName)
ctx.body = {
message: `${ctx.params.feature} disabled`,
}
@ -61,11 +57,11 @@ export const toggleBetaUiFeature = async function (ctx: any) {
if (!fs.existsSync(builderPath)) {
fs.mkdirSync(builderPath)
}
await downloadTarballDirect(
await objectStore.downloadTarballDirect(
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
builderPath
)
setCookie(ctx, {}, cookieName)
utils.setCookie(ctx, {}, cookieName)
ctx.body = {
message: `${ctx.params.feature} enabled`,
@ -103,9 +99,9 @@ export const deleteObjects = 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)
let appId = getAppId()
let appId = context.getAppId()
if (!env.isJest()) {
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) {
const db = getAppDB({ skip_setup: true })
const db = context.getAppDB({ skip_setup: true })
const appInfo = await db.get(DocumentType.APP_METADATA)
if (!env.isJest()) {
let appId = getAppId()
let appId = context.getAppId()
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
ctx.body = await processString(previewHbs, {
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
@ -156,7 +152,7 @@ export const serveClientLibrary = async function (ctx: any) {
}
export const getSignedUploadURL = async function (ctx: any) {
const database = getAppDB()
const database = context.getAppDB()
// Ensure datasource is valid
let datasource

View file

@ -1,13 +1,14 @@
const { FieldTypes, FormulaTypes } = require("../../../constants")
const { clearColumns } = require("./utils")
const { doesContainStrings } = require("@budibase/string-templates")
const { cloneDeep } = require("lodash/fp")
const { isEqual, uniq } = require("lodash")
const { updateAllFormulasInTable } = require("../row/staticFormula")
const { getAppDB } = require("@budibase/backend-core/context")
const sdk = require("../../../sdk")
import { FieldTypes, FormulaTypes } from "../../../constants"
import { clearColumns } from "./utils"
import { doesContainStrings } from "@budibase/string-templates"
import { cloneDeep } from "lodash/fp"
import { isEqual, uniq } from "lodash"
import { updateAllFormulasInTable } from "../row/staticFormula"
import { context } from "@budibase/backend-core"
import { FieldSchema, Table } from "@budibase/types"
import sdk from "../../../sdk"
function isStaticFormula(column) {
function isStaticFormula(column: FieldSchema) {
return (
column.type === FieldTypes.FORMULA &&
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
* in the formula.
*/
function getFormulaThatUseColumn(table, columnNames) {
let formula = []
function getFormulaThatUseColumn(table: Table, columnNames: string[] | string) {
let formula: string[] = []
columnNames = Array.isArray(columnNames) ? columnNames : [columnNames]
for (let column of Object.values(table.schema)) {
// 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
* 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
const tables = (await sdk.tables.getAllInternalTables()).filter(
tbl => tbl._id !== table._id
@ -49,11 +53,14 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
)
// remove any formula columns that used related columns
for (let removed of removedColumns) {
let tableToUse = table
let tableToUse: Table | undefined = table
// if relationship, get the related table
if (removed.type === FieldTypes.LINK) {
tableToUse = tables.find(table => table._id === removed.tableId)
}
if (!tableToUse) {
continue
}
const columnsToDelete = getFormulaThatUseColumn(tableToUse, removed.name)
if (columnsToDelete.length > 0) {
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,
// relationships won't be used for this
if (relatedTable && relatedColumns && removed.type !== FieldTypes.LINK) {
let relatedFormulaToRemove = []
let relatedFormulaToRemove: string[] = []
for (let column of relatedColumns) {
relatedFormulaToRemove = relatedFormulaToRemove.concat(
getFormulaThatUseColumn(relatedTable, [
column.fieldName,
column.fieldName!,
removed.name,
])
)
@ -95,13 +102,14 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
* specifically only for static formula.
*/
async function updateRelatedFormulaLinksOnTables(
table,
{ deletion } = { deletion: false }
table: Table,
{ 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
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
const initialTables = cloneDeep(tables)
@ -114,7 +122,7 @@ async function updateRelatedFormulaLinksOnTables(
if (!otherTable.relatedFormula) {
continue
}
const index = otherTable.relatedFormula.indexOf(table._id)
const index = otherTable.relatedFormula.indexOf(tableId)
if (index !== -1) {
otherTable.relatedFormula.splice(index, 1)
}
@ -133,11 +141,11 @@ async function updateRelatedFormulaLinksOnTables(
if (
relatedTable &&
(!relatedTable.relatedFormula ||
!relatedTable.relatedFormula.includes(table._id))
!relatedTable.relatedFormula.includes(tableId))
) {
relatedTable.relatedFormula = relatedTable.relatedFormula
? [...relatedTable.relatedFormula, table._id]
: [table._id]
? [...relatedTable.relatedFormula, tableId]
: [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
const shouldUpdate = Object.values(table.schema).find(
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 checkIfFormulaNeedsCleared(table, { oldTable, deletion })
if (!deletion) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,16 +1,17 @@
const {
import {
ViewName,
generateMemoryViewID,
getMemoryViewParams,
DocumentType,
SEPARATOR,
} = require("../../../db/utils")
const env = require("../../../environment")
const { getAppDB } = require("@budibase/backend-core/context")
const viewBuilder = require("./viewBuilder")
} from "../../../db/utils"
import env from "../../../environment"
import { context } from "@budibase/backend-core"
import viewBuilder from "./viewBuilder"
import { Database } from "@budibase/types"
exports.getView = async viewName => {
const db = getAppDB()
export async function getView(viewName: string) {
const db = context.getAppDB()
if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database")
return designDoc.views[viewName]
@ -23,7 +24,7 @@ exports.getView = async viewName => {
try {
const viewDoc = await db.get(generateMemoryViewID(viewName))
return viewDoc.view
} catch (err) {
} catch (err: any) {
// Return null when PouchDB doesn't found the view
if (err.status === 404) {
return null
@ -34,14 +35,15 @@ exports.getView = async viewName => {
}
}
exports.getViews = async () => {
const db = getAppDB()
export async function getViews() {
const db = context.getAppDB()
const response = []
if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database")
for (let name of Object.keys(designDoc.views)) {
// 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
}
response.push({
@ -67,8 +69,12 @@ exports.getViews = async () => {
return response
}
exports.saveView = async (originalName, viewName, viewTemplate) => {
const db = getAppDB()
export async function saveView(
originalName: string | null,
viewName: string,
viewTemplate: any
) {
const db = context.getAppDB()
if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database")
designDoc.views = {
@ -83,7 +89,7 @@ exports.saveView = async (originalName, viewName, viewTemplate) => {
} else {
const id = generateMemoryViewID(viewName)
const originalId = originalName ? generateMemoryViewID(originalName) : null
const viewDoc = {
const viewDoc: any = {
_id: id,
view: viewTemplate,
name: viewName,
@ -105,8 +111,8 @@ exports.saveView = async (originalName, viewName, viewTemplate) => {
}
}
exports.deleteView = async viewName => {
const db = getAppDB()
export async function deleteView(viewName: string) {
const db = context.getAppDB()
if (env.SELF_HOSTED) {
const designDoc = await db.get("_design/database")
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
const designDoc = await db.get("_design/database")
// 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)
}
exports.migrateToDesignView = async (db, viewName) => {
export async function migrateToDesignView(db: Database, viewName: string) {
let view = await db.get(generateMemoryViewID(viewName))
const designDoc = await db.get("_design/database")
designDoc.views[viewName] = viewBuilder(view.view.meta)
@ -139,7 +145,7 @@ exports.migrateToDesignView = async (db, viewName) => {
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")
let view = designDoc.views[viewName]
if (view == null) {
@ -148,7 +154,7 @@ exports.getFromDesignDoc = async (db, viewName) => {
return view
}
exports.getFromMemoryDoc = async (db, viewName) => {
export async function getFromMemoryDoc(db: Database, viewName: string) {
let view = await db.get(generateMemoryViewID(viewName))
if (view) {
view = view.view

View file

@ -1,20 +1,15 @@
const Router = require("@koa/router")
const {
buildAuthMiddleware,
auditLog,
buildTenancyMiddleware,
} = require("@budibase/backend-core/auth")
const { errors } = require("@budibase/backend-core")
const currentApp = require("../middleware/currentapp")
import Router from "@koa/router"
import { errors, auth } from "@budibase/backend-core"
import currentApp from "../middleware/currentapp"
import zlib from "zlib"
import { mainRoutes, staticRoutes, publicRoutes } from "./routes"
import pkg from "../../package.json"
import env from "../environment"
import { middleware as pro } from "@budibase/pro"
export { shutdown } from "./routes/public"
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("/version", ctx => (ctx.body = pkg.version))
@ -42,7 +37,7 @@ router
// re-direct before any middlewares occur
.redirect("/", "/builder")
.use(
buildAuthMiddleware(null, {
auth.buildAuthMiddleware(null, {
publicAllowed: true,
})
)
@ -50,19 +45,20 @@ router
// 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
.use(
buildTenancyMiddleware(null, null, {
auth.buildTenancyMiddleware(null, null, {
noTenancyRequired: true,
})
)
.use(pro.licensing())
// @ts-ignore
.use(currentApp)
.use(auditLog)
.use(auth.auditLog)
// error handling middleware
router.use(async (ctx, next) => {
try {
await next()
} catch (err) {
} catch (err: any) {
ctx.status = err.status || err.statusCode || 500
const error = errors.getPublicError(err)
ctx.body = {
@ -91,6 +87,3 @@ router.use(publicRoutes.allowedMethods())
// WARNING - static routes will catch everything else after them this must be last
router.use(staticRoutes.routes())
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 * as controller from "../controllers/application"
import authorized from "../../middleware/authorized"
import { BUILDER } from "@budibase/backend-core/permissions"
import { permissions } from "@budibase/backend-core"
import { applicationValidator } from "./utils/validators"
const router = new Router()
const router: Router = new Router()
router
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
.post(
"/api/applications/:appId/sync",
authorized(permissions.BUILDER),
controller.sync
)
.post(
"/api/applications",
authorized(BUILDER),
authorized(permissions.BUILDER),
applicationValidator(),
controller.create
)
@ -19,20 +23,24 @@ router
.get("/api/applications/:appId/appPackage", controller.fetchAppPackage)
.put(
"/api/applications/:appId",
authorized(BUILDER),
authorized(permissions.BUILDER),
applicationValidator({ isCreate: false }),
controller.update
)
.post(
"/api/applications/:appId/client/update",
authorized(BUILDER),
authorized(permissions.BUILDER),
controller.updateClient
)
.post(
"/api/applications/:appId/client/revert",
authorized(BUILDER),
authorized(permissions.BUILDER),
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 * as controller from "../controllers/auth"
const router = new Router()
const router: Router = new Router()
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 * as controller from "../controllers/backup"
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")
const controller = require("../controllers/metadata")
const {
middleware: appInfoMiddleware,
import Router from "@koa/router"
import * as controller from "../controllers/metadata"
import {
middleware as appInfoMiddleware,
AppType,
} = require("../../middleware/appInfo")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("@budibase/backend-core/permissions")
} from "../../middleware/appInfo"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router = new Router()
const router: Router = new Router()
router
.post(
"/api/metadata/:type/:entityId",
authorized(BUILDER),
authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }),
controller.saveMetadata
)
.delete(
"/api/metadata/:type/:entityId",
authorized(BUILDER),
authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }),
controller.deleteMetadata
)
.get(
"/api/metadata/type",
authorized(BUILDER),
authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }),
controller.getTypes
)
.get(
"/api/metadata/:type/:entityId",
authorized(BUILDER),
authorized(permissions.BUILDER),
appInfoMiddleware({ appType: AppType.DEV }),
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 * as controller from "../controllers/plugin"
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
.post("/api/plugin/upload", authorized(BUILDER), controller.upload)
.post("/api/plugin", authorized(BUILDER), controller.create)
.get("/api/plugin", authorized(BUILDER), controller.fetch)
.delete("/api/plugin/:pluginId", authorized(BUILDER), controller.destroy)
.post(
"/api/plugin/upload",
authorized(permissions.BUILDER),
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

View file

@ -1,22 +1,19 @@
const Router = require("@koa/router")
const queryController = require("../controllers/query")
const authorized = require("../../middleware/authorized")
const {
PermissionLevel,
PermissionType,
BUILDER,
} = require("@budibase/backend-core/permissions")
const {
import Router from "@koa/router"
import * as queryController from "../controllers/query"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
import {
bodyResource,
bodySubResource,
paramResource,
} = require("../../middleware/resourceId")
const {
} from "../../middleware/resourceId"
import {
generateQueryPreviewValidation,
generateQueryValidation,
} = require("../controllers/query/validation")
} from "../controllers/query/validation"
const { BUILDER, PermissionType, PermissionLevel } = permissions
const router = new Router()
const router: Router = new Router()
router
.get("/api/queries", authorized(BUILDER), queryController.fetch)
@ -48,17 +45,17 @@ router
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
queryController.executeV1
)
.post(
"/api/v2/queries/:queryId",
paramResource("queryId"),
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
queryController.executeV2
)
.delete(
"/api/queries/:queryId/:revId",
paramResource("queryId"),
authorized(BUILDER),
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 authorized from "../../middleware/authorized"
import { paramResource, paramSubResource } from "../../middleware/resourceId"
const {
PermissionLevel,
PermissionType,
} = require("@budibase/backend-core/permissions")
import { permissions } from "@budibase/backend-core"
const { internalSearchValidator } = require("./utils/validators")
const { PermissionType, PermissionLevel } = permissions
const router = new Router()
const router: Router = new 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 { budibaseTempDir } from "../../utilities/budibaseDir"
import authorized from "../../middleware/authorized"
import {
BUILDER,
PermissionType,
PermissionLevel,
} from "@budibase/backend-core/permissions"
import { permissions } from "@budibase/backend-core"
import * as env from "../../environment"
import { paramResource } from "../../middleware/resourceId"
const { BUILDER, PermissionType, PermissionLevel } = permissions
const router = new Router()
const router: Router = new Router()
/* istanbul ignore next */
router.param("file", async (file: any, ctx: any, next: any) => {
@ -65,4 +62,4 @@ router
controller.getSignedUploadURL
)
export default router
export = router

View file

@ -1,15 +1,12 @@
const Router = require("@koa/router")
const tableController = require("../controllers/table")
const authorized = require("../../middleware/authorized")
const { paramResource, bodyResource } = require("../../middleware/resourceId")
const {
BUILDER,
PermissionLevel,
PermissionType,
} = require("@budibase/backend-core/permissions")
const { tableValidator } = require("./utils/validators")
import Router from "@koa/router"
import * as tableController from "../controllers/table"
import authorized from "../../middleware/authorized"
import { paramResource, bodyResource } from "../../middleware/resourceId"
import { permissions } from "@budibase/backend-core"
import { tableValidator } from "./utils/validators"
const { BUILDER, PermissionLevel, PermissionType } = permissions
const router = new Router()
const router: Router = new Router()
router
/**
@ -193,4 +190,4 @@ router
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 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 { basicRow } = setup.structures
const { BUILTIN_ROLE_IDS } = roles
const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC
const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC

View file

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

View file

@ -1,7 +1,8 @@
const setup = require("./utilities")
const { basicScreen } = setup.structures
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"

View file

@ -1,8 +1,7 @@
const { outputProcessing } = require("../../../utilities/rowProcessor")
const setup = require("./utilities")
const { basicRow } = setup.structures
const { doInAppContext } = require("@budibase/backend-core/context")
const { doInTenant } = require("@budibase/backend-core/tenancy")
const { context, tenancy } = require("@budibase/backend-core")
const {
quotas,
} = require("@budibase/pro")
@ -447,7 +446,7 @@ describe("/rows", () => {
describe("fetchEnrichedRows", () => {
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,
async () => {
const table = await config.createLinkedTable()
@ -507,7 +506,7 @@ describe("/rows", () => {
})
// the environment needs configured for this
await setup.switchToSelfHosted(async () => {
doInAppContext(config.getAppId(), async () => {
context.doInAppContext(config.getAppId(), async () => {
const enriched = await outputProcessing(table, [row])
expect(enriched[0].attachment[0].url).toBe(
`/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 setup = require("./utilities")
const { BUILTIN_ROLE_IDS } = roles
jest.mock("../../../utilities/workerRequests", () => ({
getGlobalUsers: jest.fn(() => {

View file

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

View file

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

View file

@ -1,20 +1,16 @@
const { joiValidator } = require("@budibase/backend-core/auth")
const { DataSourceOperation } = require("../../../constants")
const {
BuiltinPermissionID,
PermissionLevel,
} = require("@budibase/backend-core/permissions")
const { WebhookActionType } = require("@budibase/types")
const Joi = require("joi")
import { auth, permissions } from "@budibase/backend-core"
import { DataSourceOperation } from "../../../constants"
import { WebhookActionType } from "@budibase/types"
import Joi from "joi"
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
const OPTIONAL_NUMBER = Joi.number().optional().allow(null)
const OPTIONAL_BOOLEAN = Joi.boolean().optional().allow(null)
const APP_NAME_REGEX = /^[\w\s]+$/
exports.tableValidator = () => {
export function tableValidator() {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
type: OPTIONAL_STRING.valid("table", "internal", "external"),
@ -26,16 +22,16 @@ exports.tableValidator = () => {
}).unknown(true))
}
exports.nameValidator = () => {
export function nameValidator() {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
name: OPTIONAL_STRING,
}))
}
exports.datasourceValidator = () => {
export function datasourceValidator() {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
_id: Joi.string(),
_rev: Joi.string(),
type: OPTIONAL_STRING.allow("datasource_plus"),
@ -64,9 +60,9 @@ function filterObject() {
}).unknown(true)
}
exports.internalSearchValidator = () => {
export function internalSearchValidator() {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
tableId: OPTIONAL_STRING,
query: filterObject(),
limit: OPTIONAL_NUMBER,
@ -78,8 +74,8 @@ exports.internalSearchValidator = () => {
}))
}
exports.externalSearchValidator = () => {
return joiValidator.body(
export function externalSearchValidator() {
return auth.joiValidator.body(
Joi.object({
query: filterObject(),
paginate: Joi.boolean().optional(),
@ -96,9 +92,9 @@ exports.externalSearchValidator = () => {
)
}
exports.datasourceQueryValidator = () => {
export function datasourceQueryValidator() {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
endpoint: Joi.object({
datasourceId: Joi.string().required(),
operation: Joi.string().required().valid(...Object.values(DataSourceOperation)),
@ -117,9 +113,9 @@ exports.datasourceQueryValidator = () => {
}))
}
exports.webhookValidator = () => {
export function webhookValidator() {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
live: Joi.bool(),
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
@ -132,15 +128,15 @@ exports.webhookValidator = () => {
}).unknown(true))
}
exports.roleValidator = () => {
const permLevelArray = Object.values(PermissionLevel)
export function roleValidator() {
const permLevelArray = Object.values(permissions.PermissionLevel)
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
name: Joi.string().required(),
// 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()
.pattern(/.*/, [Joi.string().valid(...permLevelArray)])
.optional(),
@ -148,19 +144,19 @@ exports.roleValidator = () => {
}).unknown(true))
}
exports.permissionValidator = () => {
const permLevelArray = Object.values(PermissionLevel)
export function permissionValidator() {
const permLevelArray = Object.values(permissions.PermissionLevel)
// prettier-ignore
return joiValidator.params(Joi.object({
return auth.joiValidator.params(Joi.object({
level: Joi.string().valid(...permLevelArray).required(),
resourceId: Joi.string(),
roleId: Joi.string(),
}).unknown(true))
}
exports.screenValidator = () => {
export function screenValidator() {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
name: Joi.string().required(),
showNavigation: OPTIONAL_BOOLEAN,
width: OPTIONAL_STRING,
@ -181,7 +177,7 @@ exports.screenValidator = () => {
}).unknown(true))
}
function generateStepSchema(allowStepTypes) {
function generateStepSchema(allowStepTypes: string[]) {
// prettier-ignore
return Joi.object({
stepId: Joi.string().required(),
@ -196,9 +192,9 @@ function generateStepSchema(allowStepTypes) {
}).unknown(true)
}
exports.automationValidator = (existing = false) => {
export function automationValidator(existing = false) {
// prettier-ignore
return joiValidator.body(Joi.object({
return auth.joiValidator.body(Joi.object({
_id: existing ? Joi.string().required() : OPTIONAL_STRING,
_rev: existing ? Joi.string().required() : OPTIONAL_STRING,
name: Joi.string().required(),
@ -210,9 +206,9 @@ exports.automationValidator = (existing = false) => {
}).unknown(true))
}
exports.applicationValidator = (opts = { isCreate: true }) => {
export function applicationValidator(opts = { isCreate: true }) {
// prettier-ignore
const base = {
const base: any = {
_id: OPTIONAL_STRING,
_rev: OPTIONAL_STRING,
url: OPTIONAL_STRING,
@ -230,7 +226,7 @@ exports.applicationValidator = (opts = { isCreate: true }) => {
base.name = appNameValidator.optional()
}
return joiValidator.body(
return auth.joiValidator.body(
Joi.object({
_id: 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"
const BUILDER = permissions.BUILDER
const router = new Router()
const router: Router = new Router()
router
.get("/api/webhooks", authorized(BUILDER), controller.fetch)
@ -24,4 +24,4 @@ router
// this shouldn't have authorisation, right now its always public
.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 automations = require("./automations/index")
const Sentry = require("@sentry/node")
const { logAlert } = require("@budibase/backend-core/logging")
const { Thread } = require("./threads")
import redis from "./utilities/redis"
import { events } from "@budibase/backend-core"
import * as redis from "./utilities/redis"
import { events, logging } from "@budibase/backend-core"
import { initialise as initialiseWebsockets } from "./websocket"
import { startup } from "./startup"
@ -93,7 +92,7 @@ process.on("uncaughtException", err => {
return
}
errCode = -1
logAlert("Uncaught exception.", err)
logging.logAlert("Uncaught exception.", err)
shutdown()
})

View file

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

View file

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