More controller typescript conversions.

This commit is contained in:
mike12345567 2022-11-22 16:52:25 +00:00
parent 7ab2029b5d
commit a620791d13
16 changed files with 284 additions and 261 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

@ -27,6 +27,7 @@ const EXTERNAL_BUILTIN_ROLE_IDS = [
export class Role implements RoleDoc {
_id: string
_rev?: string
name: string
permissionId: string
inherits?: string

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

@ -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,15 @@
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"
import { getAllApps } from "@budibase/backend-core/src/db"
async function createApp(appName, appDirectory) {
async function createApp(appName: string, appDirectory: string) {
const ctx = {
request: {
body: {
@ -25,7 +26,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 +35,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 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,7 +57,7 @@ exports.exportApps = async ctx => {
ctx.body = streamFile(tmpPath)
}
async function hasBeenImported() {
async function checkHasBeenImported() {
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
return true
}
@ -64,17 +65,17 @@ async function hasBeenImported() {
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 +91,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

@ -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

@ -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

@ -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,
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

@ -35,6 +35,7 @@ export interface Automation extends Document {
trigger: AutomationTrigger
}
appId: string
live?: boolean
name: string
}

View File

@ -14,6 +14,7 @@ export interface ContextUser extends Omit<User, "roles"> {
export interface BBRequest extends Request {
body: any
files?: any
}
export interface BBContext extends Context {