From a620791d130084ff9f52d1021d4402f707eb0b28 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 22 Nov 2022 16:52:25 +0000 Subject: [PATCH] More controller typescript conversions. --- packages/backend-core/src/db/utils.ts | 4 +- packages/backend-core/src/security/roles.ts | 1 + .../controllers/{apikeys.js => apikeys.ts} | 20 ++-- .../{automation.js => automation.ts} | 96 +++++++++---------- .../api/controllers/{cloud.js => cloud.ts} | 47 ++++----- .../controllers/{metadata.js => metadata.ts} | 21 ++-- .../{permission.js => permission.ts} | 95 +++++++++--------- .../src/api/controllers/query/import/index.ts | 5 +- .../query/{validation.js => validation.ts} | 14 +-- .../src/api/controllers/{role.js => role.ts} | 46 ++++----- .../controllers/{routing.js => routing.ts} | 75 ++++++++------- .../view/{exporters.js => exporters.ts} | 8 +- .../controllers/view/{index.js => index.ts} | 65 +++++++------ .../controllers/view/{utils.js => utils.ts} | 46 +++++---- .../types/src/documents/app/automation.ts | 1 + packages/types/src/sdk/koa.ts | 1 + 16 files changed, 284 insertions(+), 261 deletions(-) rename packages/server/src/api/controllers/{apikeys.js => apikeys.ts} (65%) rename packages/server/src/api/controllers/{automation.js => automation.ts} (75%) rename packages/server/src/api/controllers/{cloud.js => cloud.ts} (64%) rename packages/server/src/api/controllers/{metadata.js => metadata.ts} (55%) rename packages/server/src/api/controllers/{permission.js => permission.ts} (64%) rename packages/server/src/api/controllers/query/{validation.js => validation.ts} (79%) rename packages/server/src/api/controllers/{role.js => role.ts} (72%) rename packages/server/src/api/controllers/{routing.js => routing.ts} (52%) rename packages/server/src/api/controllers/view/{exporters.js => exporters.ts} (72%) rename packages/server/src/api/controllers/view/{index.js => index.ts} (73%) rename packages/server/src/api/controllers/view/{utils.js => utils.ts} (76%) diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index 9920be7e55..04feafa008 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -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()}` } diff --git a/packages/backend-core/src/security/roles.ts b/packages/backend-core/src/security/roles.ts index 8e8b21a887..bdf7a38726 100644 --- a/packages/backend-core/src/security/roles.ts +++ b/packages/backend-core/src/security/roles.ts @@ -27,6 +27,7 @@ const EXTERNAL_BUILTIN_ROLE_IDS = [ export class Role implements RoleDoc { _id: string + _rev?: string name: string permissionId: string inherits?: string diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.ts similarity index 65% rename from packages/server/src/api/controllers/apikeys.js rename to packages/server/src/api/controllers/apikeys.ts index f5e36e7435..02b61954c3 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.ts @@ -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) } diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.ts similarity index 75% rename from packages/server/src/api/controllers/automation.js rename to packages/server/src/api/controllers/automation.ts index 2190adc3b9..185da80216 100644 --- a/packages/server/src/api/controllers/automation.js +++ b/packages/server/src/api/controllers/automation.ts @@ -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) diff --git a/packages/server/src/api/controllers/cloud.js b/packages/server/src/api/controllers/cloud.ts similarity index 64% rename from packages/server/src/api/controllers/cloud.js rename to packages/server/src/api/controllers/cloud.ts index 0a111eae83..7f29369bf2 100644 --- a/packages/server/src/api/controllers/cloud.js +++ b/packages/server/src/api/controllers/cloud.ts @@ -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) { diff --git a/packages/server/src/api/controllers/metadata.js b/packages/server/src/api/controllers/metadata.ts similarity index 55% rename from packages/server/src/api/controllers/metadata.js rename to packages/server/src/api/controllers/metadata.ts index e68db9b003..f579b14499 100644 --- a/packages/server/src/api/controllers/metadata.js +++ b/packages/server/src/api/controllers/metadata.ts @@ -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 { diff --git a/packages/server/src/api/controllers/permission.js b/packages/server/src/api/controllers/permission.ts similarity index 64% rename from packages/server/src/api/controllers/permission.js rename to packages/server/src/api/controllers/permission.ts index e1547eb597..bf2a905b51 100644 --- a/packages/server/src/api/controllers/permission.js +++ b/packages/server/src/api/controllers/permission.ts @@ -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 = {} + levels.forEach(level => (perms[level] = roleId!)) permissions[resource] = perms } } // apply the base permissions - const finalPermissions = {} + const finalPermissions: Record> = {} 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 = {} 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, diff --git a/packages/server/src/api/controllers/query/import/index.ts b/packages/server/src/api/controllers/query/import/index.ts index 339775cbdc..4d62765974 100644 --- a/packages/server/src/api/controllers/query/import/index.ts +++ b/packages/server/src/api/controllers/query/import/index.ts @@ -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 diff --git a/packages/server/src/api/controllers/query/validation.js b/packages/server/src/api/controllers/query/validation.ts similarity index 79% rename from packages/server/src/api/controllers/query/validation.js rename to packages/server/src/api/controllers/query/validation.ts index 1279ebbe48..339035c945 100644 --- a/packages/server/src/api/controllers/query/validation.js +++ b/packages/server/src/api/controllers/query/validation.ts @@ -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(), diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.ts similarity index 72% rename from packages/server/src/api/controllers/role.js rename to packages/server/src/api/controllers/role.ts index 9a406ad63f..77c8f8842b 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.ts @@ -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, }) ) diff --git a/packages/server/src/api/controllers/routing.js b/packages/server/src/api/controllers/routing.ts similarity index 52% rename from packages/server/src/api/controllers/routing.js rename to packages/server/src/api/controllers/routing.ts index d6ba9d6ac2..05ab35aea2 100644 --- a/packages/server/src/api/controllers/routing.js +++ b/packages/server/src/api/controllers/routing.ts @@ -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 diff --git a/packages/server/src/api/controllers/view/exporters.js b/packages/server/src/api/controllers/view/exporters.ts similarity index 72% rename from packages/server/src/api/controllers/view/exporters.js rename to packages/server/src/api/controllers/view/exporters.ts index ec366753a5..eec6e69641 100644 --- a/packages/server/src/api/controllers/view/exporters.js +++ b/packages/server/src/api/controllers/view/exporters.ts @@ -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", } diff --git a/packages/server/src/api/controllers/view/index.js b/packages/server/src/api/controllers/view/index.ts similarity index 73% rename from packages/server/src/api/controllers/view/index.js rename to packages/server/src/api/controllers/view/index.ts index 91657cfc21..17c3ee301d 100644 --- a/packages/server/src/api/controllers/view/index.js +++ b/packages/server/src/api/controllers/view/index.ts @@ -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) } } diff --git a/packages/server/src/api/controllers/view/utils.js b/packages/server/src/api/controllers/view/utils.ts similarity index 76% rename from packages/server/src/api/controllers/view/utils.js rename to packages/server/src/api/controllers/view/utils.ts index 7ce7d324e4..7fa37e6c5d 100644 --- a/packages/server/src/api/controllers/view/utils.js +++ b/packages/server/src/api/controllers/view/utils.ts @@ -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 diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index b53da956d4..d7450e4b0d 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -35,6 +35,7 @@ export interface Automation extends Document { trigger: AutomationTrigger } appId: string + live?: boolean name: string } diff --git a/packages/types/src/sdk/koa.ts b/packages/types/src/sdk/koa.ts index ffbb1cf053..3fe66b0786 100644 --- a/packages/types/src/sdk/koa.ts +++ b/packages/types/src/sdk/koa.ts @@ -14,6 +14,7 @@ export interface ContextUser extends Omit { export interface BBRequest extends Request { body: any + files?: any } export interface BBContext extends Context {