diff --git a/charts/budibase/templates/app-service-deployment.yaml b/charts/budibase/templates/app-service-deployment.yaml index bf1b672d37..72d9fc93a9 100644 --- a/charts/budibase/templates/app-service-deployment.yaml +++ b/charts/budibase/templates/app-service-deployment.yaml @@ -60,8 +60,6 @@ spec: secretKeyRef: name: {{ template "budibase.fullname" . }} key: jwtSecret - - name: LOG_LEVEL - value: {{ .Values.services.apps.logLevel | default "info" | quote }} {{ if .Values.services.objectStore.region }} - name: AWS_REGION value: {{ .Values.services.objectStore.region }} diff --git a/lerna.json b/lerna.json index 24172a05b0..56c479324c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 6a54c16c89..76b158091b 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "2.1.32-alpha.3", + "@budibase/types": "2.1.32-alpha.7", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", 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/db/views.ts b/packages/backend-core/src/db/views.ts index 5c63ce4689..c563d55be3 100644 --- a/packages/backend-core/src/db/views.ts +++ b/packages/backend-core/src/db/views.ts @@ -145,7 +145,9 @@ export const queryView = async ( return docs.length <= 1 ? (docs[0] as T) : (docs as T[]) } } catch (err: any) { - if (err != null && err.name === "not_found") { + const pouchNotFound = err && err.name === "not_found" + const couchNotFound = err && err.status === 404 + if (pouchNotFound || couchNotFound) { await removeDeprecated(db, viewName) await createFunc() return queryView(viewName, params, db, createFunc, opts) diff --git a/packages/backend-core/src/migrations/migrations.ts b/packages/backend-core/src/migrations/migrations.ts index 60c17f4020..7bc2dec290 100644 --- a/packages/backend-core/src/migrations/migrations.ts +++ b/packages/backend-core/src/migrations/migrations.ts @@ -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 { diff --git a/packages/backend-core/src/objectStore/index.ts b/packages/backend-core/src/objectStore/index.ts index 84eebeff81..a1193c0303 100644 --- a/packages/backend-core/src/objectStore/index.ts +++ b/packages/backend-core/src/objectStore/index.ts @@ -390,7 +390,7 @@ export const uploadDirectory = async ( return files } -exports.downloadTarballDirect = async ( +export const downloadTarballDirect = async ( url: string, path: string, headers = {} diff --git a/packages/backend-core/src/security/roles.ts b/packages/backend-core/src/security/roles.ts index da475322a7..bdf7a38726 100644 --- a/packages/backend-core/src/security/roles.ts +++ b/packages/backend-core/src/security/roles.ts @@ -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 diff --git a/packages/backend-core/src/security/sessions.ts b/packages/backend-core/src/security/sessions.ts index 33230afc60..48e75b0d60 100644 --- a/packages/backend-core/src/security/sessions.ts +++ b/packages/backend-core/src/security/sessions.ts @@ -89,6 +89,7 @@ export async function createASession( userId, } await client.store(key, session, EXPIRY_SECONDS) + return session } export async function updateSessionTTL(session: Session) { diff --git a/packages/bbui/package.json b/packages/bbui/package.json index b619e85651..371bdf259f 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "2.1.32-alpha.3", + "@budibase/string-templates": "2.1.32-alpha.7", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/bbui/yarn.lock b/packages/bbui/yarn.lock index f148ca201e..3b748a7daf 100644 --- a/packages/bbui/yarn.lock +++ b/packages/bbui/yarn.lock @@ -53,10 +53,10 @@ to-gfm-code-block "^0.1.1" year "^0.2.1" -"@budibase/string-templates@2.1.22-alpha.5": - version "2.1.22-alpha.5" - resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.1.22-alpha.5.tgz#3493be2ec8a3799ad1f7e470c308d9cdcd36abf6" - integrity sha512-iFN4nccB8eIjsaU0ki7DyC+zznJaGC+cUQiAiwgO+aDm3SD6vkF443IjwL/fcmpK81/4WpEWmJPDjVuQ+vjlKQ== +"@budibase/string-templates@2.1.32-alpha.0": + version "2.1.32-alpha.0" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.1.32-alpha.0.tgz#97452a80b3c3238e0d11b48ec36d2d7b8cb8e1d5" + integrity sha512-F+AkIWb8CitLt+YCJV/GQB8ECfj7GfO4I/vptIyNay9EWo0MRIU+4Xn715nBfEW5e23BQPKF3QR8S6y0IgxFbg== dependencies: "@budibase/handlebars-helpers" "^0.11.8" dayjs "^1.10.4" @@ -824,7 +824,7 @@ component-emitter@^1.2.1: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-with-sourcemaps@*, concat-with-sourcemaps@^1.1.0: version "1.1.0" @@ -2080,10 +2080,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -loader-utils@^1.1.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" - integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== +loader-utils@1.4.1, loader-utils@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.1.tgz#278ad7006660bccc4d2c0c1578e17c5c78d5c0e0" + integrity sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" @@ -2220,9 +2220,9 @@ mime@1.6.0: integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" diff --git a/packages/builder/package.json b/packages/builder/package.json index 2e8367db1f..82e4497a9b 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "2.1.32-alpha.3", - "@budibase/client": "2.1.32-alpha.3", - "@budibase/frontend-core": "2.1.32-alpha.3", - "@budibase/string-templates": "2.1.32-alpha.3", + "@budibase/bbui": "2.1.32-alpha.7", + "@budibase/client": "2.1.32-alpha.7", + "@budibase/frontend-core": "2.1.32-alpha.7", + "@budibase/string-templates": "2.1.32-alpha.7", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 45f269b1a3..654117d604 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { @@ -26,9 +26,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "2.1.32-alpha.3", - "@budibase/string-templates": "2.1.32-alpha.3", - "@budibase/types": "2.1.32-alpha.3", + "@budibase/backend-core": "2.1.32-alpha.7", + "@budibase/string-templates": "2.1.32-alpha.7", + "@budibase/types": "2.1.32-alpha.7", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", diff --git a/packages/client/package.json b/packages/client/package.json index 14ecbbf5d8..eb13a836fd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "2.1.32-alpha.3", - "@budibase/frontend-core": "2.1.32-alpha.3", - "@budibase/string-templates": "2.1.32-alpha.3", + "@budibase/bbui": "2.1.32-alpha.7", + "@budibase/frontend-core": "2.1.32-alpha.7", + "@budibase/string-templates": "2.1.32-alpha.7", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index 5fa508f8c1..a5795f8c0f 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "2.1.32-alpha.3", + "@budibase/bbui": "2.1.32-alpha.7", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 9c1efd78f3..feebaf96a1 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/sdk", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Budibase Public API SDK", "author": "Budibase", "license": "MPL-2.0", diff --git a/packages/server/package.json b/packages/server/package.json index 62b2395ad5..8892edefad 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -43,11 +43,11 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "10.0.3", - "@budibase/backend-core": "2.1.32-alpha.3", - "@budibase/client": "2.1.32-alpha.3", - "@budibase/pro": "2.1.32-alpha.3", - "@budibase/string-templates": "2.1.32-alpha.3", - "@budibase/types": "2.1.32-alpha.3", + "@budibase/backend-core": "2.1.32-alpha.7", + "@budibase/client": "2.1.32-alpha.7", + "@budibase/pro": "2.1.32-alpha.7", + "@budibase/string-templates": "2.1.32-alpha.7", + "@budibase/types": "2.1.32-alpha.7", "@bull-board/api": "3.7.0", "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/server/scripts/exportAppTemplate.js b/packages/server/scripts/exportAppTemplate.js index bb1de425ba..04688b071f 100755 --- a/packages/server/scripts/exportAppTemplate.js +++ b/packages/server/scripts/exportAppTemplate.js @@ -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 => !( 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/auth.ts b/packages/server/src/api/controllers/auth.ts index 88dc581020..c28b99529b 100644 --- a/packages/server/src/api/controllers/auth.ts +++ b/packages/server/src/api/controllers/auth.ts @@ -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 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..41c21d534a 100644 --- a/packages/server/src/api/controllers/cloud.js +++ b/packages/server/src/api/controllers/cloud.ts @@ -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) { diff --git a/packages/server/src/api/controllers/component.ts b/packages/server/src/api/controllers/component.ts index 6d65d43db6..9b196ca289 100644 --- a/packages/server/src/api/controllers/component.ts +++ b/packages/server/src/api/controllers/component.ts @@ -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) diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.ts similarity index 74% rename from packages/server/src/api/controllers/datasource.js rename to packages/server/src/api/controllers/datasource.ts index 80bd263d87..511eb8ab74 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.ts @@ -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 diff --git a/packages/server/src/api/controllers/deploy/Deployment.js b/packages/server/src/api/controllers/deploy/Deployment.ts similarity index 69% rename from packages/server/src/api/controllers/deploy/Deployment.js rename to packages/server/src/api/controllers/deploy/Deployment.ts index 65cca97d07..c15cffbcd8 100644 --- a/packages/server/src/api/controllers/deploy/Deployment.js +++ b/packages/server/src/api/controllers/deploy/Deployment.ts @@ -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 diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.ts similarity index 70% rename from packages/server/src/api/controllers/layout.js rename to packages/server/src/api/controllers/layout.ts index f3f52d4ef0..c00252d643 100644 --- a/packages/server/src/api/controllers/layout.js +++ b/packages/server/src/api/controllers/layout.ts @@ -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 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/plugin/index.ts b/packages/server/src/api/controllers/plugin/index.ts index 7d1b1291ab..907fcf65a6 100644 --- a/packages/server/src/api/controllers/plugin/index.ts +++ b/packages/server/src/api/controllers/plugin/index.ts @@ -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) { diff --git a/packages/server/src/api/controllers/public/tables.ts b/packages/server/src/api/controllers/public/tables.ts index 3ee94bddb3..a346a750da 100644 --- a/packages/server/src/api/controllers/public/tables.ts +++ b/packages/server/src/api/controllers/public/tables.ts @@ -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) { 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/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 852f124ee5..2ef7953437 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -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." diff --git a/packages/server/src/api/controllers/row/external.js b/packages/server/src/api/controllers/row/external.js index fa48106d26..e0c3a9ee4d 100644 --- a/packages/server/src/api/controllers/row/external.js +++ b/packages/server/src/api/controllers/row/external.js @@ -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.") diff --git a/packages/server/src/api/controllers/row/internal.js b/packages/server/src/api/controllers/row/internal.js index 0f1324f10e..9b105fd3ae 100644 --- a/packages/server/src/api/controllers/row/internal.js +++ b/packages/server/src/api/controllers/row/internal.js @@ -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 diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.ts similarity index 76% rename from packages/server/src/api/controllers/row/internalSearch.js rename to packages/server/src/api/controllers/row/internalSearch.ts index 051a55aa9f..7068aabc5a 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.ts @@ -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 { 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 diff --git a/packages/server/src/api/controllers/row/staticFormula.js b/packages/server/src/api/controllers/row/staticFormula.ts similarity index 80% rename from packages/server/src/api/controllers/row/staticFormula.js rename to packages/server/src/api/controllers/row/staticFormula.ts index 2de3ae0e4f..ee09264af1 100644 --- a/packages/server/src/api/controllers/row/staticFormula.js +++ b/packages/server/src/api/controllers/row/staticFormula.ts @@ -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[] = [] for (let enrichedRow of Array.isArray(enrichedRows) ? enrichedRows : [enrichedRows]) { // the related rows by tableId - let relatedRows = {} + let relatedRows: Record = {} 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, { diff --git a/packages/server/src/api/controllers/row/utils.js b/packages/server/src/api/controllers/row/utils.ts similarity index 75% rename from packages/server/src/api/controllers/row/utils.js rename to packages/server/src/api/controllers/row/utils.ts index ca2ad02a30..3719c206e2 100644 --- a/packages/server/src/api/controllers/row/utils.js +++ b/packages/server/src/api/controllers/row/utils.ts @@ -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 diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index 08040351dd..9cbd019d6e 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -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 } diff --git a/packages/server/src/api/controllers/script.js b/packages/server/src/api/controllers/script.js deleted file mode 100644 index 38211ebee7..0000000000 --- a/packages/server/src/api/controllers/script.js +++ /dev/null @@ -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") -} diff --git a/packages/server/src/api/controllers/script.ts b/packages/server/src/api/controllers/script.ts new file mode 100644 index 0000000000..8af339b0b5 --- /dev/null +++ b/packages/server/src/api/controllers/script.ts @@ -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") +} diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index bdd9bfd4a6..68c01557bc 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -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 diff --git a/packages/server/src/api/controllers/table/bulkFormula.js b/packages/server/src/api/controllers/table/bulkFormula.ts similarity index 77% rename from packages/server/src/api/controllers/table/bulkFormula.js rename to packages/server/src/api/controllers/table/bulkFormula.ts index 733c16d455..142eeff6df 100644 --- a/packages/server/src/api/controllers/table/bulkFormula.js +++ b/packages/server/src/api/controllers/table/bulkFormula.ts @@ -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) { diff --git a/packages/server/src/api/controllers/table/external.js b/packages/server/src/api/controllers/table/external.ts similarity index 74% rename from packages/server/src/api/controllers/table/external.js rename to packages/server/src/api/controllers/table/external.ts index fe9270fe1d..8fd227e633 100644 --- a/packages/server/src/api/controllers/table/external.js +++ b/packages/server/src/api/controllers/table/external.ts @@ -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, + 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, + 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 = 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) { diff --git a/packages/server/src/api/controllers/table/index.js b/packages/server/src/api/controllers/table/index.ts similarity index 74% rename from packages/server/src/api/controllers/table/index.js rename to packages/server/src/api/controllers/table/index.ts index 3a20f4dff6..5d8ab5be3e 100644 --- a/packages/server/src/api/controllers/table/index.js +++ b/packages/server/src/api/controllers/table/index.ts @@ -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 diff --git a/packages/server/src/api/controllers/table/internal.ts b/packages/server/src/api/controllers/table/internal.ts index 9b2f20497b..a70ba4dcba 100644 --- a/packages/server/src/api/controllers/table/internal.ts +++ b/packages/server/src/api/controllers/table/internal.ts @@ -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, { diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index f182e6777d..b672561325 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -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: {}, diff --git a/packages/server/src/api/controllers/templates.js b/packages/server/src/api/controllers/templates.ts similarity index 64% rename from packages/server/src/api/controllers/templates.js rename to packages/server/src/api/controllers/templates.ts index 3a1cafdf5a..6774fa2766 100644 --- a/packages/server/src/api/controllers/templates.js +++ b/packages/server/src/api/controllers/templates.ts @@ -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.`, diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.ts similarity index 69% rename from packages/server/src/api/controllers/user.js rename to packages/server/src/api/controllers/user.ts index 7d4ef65994..f1a66f2c19 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.ts @@ -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) 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..ef7ce772f9 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 | 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 diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.ts similarity index 70% rename from packages/server/src/api/index.js rename to packages/server/src/api/index.ts index 4cd574f557..a77fc62b9b 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.ts @@ -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 diff --git a/packages/server/src/api/routes/analytics.js b/packages/server/src/api/routes/analytics.js deleted file mode 100644 index 610d6d0c7f..0000000000 --- a/packages/server/src/api/routes/analytics.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/analytics.ts b/packages/server/src/api/routes/analytics.ts new file mode 100644 index 0000000000..87dcb73eab --- /dev/null +++ b/packages/server/src/api/routes/analytics.ts @@ -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 diff --git a/packages/server/src/api/routes/apikeys.js b/packages/server/src/api/routes/apikeys.js deleted file mode 100644 index ddbd35c23c..0000000000 --- a/packages/server/src/api/routes/apikeys.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/apikeys.ts b/packages/server/src/api/routes/apikeys.ts new file mode 100644 index 0000000000..3afa1a0950 --- /dev/null +++ b/packages/server/src/api/routes/apikeys.ts @@ -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 diff --git a/packages/server/src/api/routes/application.ts b/packages/server/src/api/routes/application.ts index 8627dd024c..486953ea8b 100644 --- a/packages/server/src/api/routes/application.ts +++ b/packages/server/src/api/routes/application.ts @@ -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 diff --git a/packages/server/src/api/routes/auth.ts b/packages/server/src/api/routes/auth.ts index 8a9d11fb27..3b2aaca79e 100644 --- a/packages/server/src/api/routes/auth.ts +++ b/packages/server/src/api/routes/auth.ts @@ -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 diff --git a/packages/server/src/api/routes/automation.js b/packages/server/src/api/routes/automation.js deleted file mode 100644 index 544d94abb2..0000000000 --- a/packages/server/src/api/routes/automation.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/automation.ts b/packages/server/src/api/routes/automation.ts new file mode 100644 index 0000000000..1beb8e7d76 --- /dev/null +++ b/packages/server/src/api/routes/automation.ts @@ -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 diff --git a/packages/server/src/api/routes/backup.ts b/packages/server/src/api/routes/backup.ts index 2473fa9f67..63e9fee5b6 100644 --- a/packages/server/src/api/routes/backup.ts +++ b/packages/server/src/api/routes/backup.ts @@ -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 diff --git a/packages/server/src/api/routes/cloud.js b/packages/server/src/api/routes/cloud.js deleted file mode 100644 index c183ffb5ba..0000000000 --- a/packages/server/src/api/routes/cloud.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/cloud.ts b/packages/server/src/api/routes/cloud.ts new file mode 100644 index 0000000000..acc6b9e9c9 --- /dev/null +++ b/packages/server/src/api/routes/cloud.ts @@ -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 diff --git a/packages/server/src/api/routes/component.js b/packages/server/src/api/routes/component.js deleted file mode 100644 index 275f58bd6c..0000000000 --- a/packages/server/src/api/routes/component.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/component.ts b/packages/server/src/api/routes/component.ts new file mode 100644 index 0000000000..20b3e54c81 --- /dev/null +++ b/packages/server/src/api/routes/component.ts @@ -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 diff --git a/packages/server/src/api/routes/datasource.js b/packages/server/src/api/routes/datasource.js deleted file mode 100644 index 402e464e16..0000000000 --- a/packages/server/src/api/routes/datasource.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/datasource.ts b/packages/server/src/api/routes/datasource.ts new file mode 100644 index 0000000000..c9de7f51fb --- /dev/null +++ b/packages/server/src/api/routes/datasource.ts @@ -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 diff --git a/packages/server/src/api/routes/deploy.js b/packages/server/src/api/routes/deploy.js deleted file mode 100644 index 1f6b07c6f3..0000000000 --- a/packages/server/src/api/routes/deploy.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/deploy.ts b/packages/server/src/api/routes/deploy.ts new file mode 100644 index 0000000000..b0e4960acb --- /dev/null +++ b/packages/server/src/api/routes/deploy.ts @@ -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 diff --git a/packages/server/src/api/routes/dev.js b/packages/server/src/api/routes/dev.js deleted file mode 100644 index 0103219246..0000000000 --- a/packages/server/src/api/routes/dev.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/dev.ts b/packages/server/src/api/routes/dev.ts new file mode 100644 index 0000000000..d61c56d5ee --- /dev/null +++ b/packages/server/src/api/routes/dev.ts @@ -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 diff --git a/packages/server/src/api/routes/integration.js b/packages/server/src/api/routes/integration.js deleted file mode 100644 index 5469aaa27d..0000000000 --- a/packages/server/src/api/routes/integration.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/integration.ts b/packages/server/src/api/routes/integration.ts new file mode 100644 index 0000000000..835cc5a896 --- /dev/null +++ b/packages/server/src/api/routes/integration.ts @@ -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 diff --git a/packages/server/src/api/routes/layout.js b/packages/server/src/api/routes/layout.js deleted file mode 100644 index 76103f9cfc..0000000000 --- a/packages/server/src/api/routes/layout.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/layout.ts b/packages/server/src/api/routes/layout.ts new file mode 100644 index 0000000000..cdfdb4002a --- /dev/null +++ b/packages/server/src/api/routes/layout.ts @@ -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 diff --git a/packages/server/src/api/routes/metadata.js b/packages/server/src/api/routes/metadata.ts similarity index 52% rename from packages/server/src/api/routes/metadata.js rename to packages/server/src/api/routes/metadata.ts index 0c2867c45a..e46e0eeb01 100644 --- a/packages/server/src/api/routes/metadata.js +++ b/packages/server/src/api/routes/metadata.ts @@ -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 diff --git a/packages/server/src/api/routes/migrations.js b/packages/server/src/api/routes/migrations.js deleted file mode 100644 index a40111cf25..0000000000 --- a/packages/server/src/api/routes/migrations.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/migrations.ts b/packages/server/src/api/routes/migrations.ts new file mode 100644 index 0000000000..689373cf75 --- /dev/null +++ b/packages/server/src/api/routes/migrations.ts @@ -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 diff --git a/packages/server/src/api/routes/permission.js b/packages/server/src/api/routes/permission.js deleted file mode 100644 index 4736769f61..0000000000 --- a/packages/server/src/api/routes/permission.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/permission.ts b/packages/server/src/api/routes/permission.ts new file mode 100644 index 0000000000..8a26deb9ba --- /dev/null +++ b/packages/server/src/api/routes/permission.ts @@ -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 diff --git a/packages/server/src/api/routes/plugin.ts b/packages/server/src/api/routes/plugin.ts index d619745a4a..70838c499e 100644 --- a/packages/server/src/api/routes/plugin.ts +++ b/packages/server/src/api/routes/plugin.ts @@ -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 diff --git a/packages/server/src/api/routes/query.js b/packages/server/src/api/routes/query.ts similarity index 73% rename from packages/server/src/api/routes/query.js rename to packages/server/src/api/routes/query.ts index 1195e55113..439e3a6ec8 100644 --- a/packages/server/src/api/routes/query.js +++ b/packages/server/src/api/routes/query.ts @@ -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 diff --git a/packages/server/src/api/routes/role.js b/packages/server/src/api/routes/role.js deleted file mode 100644 index a6e04e81fa..0000000000 --- a/packages/server/src/api/routes/role.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/role.ts b/packages/server/src/api/routes/role.ts new file mode 100644 index 0000000000..48933a505b --- /dev/null +++ b/packages/server/src/api/routes/role.ts @@ -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 diff --git a/packages/server/src/api/routes/routing.js b/packages/server/src/api/routes/routing.js deleted file mode 100644 index d7e971d507..0000000000 --- a/packages/server/src/api/routes/routing.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/routing.ts b/packages/server/src/api/routes/routing.ts new file mode 100644 index 0000000000..56f2b40192 --- /dev/null +++ b/packages/server/src/api/routes/routing.ts @@ -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 diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts index 72189a2482..f4462b3595 100644 --- a/packages/server/src/api/routes/row.ts +++ b/packages/server/src/api/routes/row.ts @@ -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 /** diff --git a/packages/server/src/api/routes/screen.js b/packages/server/src/api/routes/screen.js deleted file mode 100644 index 426b89fd0f..0000000000 --- a/packages/server/src/api/routes/screen.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/screen.ts b/packages/server/src/api/routes/screen.ts new file mode 100644 index 0000000000..c8bab17b31 --- /dev/null +++ b/packages/server/src/api/routes/screen.ts @@ -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 diff --git a/packages/server/src/api/routes/script.js b/packages/server/src/api/routes/script.js deleted file mode 100644 index a4b4e4a7f5..0000000000 --- a/packages/server/src/api/routes/script.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/script.ts b/packages/server/src/api/routes/script.ts new file mode 100644 index 0000000000..271cbc51c6 --- /dev/null +++ b/packages/server/src/api/routes/script.ts @@ -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 diff --git a/packages/server/src/api/routes/static.ts b/packages/server/src/api/routes/static.ts index ccfec6fd8c..992575fb74 100644 --- a/packages/server/src/api/routes/static.ts +++ b/packages/server/src/api/routes/static.ts @@ -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 diff --git a/packages/server/src/api/routes/table.js b/packages/server/src/api/routes/table.ts similarity index 95% rename from packages/server/src/api/routes/table.js rename to packages/server/src/api/routes/table.ts index 8d280e06d3..0b6d80cdaf 100644 --- a/packages/server/src/api/routes/table.js +++ b/packages/server/src/api/routes/table.ts @@ -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 diff --git a/packages/server/src/api/routes/templates.js b/packages/server/src/api/routes/templates.js deleted file mode 100644 index 61a185b5c8..0000000000 --- a/packages/server/src/api/routes/templates.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/templates.ts b/packages/server/src/api/routes/templates.ts new file mode 100644 index 0000000000..a05e1f12ac --- /dev/null +++ b/packages/server/src/api/routes/templates.ts @@ -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 diff --git a/packages/server/src/api/routes/tests/backup.spec.js b/packages/server/src/api/routes/tests/backup.spec.js index 7863129c75..7131aca852 100644 --- a/packages/server/src/api/routes/tests/backup.spec.js +++ b/packages/server/src/api/routes/tests/backup.spec.js @@ -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") diff --git a/packages/server/src/api/routes/tests/permissions.spec.js b/packages/server/src/api/routes/tests/permissions.spec.js index b416d1c0bf..093f63913a 100644 --- a/packages/server/src/api/routes/tests/permissions.spec.js +++ b/packages/server/src/api/routes/tests/permissions.spec.js @@ -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 diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index 56a3f1e9d9..8402a7b13c 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -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() diff --git a/packages/server/src/api/routes/tests/routing.spec.js b/packages/server/src/api/routes/tests/routing.spec.js index 1c3abf2457..148e5eded1 100644 --- a/packages/server/src/api/routes/tests/routing.spec.js +++ b/packages/server/src/api/routes/tests/routing.spec.js @@ -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" diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index afcc73fd36..65e6df544a 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -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` diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 29c33b3899..7e4bf02616 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -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(() => { diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.ts b/packages/server/src/api/routes/tests/utilities/TestFunctions.ts index a34527e8f2..3ac7dea5ac 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.ts +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.ts @@ -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) => { diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.ts similarity index 77% rename from packages/server/src/api/routes/user.js rename to packages/server/src/api/routes/user.ts index a290ced829..60eaf5b1b9 100644 --- a/packages/server/src/api/routes/user.js +++ b/packages/server/src/api/routes/user.ts @@ -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 diff --git a/packages/server/src/api/routes/utils/validators.js b/packages/server/src/api/routes/utils/validators.ts similarity index 79% rename from packages/server/src/api/routes/utils/validators.js rename to packages/server/src/api/routes/utils/validators.ts index b44cce5771..b6def14d70 100644 --- a/packages/server/src/api/routes/utils/validators.js +++ b/packages/server/src/api/routes/utils/validators.ts @@ -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, diff --git a/packages/server/src/api/routes/view.js b/packages/server/src/api/routes/view.js deleted file mode 100644 index 0a8bd7d4ce..0000000000 --- a/packages/server/src/api/routes/view.js +++ /dev/null @@ -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 diff --git a/packages/server/src/api/routes/view.ts b/packages/server/src/api/routes/view.ts new file mode 100644 index 0000000000..3705e74984 --- /dev/null +++ b/packages/server/src/api/routes/view.ts @@ -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 diff --git a/packages/server/src/api/routes/webhook.ts b/packages/server/src/api/routes/webhook.ts index 103ab98142..8067ecac7c 100644 --- a/packages/server/src/api/routes/webhook.ts +++ b/packages/server/src/api/routes/webhook.ts @@ -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 diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 9253186498..8a9c1d5b24 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -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() }) diff --git a/packages/server/src/automations/triggers.js b/packages/server/src/automations/triggers.js index 3481cebf9b..4f865d4df8 100644 --- a/packages/server/src/automations/triggers.js +++ b/packages/server/src/automations/triggers.js @@ -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) { diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 6d8fe57baa..5f68cc26ed 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -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), }, }, }, diff --git a/packages/server/src/constants/screens.js b/packages/server/src/constants/screens.js index 23f8bc88df..dc21c0d9bd 100644 --- a/packages/server/src/constants/screens.js +++ b/packages/server/src/constants/screens.js @@ -1,4 +1,4 @@ -const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") +const { roles } = require("@budibase/backend-core") const { BASE_LAYOUT_PROP_IDS } = require("./layouts") exports.createHomeScreen = () => ({ @@ -40,7 +40,7 @@ exports.createHomeScreen = () => ({ }, routing: { route: "/", - roleId: BUILTIN_ROLE_IDS.BASIC, + roleId: roles.BUILTIN_ROLE_IDS.BASIC, }, name: "home-screen", }) diff --git a/packages/server/src/db/inMemoryView.js b/packages/server/src/db/inMemoryView.js index 57ea89071c..278b906e24 100644 --- a/packages/server/src/db/inMemoryView.js +++ b/packages/server/src/db/inMemoryView.js @@ -2,8 +2,8 @@ const newid = require("./newid") // bypass the main application db config // use in memory pouchdb directly -const { getPouch, closePouchDB } = require("@budibase/backend-core/db") -const Pouch = getPouch({ inMemory: true }) +const { db: dbCore } = require("@budibase/backend-core") +const Pouch = dbCore.getPouch({ inMemory: true }) exports.runView = async (view, calculation, group, data) => { // use a different ID each time for the DB, make sure they @@ -44,6 +44,6 @@ exports.runView = async (view, calculation, group, data) => { return response } finally { await db.destroy() - await closePouchDB(db) + await dbCore.closePouchDB(db) } } diff --git a/packages/server/src/db/linkedRows/LinkController.js b/packages/server/src/db/linkedRows/LinkController.js index e7c338f709..df24b97e85 100644 --- a/packages/server/src/db/linkedRows/LinkController.js +++ b/packages/server/src/db/linkedRows/LinkController.js @@ -2,12 +2,12 @@ const { IncludeDocs, getLinkDocuments } = require("./linkUtils") const { InternalTables, getUserMetadataParams } = require("../utils") const Sentry = require("@sentry/node") const { FieldTypes, RelationshipTypes } = require("../../constants") -const { getAppDB } = require("@budibase/backend-core/context") +const { context } = require("@budibase/backend-core") const LinkDocument = require("./LinkDocument") class LinkController { constructor({ tableId, row, table, oldTable }) { - this._db = getAppDB() + this._db = context.getAppDB() this._tableId = tableId this._row = row this._table = table diff --git a/packages/server/src/db/linkedRows/index.js b/packages/server/src/db/linkedRows/index.js index 45bc72156f..1ee98f6148 100644 --- a/packages/server/src/db/linkedRows/index.js +++ b/packages/server/src/db/linkedRows/index.js @@ -14,7 +14,7 @@ const { getMultiIDParams, USER_METDATA_PREFIX } = require("../../db/utils") const { partition } = require("lodash") const { getGlobalUsersFromMetadata } = require("../../utilities/global") const { processFormulas } = require("../../utilities/rowProcessor/utils") -const { getAppDB } = require("@budibase/backend-core/context") +const { context } = require("@budibase/backend-core") /** * This functionality makes sure that when rows with links are created, updated or deleted they are processed @@ -74,7 +74,7 @@ async function getLinksForRows(rows) { async function getFullLinkedDocs(links) { // create DBs - const db = getAppDB() + const db = context.getAppDB() const linkedRowIds = links.map(link => link.id) const uniqueRowIds = [...new Set(linkedRowIds)] let dbRows = (await db.allDocs(getMultiIDParams(uniqueRowIds))).rows.map( diff --git a/packages/server/src/db/linkedRows/linkUtils.js b/packages/server/src/db/linkedRows/linkUtils.js index 5fc393ecf0..25a1b5fcf8 100644 --- a/packages/server/src/db/linkedRows/linkUtils.js +++ b/packages/server/src/db/linkedRows/linkUtils.js @@ -2,7 +2,7 @@ const Sentry = require("@sentry/node") const { ViewName, getQueryIndex } = require("../utils") const { FieldTypes } = require("../../constants") const { createLinkView } = require("../views/staticViews") -const { getAppDB } = require("@budibase/backend-core/context") +const { context } = require("@budibase/backend-core") /** * Only needed so that boolean parameters are being used for includeDocs @@ -30,7 +30,7 @@ exports.createLinkView = createLinkView */ exports.getLinkDocuments = async function (args) { const { tableId, rowId, includeDocs } = args - const db = getAppDB() + const db = context.getAppDB() let params if (rowId != null) { params = { key: [tableId, rowId] } @@ -89,7 +89,7 @@ exports.getLinkedTableIDs = table => { } exports.getLinkedTable = async (id, tables) => { - const db = getAppDB() + const db = context.getAppDB() let linkedTable = tables.find(table => table._id === id) if (linkedTable) { return linkedTable diff --git a/packages/server/src/db/utils.ts b/packages/server/src/db/utils.ts index 13bf6eb3b5..ac5a6162b9 100644 --- a/packages/server/src/db/utils.ts +++ b/packages/server/src/db/utils.ts @@ -128,7 +128,7 @@ export function getLinkParams(otherProps: any = {}) { * Generates a new layout ID. * @returns {string} The new layout ID which the layout doc can be stored under. */ -export function generateLayoutID(id: string) { +export function generateLayoutID(id?: string) { return `${DocumentType.LAYOUT}${SEPARATOR}${id || newid()}` } diff --git a/packages/server/src/db/views/staticViews.js b/packages/server/src/db/views/staticViews.js index d715c02968..10ad8fd410 100644 --- a/packages/server/src/db/views/staticViews.js +++ b/packages/server/src/db/views/staticViews.js @@ -1,4 +1,4 @@ -const { getAppDB } = require("@budibase/backend-core/context") +const { context } = require("@budibase/backend-core") const { DocumentType, SEPARATOR, ViewName, SearchIndexes } = require("../utils") const SCREEN_PREFIX = DocumentType.SCREEN + SEPARATOR @@ -20,7 +20,7 @@ const SCREEN_PREFIX = DocumentType.SCREEN + SEPARATOR * so it may be slow. */ exports.createLinkView = async () => { - const db = getAppDB() + const db = context.getAppDB() const designDoc = await db.get("_design/database") const view = { map: function (doc) { @@ -54,7 +54,7 @@ exports.createLinkView = async () => { } exports.createRoutingView = async () => { - const db = getAppDB() + const db = context.getAppDB() const designDoc = await db.get("_design/database") const view = { // if using variables in a map function need to inject them before use @@ -75,7 +75,7 @@ exports.createRoutingView = async () => { } async function searchIndex(indexName, fnString) { - const db = getAppDB() + const db = context.getAppDB() const designDoc = await db.get("_design/database") designDoc.indexes = { [indexName]: { diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index f18e9d1d98..43ebc8b1dd 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -96,7 +96,7 @@ function generateSelectStatement( ): (string | Knex.Raw)[] { const { resource, meta } = json const schema = meta?.table?.schema - return resource.fields.map(field => { + return resource!.fields.map(field => { const fieldNames = field.split(/\./g) const tableName = fieldNames[0] const columnName = fieldNames[1] diff --git a/packages/server/src/integrations/base/sqlTable.ts b/packages/server/src/integrations/base/sqlTable.ts index 02f7ad8718..2ab610fecd 100644 --- a/packages/server/src/integrations/base/sqlTable.ts +++ b/packages/server/src/integrations/base/sqlTable.ts @@ -185,7 +185,7 @@ class SqlTableQueryBuilder { json.table, json.meta.tables, json.meta.table, - json.meta.renamed + json.meta.renamed! ) break case Operation.DELETE_TABLE: diff --git a/packages/server/src/integrations/index.ts b/packages/server/src/integrations/index.ts index c926aeb992..e6f5968e32 100644 --- a/packages/server/src/integrations/index.ts +++ b/packages/server/src/integrations/index.ts @@ -67,50 +67,54 @@ if ( INTEGRATIONS[SourceName.ORACLE] = oracle.integration } -module.exports = { - getDefinitions: async () => { - const pluginSchemas: { [key: string]: Integration } = {} - if (environment.SELF_HOSTED) { - const plugins = await getPlugins(PluginType.DATASOURCE) - // extract the actual schema from each custom - for (let plugin of plugins) { - const sourceId = plugin.name - pluginSchemas[sourceId] = { - ...plugin.schema["schema"], - custom: true, - } - if (plugin.iconUrl) { - pluginSchemas[sourceId].iconUrl = plugin.iconUrl - } +export async function getDefinitions() { + const pluginSchemas: { [key: string]: Integration } = {} + if (environment.SELF_HOSTED) { + const plugins = await getPlugins(PluginType.DATASOURCE) + // extract the actual schema from each custom + for (let plugin of plugins) { + const sourceId = plugin.name + pluginSchemas[sourceId] = { + ...plugin.schema["schema"], + custom: true, + } + if (plugin.iconUrl) { + pluginSchemas[sourceId].iconUrl = plugin.iconUrl } } - return { - ...cloneDeep(DEFINITIONS), - ...pluginSchemas, - } - }, - getIntegration: async (integration: string) => { - if (INTEGRATIONS[integration]) { - return INTEGRATIONS[integration] - } - if (environment.SELF_HOSTED) { - const plugins = await getPlugins(PluginType.DATASOURCE) - for (let plugin of plugins) { - if (plugin.name === integration) { - // need to use commonJS require due to its dynamic runtime nature - const retrieved: any = await getDatasourcePlugin( - plugin.name, - plugin.jsUrl, - plugin.schema?.hash - ) - if (retrieved.integration) { - return retrieved.integration - } else { - return retrieved - } - } - } - } - throw new Error("No datasource implementation found.") - }, + } + return { + ...cloneDeep(DEFINITIONS), + ...pluginSchemas, + } +} + +export async function getIntegration(integration: string) { + if (INTEGRATIONS[integration]) { + return INTEGRATIONS[integration] + } + if (environment.SELF_HOSTED) { + const plugins = await getPlugins(PluginType.DATASOURCE) + for (let plugin of plugins) { + if (plugin.name === integration) { + // need to use commonJS require due to its dynamic runtime nature + const retrieved: any = await getDatasourcePlugin( + plugin.name, + plugin.jsUrl, + plugin.schema?.hash + ) + if (retrieved.integration) { + return retrieved.integration + } else { + return retrieved + } + } + } + } + throw new Error("No datasource implementation found.") +} + +export default { + getDefinitions, + getIntegration, } diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index 9c870208a7..d47e5c42da 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -1,23 +1,13 @@ -import { - getUserRoleHierarchy, - getRequiredResourceRole, - BUILTIN_ROLE_IDS, -} from "@budibase/backend-core/roles" -const { - PermissionType, - PermissionLevel, - doesHaveBasePermission, -} = require("@budibase/backend-core/permissions") -const builderMiddleware = require("./builder") -const { isWebhookEndpoint } = require("./utils") -const { buildCsrfMiddleware } = require("@budibase/backend-core/auth") -const { getAppId } = require("@budibase/backend-core/context") +import { roles, permissions, auth, context } from "@budibase/backend-core" +import { Role } from "@budibase/types" +import builderMiddleware from "./builder" +import { isWebhookEndpoint } from "./utils" function hasResource(ctx: any) { return ctx.resourceId != null } -const csrf = buildCsrfMiddleware() +const csrf = auth.buildCsrfMiddleware() /** * Apply authorization to the requested resource: @@ -33,7 +23,7 @@ const checkAuthorized = async ( ) => { // check if this is a builder api and the user is not a builder const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global - const isBuilderApi = permType === PermissionType.BUILDER + const isBuilderApi = permType === permissions.PermissionType.BUILDER if (isBuilderApi && !isBuilder) { return ctx.throw(403, "Not Authorized") } @@ -51,10 +41,10 @@ const checkAuthorizedResource = async ( permLevel: any ) => { // get the user's roles - const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC - const userRoles = (await getUserRoleHierarchy(roleId, { + const roleId = ctx.roleId || roles.BUILTIN_ROLE_IDS.PUBLIC + const userRoles = (await roles.getUserRoleHierarchy(roleId, { idOnly: false, - })) as { _id: string }[] + })) as Role[] const permError = "User does not have permission" // check if the user has the required role if (resourceRoles.length > 0) { @@ -66,7 +56,9 @@ const checkAuthorizedResource = async ( ctx.throw(403, permError) } // fallback to the base permissions when no resource roles are found - } else if (!doesHaveBasePermission(permType, permLevel, userRoles)) { + } else if ( + !permissions.doesHaveBasePermission(permType, permLevel, userRoles) + ) { ctx.throw(403, permError) } } @@ -91,21 +83,22 @@ export = (permType: any, permLevel: any = null, opts = { schema: false }) => let resourceRoles: any = [] let otherLevelRoles: any = [] const otherLevel = - permLevel === PermissionLevel.READ - ? PermissionLevel.WRITE - : PermissionLevel.READ - const appId = getAppId() + permLevel === permissions.PermissionLevel.READ + ? permissions.PermissionLevel.WRITE + : permissions.PermissionLevel.READ + const appId = context.getAppId() if (appId && hasResource(ctx)) { - resourceRoles = await getRequiredResourceRole(permLevel, ctx) + resourceRoles = await roles.getRequiredResourceRole(permLevel, ctx) if (opts && opts.schema) { - otherLevelRoles = await getRequiredResourceRole(otherLevel, ctx) + otherLevelRoles = await roles.getRequiredResourceRole(otherLevel, ctx) } } // if the resource is public, proceed if ( - resourceRoles.includes(BUILTIN_ROLE_IDS.PUBLIC) || - (otherLevelRoles && otherLevelRoles.includes(BUILTIN_ROLE_IDS.PUBLIC)) + resourceRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC) || + (otherLevelRoles && + otherLevelRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC)) ) { return next() } diff --git a/packages/server/src/middleware/tests/authorized.spec.js b/packages/server/src/middleware/tests/authorized.spec.js index c64f758749..85697b6052 100644 --- a/packages/server/src/middleware/tests/authorized.spec.js +++ b/packages/server/src/middleware/tests/authorized.spec.js @@ -9,8 +9,7 @@ jest.mock("../../environment", () => ({ ) const authorizedMiddleware = require("../authorized") const env = require("../../environment") -const { PermissionType, PermissionLevel } = require("@budibase/backend-core/permissions") -const { doInAppContext } = require("@budibase/backend-core/context") +const { permissions } = require("@budibase/backend-core") const APP_ID = "" @@ -113,7 +112,7 @@ describe("Authorization middleware", () => { it("throws if the user does not have builder permissions", async () => { config.setEnvironment(false) - config.setMiddlewareRequiredPermission(PermissionType.BUILDER) + config.setMiddlewareRequiredPermission(permissions.PermissionType.BUILDER) config.setUser({ role: { _id: "" @@ -125,13 +124,13 @@ describe("Authorization middleware", () => { }) it("passes on to next() middleware if the user has resource permission", async () => { - config.setResourceId(PermissionType.QUERY) + config.setResourceId(permissions.PermissionType.QUERY) config.setUser({ role: { _id: "" } }) - config.setMiddlewareRequiredPermission(PermissionType.QUERY) + config.setMiddlewareRequiredPermission(permissions.PermissionType.QUERY) await config.executeMiddleware() expect(config.next).toHaveBeenCalled() @@ -155,7 +154,7 @@ describe("Authorization middleware", () => { _id: "" }, }) - config.setMiddlewareRequiredPermission(PermissionType.ADMIN, PermissionLevel.BASIC) + config.setMiddlewareRequiredPermission(permissions.PermissionType.ADMIN, permissions.PermissionLevel.BASIC) await config.executeMiddleware() expect(config.throw).toHaveBeenCalledWith(403, "User does not have permission") diff --git a/packages/server/src/migrations/functions/appUrls.ts b/packages/server/src/migrations/functions/appUrls.ts index 1b563bb932..4d5ce6c30c 100644 --- a/packages/server/src/migrations/functions/appUrls.ts +++ b/packages/server/src/migrations/functions/appUrls.ts @@ -1,4 +1,4 @@ -const { DocumentType } = require("@budibase/backend-core/db") +import { db as dbCore } from "@budibase/backend-core" import { getAppUrl } from "../../api/controllers/application" /** @@ -11,7 +11,7 @@ import { getAppUrl } from "../../api/controllers/application" export const run = async (appDb: any) => { let metadata try { - metadata = await appDb.get(DocumentType.APP_METADATA) + metadata = await appDb.get(dbCore.DocumentType.APP_METADATA) } catch (e) { // sometimes the metadata document doesn't exist // exit early instead of failing the migration diff --git a/packages/server/src/migrations/functions/tests/appUrls.spec.js b/packages/server/src/migrations/functions/tests/appUrls.spec.js index 4551bac160..dafe67712f 100644 --- a/packages/server/src/migrations/functions/tests/appUrls.spec.js +++ b/packages/server/src/migrations/functions/tests/appUrls.spec.js @@ -1,4 +1,4 @@ -const { DocumentType, doWithDB } = require("@budibase/backend-core/db") +const { db: dbCore } = require("@budibase/backend-core") const TestConfig = require("../../../tests/utilities/TestConfiguration") const migration = require("../appUrls") @@ -14,12 +14,12 @@ describe("run", () => { it("runs successfully", async () => { const app = await config.createApp("testApp") - const metadata = await doWithDB(app.appId, async db => { - const metadataDoc = await db.get( DocumentType.APP_METADATA) + const metadata = await dbCore.doWithDB(app.appId, async db => { + const metadataDoc = await db.get(dbCore.DocumentType.APP_METADATA) delete metadataDoc.url await db.put(metadataDoc) await migration.run(db) - return await db.get( DocumentType.APP_METADATA) + return await db.get(dbCore.DocumentType.APP_METADATA) }) expect(metadata.url).toEqual("/testapp") }) diff --git a/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js b/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js index 3c8894f8e5..e6e6725f07 100644 --- a/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js +++ b/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js @@ -1,11 +1,16 @@ -jest.mock("@budibase/backend-core/db", () => ({ - ...jest.requireActual("@budibase/backend-core/db"), - createNewUserEmailView: jest.fn(), -})) -const coreDb = require("@budibase/backend-core/db") +jest.mock("@budibase/backend-core", () => { + const core = jest.requireActual("@budibase/backend-core") + return { + ...core, + db: { + ...core.db, + createNewUserEmailView: jest.fn() + } + } +}) +const { tenancy, db: dbCore } = require("@budibase/backend-core") const TestConfig = require("../../../tests/utilities/TestConfiguration") const { TENANT_ID } = require("../../../tests/utilities/structures") -const { getGlobalDB, doInTenant } = require("@budibase/backend-core/tenancy") // mock email view creation @@ -21,10 +26,10 @@ describe("run", () => { afterAll(config.end) it("runs successfully", async () => { - await doInTenant(TENANT_ID, async () => { - const globalDb = getGlobalDB() + await tenancy.doInTenant(TENANT_ID, async () => { + const globalDb = tenancy.getGlobalDB() await migration.run(globalDb) - expect(coreDb.createNewUserEmailView).toHaveBeenCalledTimes(1) + expect(dbCore.createNewUserEmailView).toHaveBeenCalledTimes(1) }) }) }) diff --git a/packages/server/src/migrations/functions/usageQuotas/syncApps.ts b/packages/server/src/migrations/functions/usageQuotas/syncApps.ts index 76e2dbdacd..351154dccc 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncApps.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncApps.ts @@ -1,15 +1,14 @@ -import { getTenantId } from "@budibase/backend-core/tenancy" -import { getAllApps } from "@budibase/backend-core/db" +import { tenancy, db as dbCore } from "@budibase/backend-core" import { quotas } from "@budibase/pro" import { QuotaUsageType, StaticQuotaName } from "@budibase/types" export const run = async () => { // get app count - const devApps = await getAllApps({ dev: true }) + const devApps = await dbCore.getAllApps({ dev: true }) const appCount = devApps ? devApps.length : 0 // sync app count - const tenantId = getTenantId() + const tenantId = tenancy.getTenantId() console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`) await quotas.setUsage(appCount, StaticQuotaName.APPS, QuotaUsageType.STATIC) } diff --git a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts index e5c8a1743c..92574d1f27 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts @@ -1,12 +1,11 @@ -import { getTenantId } from "@budibase/backend-core/tenancy" -import { getAllApps } from "@budibase/backend-core/db" +import { tenancy, db as dbCore } from "@budibase/backend-core" import { getUniqueRows } from "../../../utilities/usageQuota/rows" import { quotas } from "@budibase/pro" import { StaticQuotaName, QuotaUsageType, App } from "@budibase/types" export const run = async () => { // get all rows in all apps - const allApps = (await getAllApps({ all: true })) as App[] + const allApps = (await dbCore.getAllApps({ all: true })) as App[] const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] const { appRows } = await getUniqueRows(appIds) @@ -19,7 +18,7 @@ export const run = async () => { }) // sync row count - const tenantId = getTenantId() + const tenantId = tenancy.getTenantId() console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`) await quotas.setUsagePerApp( counts, diff --git a/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts b/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts index b0d429fbe8..a5cb203e0f 100644 --- a/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +++ b/packages/server/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts @@ -2,7 +2,7 @@ import TestConfig from "../../../../tests/utilities/TestConfiguration" import * as syncRows from "../syncRows" import { quotas } from "@budibase/pro" import { QuotaUsageType, StaticQuotaName } from "@budibase/types" -const { getProdAppID } = require("@budibase/backend-core/db") +const { db: dbCore } = require("@budibase/backend-core") describe("syncRows", () => { let config = new TestConfig(false) @@ -38,12 +38,12 @@ describe("syncRows", () => { // assert the migration worked usageDoc = await quotas.getQuotaUsage() expect(usageDoc.usageQuota.rows).toEqual(3) - expect(usageDoc.apps?.[getProdAppID(app1.appId)].usageQuota.rows).toEqual( - 1 - ) - expect(usageDoc.apps?.[getProdAppID(app2.appId)].usageQuota.rows).toEqual( - 2 - ) + expect( + usageDoc.apps?.[dbCore.getProdAppID(app1.appId)].usageQuota.rows + ).toEqual(1) + expect( + usageDoc.apps?.[dbCore.getProdAppID(app2.appId)].usageQuota.rows + ).toEqual(2) }) }) }) diff --git a/packages/server/src/migrations/functions/userEmailViewCasing.ts b/packages/server/src/migrations/functions/userEmailViewCasing.ts index fe58692a27..078289cddf 100644 --- a/packages/server/src/migrations/functions/userEmailViewCasing.ts +++ b/packages/server/src/migrations/functions/userEmailViewCasing.ts @@ -1,4 +1,4 @@ -const { createNewUserEmailView } = require("@budibase/backend-core/db") +import { db as dbCore } from "@budibase/backend-core" /** * Date: @@ -8,6 +8,6 @@ const { createNewUserEmailView } = require("@budibase/backend-core/db") * Recreate the user email view to include latest changes i.e. lower casing the email address */ -export const run = async (db: any) => { - await createNewUserEmailView(db) +export const run = async () => { + await dbCore.createNewUserEmailView() } diff --git a/packages/server/src/module.d.ts b/packages/server/src/module.d.ts deleted file mode 100644 index ada45a3a66..0000000000 --- a/packages/server/src/module.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -declare module "@budibase/backend-core/tenancy" -declare module "@budibase/backend-core/db" -declare module "@budibase/backend-core/context" -declare module "@budibase/backend-core/cache" -declare module "@budibase/backend-core/permissions" -declare module "@budibase/backend-core/roles" -declare module "@budibase/backend-core/constants" -declare module "@budibase/backend-core/auth" -declare module "@budibase/backend-core/sessions" -declare module "@budibase/backend-core/encryption" -declare module "@budibase/backend-core/utils" -declare module "@budibase/backend-core/redis" -declare module "@budibase/backend-core/objectStore" -declare module "@budibase/backend-core/plugins" diff --git a/packages/server/src/sdk/app/backups/exports.ts b/packages/server/src/sdk/app/backups/exports.ts index 8de51ed1e6..9acad1344c 100644 --- a/packages/server/src/sdk/app/backups/exports.ts +++ b/packages/server/src/sdk/app/backups/exports.ts @@ -1,6 +1,5 @@ -import { db as dbCore } from "@budibase/backend-core" +import { db as dbCore, objectStore } from "@budibase/backend-core" import { budibaseTempDir } from "../../../utilities/budibaseDir" -import { retrieveDirectory } from "../../../utilities/fileSystem/utilities" import { streamFile, createTempFolder } from "../../../utilities/fileSystem" import { ObjectStoreBuckets } from "../../../constants" import { @@ -88,7 +87,10 @@ export async function exportApp(appId: string, config?: ExportOpts) { // export bucket contents let tmpPath if (!env.isTest()) { - tmpPath = await retrieveDirectory(ObjectStoreBuckets.APPS, appPath) + tmpPath = await objectStore.retrieveDirectory( + ObjectStoreBuckets.APPS, + appPath + ) } else { tmpPath = createTempFolder(uuid()) } diff --git a/packages/server/src/sdk/app/backups/imports.ts b/packages/server/src/sdk/app/backups/imports.ts index 1a745599f7..4c2a721c2c 100644 --- a/packages/server/src/sdk/app/backups/imports.ts +++ b/packages/server/src/sdk/app/backups/imports.ts @@ -1,12 +1,8 @@ -import { db as dbCore } from "@budibase/backend-core" +import { db as dbCore, objectStore } from "@budibase/backend-core" import { Database } from "@budibase/types" import { getAutomationParams, TABLE_ROW_PREFIX } from "../../../db/utils" import { budibaseTempDir } from "../../../utilities/budibaseDir" import { DB_EXPORT_FILE, GLOBAL_DB_EXPORT_FILE } from "./constants" -import { - upload, - uploadDirectory, -} from "../../../utilities/fileSystem/utilities" import { downloadTemplate } from "../../../utilities/fileSystem" import { FieldTypes, ObjectStoreBuckets } from "../../../constants" import { join } from "path" @@ -174,11 +170,11 @@ export async function importApp( filename = join(prodAppId, filename) if (fs.lstatSync(path).isDirectory()) { promises.push( - uploadDirectory(ObjectStoreBuckets.APPS, path, filename) + objectStore.uploadDirectory(ObjectStoreBuckets.APPS, path, filename) ) } else { promises.push( - upload({ + objectStore.upload({ bucket: ObjectStoreBuckets.APPS, path, filename, diff --git a/packages/server/src/startup.ts b/packages/server/src/startup.ts index 7d3302655a..53fcf3ebef 100644 --- a/packages/server/src/startup.ts +++ b/packages/server/src/startup.ts @@ -1,5 +1,5 @@ import * as env from "./environment" -import redis from "./utilities/redis" +import * as redis from "./utilities/redis" import { createAdminUser, generateApiKey, @@ -19,7 +19,7 @@ import eventEmitter from "./events" import * as migrations from "./migrations" import bullboard from "./automations/bullboard" import * as pro from "@budibase/pro" -import api from "./api" +import * as api from "./api" import sdk from "./sdk" const pino = require("koa-pino-logger") diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 044ad4bbf7..76c79b838f 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -1,5 +1,4 @@ require("../../db").init() -const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const env = require("../../environment") const { basicTable, @@ -13,18 +12,21 @@ const { basicWebhook, TENANT_ID, } = require("./structures") +const { + constants, + tenancy, + sessions, + cache, + context, + db: dbCore, + encryption, + auth, + roles, +} = require("@budibase/backend-core") const controllers = require("./controllers") const supertest = require("supertest") const { cleanup } = require("../../utilities/fileSystem") -const { Cookie, Header } = require("@budibase/backend-core/constants") -const { jwt } = require("@budibase/backend-core/auth") -const { doInTenant, doWithGlobalDB } = require("@budibase/backend-core/tenancy") -const { createASession } = require("@budibase/backend-core/sessions") -const { user: userCache } = require("@budibase/backend-core/cache") const newid = require("../../db/newid") -const context = require("@budibase/backend-core/context") -const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db") -const { encrypt } = require("@budibase/backend-core/encryption") const { DocumentType, generateUserMetadataID } = require("../../db/utils") const { startup } = require("../../startup") @@ -83,7 +85,7 @@ class TestConfiguration { if (!appId) { appId = this.appId } - return doInTenant(TENANT_ID, () => { + return tenancy.doInTenant(TENANT_ID, () => { // check if already in a context if (context.getAppId() == null && appId !== null) { return context.doInAppContext(appId, async () => { @@ -155,7 +157,7 @@ class TestConfiguration { email = EMAIL, roles, } = {}) { - return doWithGlobalDB(TENANT_ID, async db => { + return tenancy.doWithGlobalDB(TENANT_ID, async db => { let existing try { existing = await db.get(id) @@ -170,7 +172,7 @@ class TestConfiguration { firstName, lastName, } - await createASession(id, { + await sessions.createASession(id, { sessionId: "sessionid", tenantId: TENANT_ID, csrfToken: CSRF_TOKEN, @@ -212,7 +214,7 @@ class TestConfiguration { admin, roles, }) - await userCache.invalidateUser(globalId) + await cache.user.invalidateUser(globalId) return { ...resp, globalId, @@ -227,19 +229,19 @@ class TestConfiguration { throw "Server has not been opened, cannot login." } // make sure the user exists in the global DB - if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) { + if (roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) { await this.globalUser({ id: userId, builder, roles: { [this.prodAppId]: roleId }, }) } - await createASession(userId, { + await sessions.createASession(userId, { sessionId: "sessionid", tenantId: TENANT_ID, }) // have to fake this - const auth = { + const authObj = { userId, sessionId: "sessionid", tenantId: TENANT_ID, @@ -248,45 +250,45 @@ class TestConfiguration { roleId: roleId, appId, } - const authToken = jwt.sign(auth, env.JWT_SECRET) - const appToken = jwt.sign(app, env.JWT_SECRET) + const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) + const appToken = auth.jwt.sign(app, env.JWT_SECRET) // returning necessary request headers - await userCache.invalidateUser(userId) + await cache.user.invalidateUser(userId) return { Accept: "application/json", Cookie: [ - `${Cookie.Auth}=${authToken}`, - `${Cookie.CurrentApp}=${appToken}`, + `${constants.Cookie.Auth}=${authToken}`, + `${constants.Cookie.CurrentApp}=${appToken}`, ], - [Header.APP_ID]: appId, + [constants.Header.APP_ID]: appId, } }) } defaultHeaders(extras = {}) { - const auth = { + const authObj = { userId: GLOBAL_USER_ID, sessionId: "sessionid", tenantId: TENANT_ID, } const app = { - roleId: BUILTIN_ROLE_IDS.ADMIN, + roleId: roles.BUILTIN_ROLE_IDS.ADMIN, appId: this.appId, } - const authToken = jwt.sign(auth, env.JWT_SECRET) - const appToken = jwt.sign(app, env.JWT_SECRET) + const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) + const appToken = auth.jwt.sign(app, env.JWT_SECRET) const headers = { Accept: "application/json", Cookie: [ - `${Cookie.Auth}=${authToken}`, - `${Cookie.CurrentApp}=${appToken}`, + `${constants.Cookie.Auth}=${authToken}`, + `${constants.Cookie.CurrentApp}=${appToken}`, ], - [Header.CSRF_TOKEN]: CSRF_TOKEN, + [constants.Header.CSRF_TOKEN]: CSRF_TOKEN, ...extras, } if (this.appId) { - headers[Header.APP_ID] = this.appId + headers[constants.Header.APP_ID] = this.appId } return headers } @@ -298,14 +300,14 @@ class TestConfiguration { Accept: "application/json", } if (appId) { - headers[Header.APP_ID] = appId + headers[constants.Header.APP_ID] = appId } return headers } async roleHeaders({ email = EMAIL, - roleId = BUILTIN_ROLE_IDS.ADMIN, + roleId = roles.BUILTIN_ROLE_IDS.ADMIN, builder = false, prodApp = true, } = {}) { @@ -315,15 +317,17 @@ class TestConfiguration { // API async generateApiKey(userId = GLOBAL_USER_ID) { - return doWithGlobalDB(TENANT_ID, async db => { - const id = generateDevInfoID(userId) + return tenancy.doWithGlobalDB(TENANT_ID, async db => { + const id = dbCore.generateDevInfoID(userId) let devInfo try { devInfo = await db.get(id) } catch (err) { devInfo = { _id: id, userId } } - devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`) + devInfo.apiKey = encryption.encrypt( + `${TENANT_ID}${dbCore.SEPARATOR}${newid()}` + ) await db.put(devInfo) return devInfo.apiKey }) diff --git a/packages/server/src/tests/utilities/structures.js b/packages/server/src/tests/utilities/structures.js index 39361c5d32..610084b04e 100644 --- a/packages/server/src/tests/utilities/structures.js +++ b/packages/server/src/tests/utilities/structures.js @@ -1,5 +1,4 @@ -const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") -const { BuiltinPermissionID } = require("@budibase/backend-core/permissions") +const { roles, permissions } = require("@budibase/backend-core") const { createHomeScreen } = require("../../constants/screens") const { EMPTY_LAYOUT } = require("../../constants/layouts") const { cloneDeep } = require("lodash/fp") @@ -134,8 +133,8 @@ exports.basicLinkedRow = (tableId, linkedRowId, linkField = "link") => { exports.basicRole = () => { return { name: "NewRole", - inherits: BUILTIN_ROLE_IDS.BASIC, - permissionId: BuiltinPermissionID.READ_ONLY, + inherits: roles.BUILTIN_ROLE_IDS.BASIC, + permissionId: permissions.BuiltinPermissionID.READ_ONLY, } } diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 3552ffa0f3..5e2817ed06 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -23,15 +23,14 @@ import { AutomationMetadata, } from "../definitions/automations" import { WorkerCallback } from "./definitions" -const { doInAppContext, getAppDB } = require("@budibase/backend-core/context") -const { logAlertWithInfo, logWarn } = require("@budibase/backend-core/logging") -const { processObject } = require("@budibase/string-templates") +import { context, logging } from "@budibase/backend-core" +import { processObject } from "@budibase/string-templates" +import { cloneDeep } from "lodash/fp" +import env from "../environment" const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId const LOOP_STEP_ID = actions.ACTION_DEFINITIONS.LOOP.stepId const CRON_STEP_ID = triggerDefs.CRON.stepId const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED } -const { cloneDeep } = require("lodash/fp") -const env = require("../environment") function typecastForLooping(loopStep: LoopStep, input: LoopInput) { if (!input || !input.binding) { @@ -121,7 +120,7 @@ class Orchestrator { async getMetadata(): Promise { const metadataId = generateAutomationMetadataID(this._automation._id!) - const db = getAppDB() + const db = context.getAppDB() let metadata: AutomationMetadata try { metadata = await db.get(metadataId) @@ -138,7 +137,7 @@ class Orchestrator { if (!this._job.opts.repeat) { return } - logWarn( + logging.logWarn( `CRON disabled reason=${reason} - ${this._appId}/${this._automation._id}` ) const automation = this._automation @@ -184,14 +183,14 @@ class Orchestrator { } else { metadata.errorCount = 0 } - const db = getAppDB() + const db = context.getAppDB() try { await db.put(metadata) } catch (err) { - logAlertWithInfo( + logging.logAlertWithInfo( "Failed to write automation metadata", db.name, - automation._id, + automation._id!, err ) } @@ -265,7 +264,7 @@ class Orchestrator { for (let step of automation.definition.steps) { stepCount++ - let input, + let input: any, iterations = 1, iterationCount = 0 @@ -283,7 +282,7 @@ class Orchestrator { let originalStepInput = cloneDeep(step.inputs) // Handle if the user has set a max iteration count or if it reaches the max limit set by us if (loopStep && input.binding) { - let newInput = await processObject( + let newInput: any = await processObject( loopStep.inputs, cloneDeep(this._context) ) @@ -352,7 +351,7 @@ class Orchestrator { } } if ( - index === env.AUTOMATION_MAX_ITERATION || + index === env.AUTOMATION_MAX_ITERATIONS || index === parseInt(loopStep.inputs.iterations) ) { this.updateContextAndOutput(loopStepNumber, step, tempOutput, { @@ -481,7 +480,7 @@ export function execute(job: Job, callback: WorkerCallback) { if (!appId) { throw new Error("Unable to execute, event doesn't contain app ID.") } - doInAppContext(appId, async () => { + return context.doInAppContext(appId, async () => { const automationOrchestrator = new Orchestrator(job) try { const response = await automationOrchestrator.execute() @@ -497,7 +496,7 @@ export const removeStalled = async (job: Job) => { if (!appId) { throw new Error("Unable to execute, event doesn't contain app ID.") } - await doInAppContext(appId, async () => { + await context.doInAppContext(appId, async () => { const automationOrchestrator = new Orchestrator(job) await automationOrchestrator.stopCron("stalled") }) diff --git a/packages/server/src/threads/query.ts b/packages/server/src/threads/query.ts index 86cbf89c87..8ee8bcb463 100644 --- a/packages/server/src/threads/query.ts +++ b/packages/server/src/threads/query.ts @@ -1,17 +1,12 @@ import { default as threadUtils } from "./utils" threadUtils.threadSetup() import { WorkerCallback, QueryEvent, QueryVariable } from "./definitions" -const ScriptRunner = require("../utilities/scriptRunner") -const { getIntegration } = require("../integrations") -const { processStringSync } = require("@budibase/string-templates") -const { doInAppContext, getAppDB } = require("@budibase/backend-core/context") -const { - refreshOAuthToken, - updateUserOAuth, -} = require("@budibase/backend-core/auth") -const { user: userCache } = require("@budibase/backend-core/cache") -const { getGlobalIDFromUserMetadataID } = require("../db/utils") -const { cloneDeep } = require("lodash/fp") +import ScriptRunner from "../utilities/scriptRunner" +import { getIntegration } from "../integrations" +import { processStringSync } from "@budibase/string-templates" +import { context, cache, auth } from "@budibase/backend-core" +import { getGlobalIDFromUserMetadataID } from "../db/utils" +import { cloneDeep } from "lodash/fp" const { isSQL } = require("../integrations/utils") const { @@ -170,7 +165,7 @@ class QueryRunner { } async runAnotherQuery(queryId: string, parameters: any) { - const db = getAppDB() + const db = context.getAppDB() const query = await db.get(queryId) const datasource = await db.get(query.datasourceId) return new QueryRunner( @@ -194,7 +189,7 @@ class QueryRunner { throw new Error("No refresh token found for authenticated user") } - const resp = await refreshOAuthToken( + const resp: any = await auth.refreshOAuthToken( oauth2.refreshToken, providerType, configId @@ -204,8 +199,8 @@ class QueryRunner { // There are several other properties available in 'resp' if (!resp.err) { const globalUserId = getGlobalIDFromUserMetadataID(_id) - await updateUserOAuth(globalUserId, resp) - this.ctx.user = await userCache.getUser(globalUserId) + await auth.updateUserOAuth(globalUserId, resp) + this.ctx.user = await cache.user.getUser(globalUserId) } else { // In this event the user may have oAuth issues that // could require re-authenticating with their provider. @@ -285,7 +280,7 @@ class QueryRunner { } export function execute(input: QueryEvent, callback: WorkerCallback) { - doInAppContext(input.appId, async () => { + context.doInAppContext(input.appId!, async () => { const Runner = new QueryRunner(input) try { const response = await Runner.execute() diff --git a/packages/server/src/threads/utils.ts b/packages/server/src/threads/utils.ts index a51c861b00..53120160a8 100644 --- a/packages/server/src/threads/utils.ts +++ b/packages/server/src/threads/utils.ts @@ -1,8 +1,7 @@ import { QueryVariable } from "./definitions" -const env = require("../environment") -const db = require("../db") -const redis = require("@budibase/backend-core/redis") -const { SEPARATOR } = require("@budibase/backend-core/db") +import env from "../environment" +import db from "../db" +import { redis, db as dbCore } from "@budibase/backend-core" const VARIABLE_TTL_SECONDS = 3600 let client: any @@ -21,7 +20,7 @@ process.on("exit", async () => { }) function makeVariableKey(queryId: string, variable: string) { - return `${queryId}${SEPARATOR}${variable}` + return `${queryId}${dbCore.SEPARATOR}${variable}` } export function threadSetup() { diff --git a/packages/server/src/utilities/budibaseDir.js b/packages/server/src/utilities/budibaseDir.js deleted file mode 100644 index dc992aaa8c..0000000000 --- a/packages/server/src/utilities/budibaseDir.js +++ /dev/null @@ -1,3 +0,0 @@ -const { budibaseTempDir } = require("@budibase/backend-core/objectStore") - -module.exports.budibaseTempDir = budibaseTempDir diff --git a/packages/server/src/utilities/budibaseDir.ts b/packages/server/src/utilities/budibaseDir.ts new file mode 100644 index 0000000000..bee92ab7b4 --- /dev/null +++ b/packages/server/src/utilities/budibaseDir.ts @@ -0,0 +1,3 @@ +import { objectStore } from "@budibase/backend-core" + +export const budibaseTempDir = objectStore.budibaseTempDir diff --git a/packages/server/src/utilities/fileSystem/clientLibrary.js b/packages/server/src/utilities/fileSystem/clientLibrary.js index 4d3ad551cd..37faa4256f 100644 --- a/packages/server/src/utilities/fileSystem/clientLibrary.js +++ b/packages/server/src/utilities/fileSystem/clientLibrary.js @@ -1,7 +1,7 @@ const { join } = require("path") const { ObjectStoreBuckets } = require("../../constants") const fs = require("fs") -const { upload, retrieveToTmp, streamUpload } = require("./utilities") +const { objectStore } = require("@budibase/backend-core") const { resolve } = require("../centralPath") const env = require("../../environment") const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..") @@ -38,13 +38,13 @@ exports.backupClientLibrary = async appId => { let tmpManifestPath try { // Try to load the manifest from the new file location - tmpManifestPath = await retrieveToTmp( + tmpManifestPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "manifest.json") ) } catch (error) { // Fallback to loading it from the old location for old apps - tmpManifestPath = await retrieveToTmp( + tmpManifestPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join( appId, @@ -58,19 +58,19 @@ exports.backupClientLibrary = async appId => { } // Copy existing client lib to tmp - const tmpClientPath = await retrieveToTmp( + const tmpClientPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "budibase-client.js") ) // Upload manifest and client library as backups - const manifestUpload = upload({ + const manifestUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "manifest.json.bak"), path: tmpManifestPath, type: "application/json", }) - const clientUpload = upload({ + const clientUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "budibase-client.js.bak"), path: tmpClientPath, @@ -99,7 +99,7 @@ exports.updateClientLibrary = async appId => { } // Upload latest manifest and client library - const manifestUpload = streamUpload( + const manifestUpload = objectStore.streamUpload( ObjectStoreBuckets.APPS, join(appId, "manifest.json"), fs.createReadStream(manifest), @@ -107,7 +107,7 @@ exports.updateClientLibrary = async appId => { ContentType: "application/json", } ) - const clientUpload = streamUpload( + const clientUpload = objectStore.streamUpload( ObjectStoreBuckets.APPS, join(appId, "budibase-client.js"), fs.createReadStream(client), @@ -126,25 +126,25 @@ exports.updateClientLibrary = async appId => { */ exports.revertClientLibrary = async appId => { // Copy backups manifest to tmp directory - const tmpManifestPath = await retrieveToTmp( + const tmpManifestPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "manifest.json.bak") ) // Copy backup client lib to tmp - const tmpClientPath = await retrieveToTmp( + const tmpClientPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "budibase-client.js.bak") ) // Upload backups as new versions - const manifestUpload = upload({ + const manifestUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "manifest.json"), path: tmpManifestPath, type: "application/json", }) - const clientUpload = upload({ + const clientUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "budibase-client.js"), path: tmpClientPath, diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index 1eb8a481e5..5b46565897 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -2,7 +2,13 @@ const { budibaseTempDir } = require("../budibaseDir") const fs = require("fs") const { join } = require("path") const uuid = require("uuid/v4") +const { context, objectStore } = require("@budibase/backend-core") const { ObjectStoreBuckets } = require("../../constants") +const { updateClientLibrary } = require("./clientLibrary") +const { checkSlashesInUrl } = require("../") +const env = require("../../environment") +const tar = require("tar") +const fetch = require("node-fetch") const { upload, retrieve, @@ -11,13 +17,7 @@ const { downloadTarball, downloadTarballDirect, deleteFiles, -} = require("./utilities") -const { updateClientLibrary } = require("./clientLibrary") -const { checkSlashesInUrl } = require("../") -const env = require("../../environment") -const { getAppId } = require("@budibase/backend-core/context") -const tar = require("tar") -const fetch = require("node-fetch") +} = objectStore const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..") const NODE_MODULES_PATH = join(TOP_LEVEL_PATH, "node_modules") @@ -165,7 +165,7 @@ exports.downloadTemplate = async (type, name) => { * Retrieves component libraries from object store (or tmp symlink if in local) */ exports.getComponentLibraryManifest = async library => { - const appId = getAppId() + const appId = context.getAppId() const filename = "manifest.json" /* istanbul ignore next */ // when testing in cypress and so on we need to get the package diff --git a/packages/server/src/utilities/fileSystem/utilities.js b/packages/server/src/utilities/fileSystem/utilities.js deleted file mode 100644 index 01ba58f5bc..0000000000 --- a/packages/server/src/utilities/fileSystem/utilities.js +++ /dev/null @@ -1,36 +0,0 @@ -const { - ObjectStore, - makeSureBucketExists, - upload, - deleteFiles, - streamUpload, - retrieve, - retrieveToTmp, - retrieveDirectory, - deleteFolder, - uploadDirectory, - downloadTarball, - downloadTarballDirect, -} = require("@budibase/backend-core/objectStore") - -/*********************************** - * NOTE * - * This file purely exists so that * - * the object store functionality * - * can easily be mocked out of * - * the server without mocking the * - * entire core library. * - ***********************************/ - -exports.ObjectStore = ObjectStore -exports.makeSureBucketExists = makeSureBucketExists -exports.upload = upload -exports.streamUpload = streamUpload -exports.retrieve = retrieve -exports.retrieveToTmp = retrieveToTmp -exports.retrieveDirectory = retrieveDirectory -exports.deleteFolder = deleteFolder -exports.uploadDirectory = uploadDirectory -exports.downloadTarball = downloadTarball -exports.downloadTarballDirect = downloadTarballDirect -exports.deleteFiles = deleteFiles diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.ts similarity index 67% rename from packages/server/src/utilities/index.js rename to packages/server/src/utilities/index.ts index 9b51e0e628..4e672124cd 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.ts @@ -1,17 +1,20 @@ -const env = require("../environment") -const { OBJ_STORE_DIRECTORY } = require("../constants") -const { sanitizeKey } = require("@budibase/backend-core/objectStore") -const { generateMetadataID } = require("../db/utils") -const Readable = require("stream").Readable -const { getAppDB } = require("@budibase/backend-core/context") +import env from "../environment" +import { OBJ_STORE_DIRECTORY } from "../constants" +import { objectStore, context } from "@budibase/backend-core" +import { generateMetadataID } from "../db/utils" +import { Document } from "@budibase/types" +import stream from "stream" +const Readable = stream.Readable -exports.wait = ms => new Promise(resolve => setTimeout(resolve, ms)) +export function wait(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)) +} -exports.isDev = env.isDev +export const isDev = env.isDev -exports.NUMBER_REGEX = /^[+-]?([0-9]*[.])?[0-9]+$/g +export const NUMBER_REGEX = /^[+-]?([0-9]*[.])?[0-9]+$/g -exports.removeFromArray = (array, element) => { +export function removeFromArray(array: any[], element: any) { const index = array.indexOf(element) if (index !== -1) { array.splice(index, 1) @@ -25,7 +28,7 @@ exports.removeFromArray = (array, element) => { * @param {string} url The URL to test and remove any extra double slashes. * @return {string} The updated url. */ -exports.checkSlashesInUrl = url => { +export function checkSlashesInUrl(url: string) { return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2") } @@ -33,7 +36,7 @@ exports.checkSlashesInUrl = url => { * Gets the address of the object store, depending on whether self hosted or in cloud. * @return {string} The base URL of the object store (MinIO or S3). */ -exports.objectStoreUrl = () => { +export function objectStoreUrl() { if (env.SELF_HOSTED || env.MINIO_URL) { // can use a relative url for this as all goes through the proxy (this is hosted in minio) return OBJ_STORE_DIRECTORY @@ -52,9 +55,9 @@ exports.objectStoreUrl = () => { * @return {string} The URL to be inserted into appPackage response or server rendered * app index file. */ -exports.clientLibraryPath = (appId, version) => { +export function clientLibraryPath(appId: string, version: string) { if (env.isProd()) { - let url = `${exports.objectStoreUrl()}/${sanitizeKey( + let url = `${objectStoreUrl()}/${objectStore.sanitizeKey( appId )}/budibase-client.js` @@ -68,18 +71,19 @@ exports.clientLibraryPath = (appId, version) => { } } -exports.attachmentsRelativeURL = attachmentKey => { - return exports.checkSlashesInUrl( - `${exports.objectStoreUrl()}/${attachmentKey}` - ) +export function attachmentsRelativeURL(attachmentKey: string) { + return checkSlashesInUrl(`${objectStoreUrl()}/${attachmentKey}`) } -exports.updateEntityMetadata = async (type, entityId, updateFn) => { - const db = getAppDB() +export async function updateEntityMetadata( + type: string, + entityId: string, + updateFn: any +) { + const db = context.getAppDB() const id = generateMetadataID(type, entityId) // read it to see if it exists, we'll overwrite it no matter what - let rev, - metadata = {} + let rev, metadata: Document try { const oldMetadata = await db.get(id) rev = oldMetadata._rev @@ -100,14 +104,18 @@ exports.updateEntityMetadata = async (type, entityId, updateFn) => { } } -exports.saveEntityMetadata = async (type, entityId, metadata) => { - return exports.updateEntityMetadata(type, entityId, () => { +export async function saveEntityMetadata( + type: string, + entityId: string, + metadata: Document +) { + return updateEntityMetadata(type, entityId, () => { return metadata }) } -exports.deleteEntityMetadata = async (type, entityId) => { - const db = getAppDB() +export async function deleteEntityMetadata(type: string, entityId: string) { + const db = context.getAppDB() const id = generateMetadataID(type, entityId) let rev try { @@ -123,7 +131,7 @@ exports.deleteEntityMetadata = async (type, entityId) => { } } -exports.escapeDangerousCharacters = string => { +export function escapeDangerousCharacters(string: string) { return string .replace(/[\\]/g, "\\\\") .replace(/[\b]/g, "\\b") @@ -133,7 +141,7 @@ exports.escapeDangerousCharacters = string => { .replace(/[\t]/g, "\\t") } -exports.stringToReadStream = string => { +export function stringToReadStream(string: string) { return new Readable({ read() { this.push(string) @@ -142,7 +150,7 @@ exports.stringToReadStream = string => { }) } -exports.formatBytes = bytes => { +export function formatBytes(bytes: string) { const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] const byteIncrements = 1024 let unit = 0 @@ -153,7 +161,7 @@ exports.formatBytes = bytes => { return `${size.toFixed(size < 10 && unit > 0 ? 1 : 0)}${units[unit]}` } -exports.convertBookmark = bookmark => { +export function convertBookmark(bookmark: string) { const IS_NUMBER = /^\d+\.?\d*$/ if (typeof bookmark === "string" && bookmark.match(IS_NUMBER)) { return parseFloat(bookmark) @@ -161,7 +169,7 @@ exports.convertBookmark = bookmark => { return bookmark } -exports.isQsTrue = param => { +export function isQsTrue(param: string) { if (typeof param === "string") { return param.toLowerCase() === "true" } else { diff --git a/packages/server/src/utilities/redis.js b/packages/server/src/utilities/redis.ts similarity index 54% rename from packages/server/src/utilities/redis.js rename to packages/server/src/utilities/redis.ts index b39b7cae55..1b7a3ce64c 100644 --- a/packages/server/src/utilities/redis.js +++ b/packages/server/src/utilities/redis.ts @@ -1,46 +1,47 @@ -const { Client, utils } = require("@budibase/backend-core/redis") -const { getGlobalIDFromUserMetadataID } = require("../db/utils") +import { redis } from "@budibase/backend-core" +import { getGlobalIDFromUserMetadataID } from "../db/utils" +import { ContextUser } from "@budibase/types" const APP_DEV_LOCK_SECONDS = 600 const AUTOMATION_TEST_FLAG_SECONDS = 60 -let devAppClient, debounceClient, flagClient +let devAppClient: any, debounceClient: any, flagClient: any // we init this as we want to keep the connection open all the time // reduces the performance hit -exports.init = async () => { - devAppClient = new Client(utils.Databases.DEV_LOCKS) - debounceClient = new Client(utils.Databases.DEBOUNCE) - flagClient = new Client(utils.Databases.FLAGS) +export async function init() { + devAppClient = new redis.Client(redis.utils.Databases.DEV_LOCKS) + debounceClient = new redis.Client(redis.utils.Databases.DEBOUNCE) + flagClient = new redis.Client(redis.utils.Databases.FLAGS) await devAppClient.init() await debounceClient.init() await flagClient.init() } -exports.shutdown = async () => { +export async function shutdown() { if (devAppClient) await devAppClient.finish() if (debounceClient) await debounceClient.finish() if (flagClient) await flagClient.finish() console.log("Redis shutdown") } -exports.doesUserHaveLock = async (devAppId, user) => { +export async function doesUserHaveLock(devAppId: string, user: ContextUser) { const value = await devAppClient.get(devAppId) if (!value) { return true } // make sure both IDs are global const expected = getGlobalIDFromUserMetadataID(value._id) - const userId = getGlobalIDFromUserMetadataID(user._id) + const userId = getGlobalIDFromUserMetadataID(user._id!) return expected === userId } -exports.getLocksById = async appIds => { +export async function getLocksById(appIds: string[]) { return await devAppClient.bulkGet(appIds) } -exports.updateLock = async (devAppId, user) => { +export async function updateLock(devAppId: string, user: ContextUser) { // make sure always global user ID - const globalId = getGlobalIDFromUserMetadataID(user._id) + const globalId = getGlobalIDFromUserMetadataID(user._id!) const inputUser = { ...user, userId: globalId, @@ -51,35 +52,35 @@ exports.updateLock = async (devAppId, user) => { await devAppClient.store(devAppId, inputUser, APP_DEV_LOCK_SECONDS) } -exports.clearLock = async (devAppId, user) => { +export async function clearLock(devAppId: string, user: ContextUser) { const value = await devAppClient.get(devAppId) if (!value) { return } - const userId = getGlobalIDFromUserMetadataID(user._id) + const userId = getGlobalIDFromUserMetadataID(user._id!) if (value._id !== userId) { throw "User does not hold lock, cannot clear it." } await devAppClient.delete(devAppId) } -exports.checkDebounce = async id => { +export async function checkDebounce(id: string) { return debounceClient.get(id) } -exports.setDebounce = async (id, seconds) => { +export async function setDebounce(id: string, seconds: number) { await debounceClient.store(id, "debouncing", seconds) } -exports.setTestFlag = async id => { +export async function setTestFlag(id: string) { await flagClient.store(id, { testing: true }, AUTOMATION_TEST_FLAG_SECONDS) } -exports.checkTestFlag = async id => { +export async function checkTestFlag(id: string) { const flag = await flagClient.get(id) return !!(flag && flag.testing) } -exports.clearTestFlag = async id => { +export async function clearTestFlag(id: string) { await devAppClient.delete(id) } diff --git a/packages/server/src/utilities/routing/index.js b/packages/server/src/utilities/routing/index.js deleted file mode 100644 index 963119130b..0000000000 --- a/packages/server/src/utilities/routing/index.js +++ /dev/null @@ -1,25 +0,0 @@ -const { createRoutingView } = require("../../db/views/staticViews") -const { ViewName, getQueryIndex, UNICODE_MAX } = require("../../db/utils") -const { getAppDB } = require("@budibase/backend-core/context") - -exports.getRoutingInfo = async () => { - const db = getAppDB() - try { - const allRouting = await db.query(getQueryIndex(ViewName.ROUTING), { - startKey: "", - endKey: UNICODE_MAX, - }) - return allRouting.rows.map(row => row.value) - } catch (err) { - // check if the view doesn't exist, it should for all new instances - /* istanbul ignore next */ - if (err != null && err.name === "not_found") { - await createRoutingView() - return exports.getRoutingInfo() - } else { - throw err - } - } -} - -exports.createRoutingView = createRoutingView diff --git a/packages/server/src/utilities/routing/index.ts b/packages/server/src/utilities/routing/index.ts new file mode 100644 index 0000000000..de966a946b --- /dev/null +++ b/packages/server/src/utilities/routing/index.ts @@ -0,0 +1,32 @@ +import { createRoutingView } from "../../db/views/staticViews" +import { ViewName, getQueryIndex, UNICODE_MAX } from "../../db/utils" +import { context } from "@budibase/backend-core" +import { ScreenRouting } from "@budibase/types" + +type ScreenRoutesView = { + id: string + routing: ScreenRouting +} + +export async function getRoutingInfo(): Promise { + const db = context.getAppDB() + try { + const allRouting = await db.query( + getQueryIndex(ViewName.ROUTING), + { + startkey: "", + endkey: UNICODE_MAX, + } + ) + return allRouting.rows.map(row => row.value as ScreenRoutesView) + } catch (err: any) { + // check if the view doesn't exist, it should for all new instances + /* istanbul ignore next */ + if (err != null && err.name === "not_found") { + await createRoutingView() + return getRoutingInfo() + } else { + throw err + } + } +} diff --git a/packages/server/src/utilities/rowProcessor/index.js b/packages/server/src/utilities/rowProcessor/index.ts similarity index 64% rename from packages/server/src/utilities/rowProcessor/index.js rename to packages/server/src/utilities/rowProcessor/index.ts index 91daa1b5a0..9807fc7d61 100644 --- a/packages/server/src/utilities/rowProcessor/index.js +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -1,129 +1,41 @@ -const linkRows = require("../../db/linkedRows") +import linkRows from "../../db/linkedRows" +import { FieldTypes, AutoFieldSubTypes } from "../../constants" +import { attachmentsRelativeURL } from "../index" +import { processFormulas, fixAutoColumnSubType } from "./utils" +import { ObjectStoreBuckets } from "../../constants" +import { context, db as dbCore, objectStore } from "@budibase/backend-core" +import { InternalTables } from "../../db/utils" +import { TYPE_TRANSFORM_MAP } from "./map" +import { Row, User, Table } from "@budibase/types" const { cloneDeep } = require("lodash/fp") -const { FieldTypes, AutoFieldSubTypes } = require("../../constants") -const { attachmentsRelativeURL } = require("../index") -const { processFormulas, fixAutoColumnSubType } = require("./utils") -const { deleteFiles } = require("../../utilities/fileSystem/utilities") -const { ObjectStoreBuckets } = require("../../constants") -const { - isProdAppID, - getProdAppID, - dbExists, -} = require("@budibase/backend-core/db") -const { getAppId } = require("@budibase/backend-core/context") -const { InternalTables } = require("../../db/utils") +export * from "./utils" + +type AutoColumnProcessingOpts = { + reprocessing?: boolean + noAutoRelationships?: boolean +} const BASE_AUTO_ID = 1 -/** - * A map of how we convert various properties in rows to each other based on the row type. - */ -const TYPE_TRANSFORM_MAP = { - [FieldTypes.LINK]: { - "": [], - [null]: [], - [undefined]: undefined, - parse: link => { - if (Array.isArray(link) && typeof link[0] === "object") { - return link.map(el => (el && el._id ? el._id : el)) - } - if (typeof link === "string") { - return [link] - } - return link - }, - }, - [FieldTypes.OPTIONS]: { - "": null, - [null]: null, - [undefined]: undefined, - }, - [FieldTypes.ARRAY]: { - "": [], - [null]: [], - [undefined]: undefined, - }, - [FieldTypes.STRING]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.BARCODEQR]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.FORMULA]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.LONGFORM]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.NUMBER]: { - "": null, - [null]: null, - [undefined]: undefined, - parse: n => parseFloat(n), - }, - [FieldTypes.DATETIME]: { - "": null, - [undefined]: undefined, - [null]: null, - parse: date => { - if (date instanceof Date) { - return date.toISOString() - } - return date - }, - }, - [FieldTypes.ATTACHMENT]: { - "": [], - [null]: [], - [undefined]: undefined, - }, - [FieldTypes.BOOLEAN]: { - "": null, - [null]: null, - [undefined]: undefined, - true: true, - false: false, - }, - [FieldTypes.AUTO]: { - parse: () => undefined, - }, - [FieldTypes.JSON]: { - parse: input => { - try { - if (input === "") { - return undefined - } - return JSON.parse(input) - } catch (err) { - return input - } - }, - }, -} - /** * Given the old state of the row and the new one after an update, this will * find the keys that have been removed in the updated row. */ -function getRemovedAttachmentKeys(oldRow, row, attachmentKey) { +function getRemovedAttachmentKeys( + oldRow: Row, + row: Row, + attachmentKey: string +) { if (!oldRow[attachmentKey]) { return [] } - const oldKeys = oldRow[attachmentKey].map(attachment => attachment.key) + const oldKeys = oldRow[attachmentKey].map((attachment: any) => attachment.key) // no attachments in new row, all removed if (!row[attachmentKey]) { return oldKeys } - const newKeys = row[attachmentKey].map(attachment => attachment.key) - return oldKeys.filter(key => newKeys.indexOf(key) === -1) + const newKeys = row[attachmentKey].map((attachment: any) => attachment.key) + return oldKeys.filter((key: any) => newKeys.indexOf(key) === -1) } /** @@ -136,11 +48,11 @@ function getRemovedAttachmentKeys(oldRow, row, attachmentKey) { * @returns {{row: Object, table: Object}} The updated row and table, the table may need to be updated * for automatic ID purposes. */ -function processAutoColumn( - user, - table, - row, - opts = { reprocessing: false, noAutoRelationships: false } +export function processAutoColumn( + user: User | null, + table: Table, + row: Row, + opts?: AutoColumnProcessingOpts ) { let noUser = !user || !user.userId let isUserTable = table._id === InternalTables.USER_METADATA @@ -149,7 +61,7 @@ function processAutoColumn( const creating = !row._rev // check its not user table, or whether any of the processing options have been disabled const shouldUpdateUserFields = - !isUserTable && !opts.reprocessing && !opts.noAutoRelationships && !noUser + !isUserTable && !opts?.reprocessing && !opts?.noAutoRelationships && !noUser for (let [key, schema] of Object.entries(table.schema)) { if (!schema.autocolumn) { continue @@ -159,7 +71,7 @@ function processAutoColumn( } switch (schema.subtype) { case AutoFieldSubTypes.CREATED_BY: - if (creating && shouldUpdateUserFields) { + if (creating && shouldUpdateUserFields && user) { row[key] = [user.userId] } break @@ -169,7 +81,7 @@ function processAutoColumn( } break case AutoFieldSubTypes.UPDATED_BY: - if (shouldUpdateUserFields) { + if (shouldUpdateUserFields && user) { row[key] = [user.userId] } break @@ -186,9 +98,6 @@ function processAutoColumn( } return { table, row } } -exports.processAutoColumn = processAutoColumn -exports.fixAutoColumnSubType = fixAutoColumnSubType -exports.processFormulas = processFormulas /** * This will coerce a value to the correct types based on the type transform map @@ -196,15 +105,17 @@ exports.processFormulas = processFormulas * @param {object} type The type fo coerce to * @returns {object} The coerced value */ -exports.coerce = (row, type) => { +export function coerce(row: any, type: any) { // no coercion specified for type, skip it if (!TYPE_TRANSFORM_MAP[type]) { return row } // eslint-disable-next-line no-prototype-builtins if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) { + // @ts-ignore return TYPE_TRANSFORM_MAP[type][row] } else if (TYPE_TRANSFORM_MAP[type].parse) { + // @ts-ignore return TYPE_TRANSFORM_MAP[type].parse(row) } @@ -220,12 +131,12 @@ exports.coerce = (row, type) => { * @param {object} opts some input processing options (like disabling auto-column relationships). * @returns {object} the row which has been prepared to be written to the DB. */ -exports.inputProcessing = ( - user = {}, - table, - row, - opts = { noAutoRelationships: false } -) => { +export function inputProcessing( + user: User, + table: Table, + row: Row, + opts: AutoColumnProcessingOpts +) { let clonedRow = cloneDeep(row) // need to copy the table so it can be differenced on way out const copiedTable = cloneDeep(table) @@ -245,7 +156,7 @@ exports.inputProcessing = ( } // otherwise coerce what is there to correct types else { - clonedRow[key] = exports.coerce(value, field.type) + clonedRow[key] = coerce(value, field.type) } } @@ -267,7 +178,11 @@ exports.inputProcessing = ( * @param {object} opts used to set some options for the output, such as disabling relationship squashing. * @returns {object[]|object} the enriched rows will be returned. */ -exports.outputProcessing = async (table, rows, opts = { squash: true }) => { +export async function outputProcessing( + table: Table, + rows: Row[] | Row, + opts = { squash: true } +) { let wasArray = true if (!(rows instanceof Array)) { rows = [rows] @@ -286,7 +201,7 @@ exports.outputProcessing = async (table, rows, opts = { squash: true }) => { if (row[property] == null || !Array.isArray(row[property])) { continue } - row[property].forEach(attachment => { + row[property].forEach((attachment: any) => { attachment.url = attachmentsRelativeURL(attachment.key) }) } @@ -308,20 +223,28 @@ exports.outputProcessing = async (table, rows, opts = { squash: true }) => { * deleted attachment columns. * @return {Promise} When all attachments have been removed this will return. */ -exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => { - const appId = getAppId() - if (!isProdAppID(appId)) { - const prodAppId = getProdAppID(appId) +export async function cleanupAttachments( + table: Table, + { + row, + rows, + oldRow, + oldTable, + }: { row?: Row; rows?: Row[]; oldRow?: Row; oldTable?: Table } +): Promise { + const appId = context.getAppId() + if (!dbCore.isProdAppID(appId)) { + const prodAppId = dbCore.getProdAppID(appId!) // if prod exists, then don't allow deleting - const exists = await dbExists(prodAppId) + const exists = await dbCore.dbExists(prodAppId) if (exists) { return } } - let files = [] - function addFiles(row, key) { + let files: string[] = [] + function addFiles(row: Row, key: string) { if (row[key]) { - files = files.concat(row[key].map(attachment => attachment.key)) + files = files.concat(row[key].map((attachment: any) => attachment.key)) } } const schemaToUse = oldTable ? oldTable.schema : table.schema @@ -330,7 +253,7 @@ exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => { continue } // old table had this column, new table doesn't - delete it - if (oldTable && !table.schema[key]) { + if (rows && oldTable && !table.schema[key]) { rows.forEach(row => addFiles(row, key)) } else if (oldRow && row) { // if updating, need to manage the differences @@ -342,6 +265,6 @@ exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => { } } if (files.length > 0) { - return deleteFiles(ObjectStoreBuckets.APPS, files) + return objectStore.deleteFiles(ObjectStoreBuckets.APPS, files) } } diff --git a/packages/server/src/utilities/rowProcessor/map.js b/packages/server/src/utilities/rowProcessor/map.js new file mode 100644 index 0000000000..4e05868bfc --- /dev/null +++ b/packages/server/src/utilities/rowProcessor/map.js @@ -0,0 +1,95 @@ +const { FieldTypes } = require("../../constants") + +/** + * A map of how we convert various properties in rows to each other based on the row type. + */ +exports.TYPE_TRANSFORM_MAP = { + [FieldTypes.LINK]: { + "": [], + [null]: [], + [undefined]: undefined, + parse: link => { + if (Array.isArray(link) && typeof link[0] === "object") { + return link.map(el => (el && el._id ? el._id : el)) + } + if (typeof link === "string") { + return [link] + } + return link + }, + }, + [FieldTypes.OPTIONS]: { + "": null, + [null]: null, + [undefined]: undefined, + }, + [FieldTypes.ARRAY]: { + "": [], + [null]: [], + [undefined]: undefined, + }, + [FieldTypes.STRING]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.BARCODEQR]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.FORMULA]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.LONGFORM]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.NUMBER]: { + "": null, + [null]: null, + [undefined]: undefined, + parse: n => parseFloat(n), + }, + [FieldTypes.DATETIME]: { + "": null, + [undefined]: undefined, + [null]: null, + parse: date => { + if (date instanceof Date) { + return date.toISOString() + } + return date + }, + }, + [FieldTypes.ATTACHMENT]: { + "": [], + [null]: [], + [undefined]: undefined, + }, + [FieldTypes.BOOLEAN]: { + "": null, + [null]: null, + [undefined]: undefined, + true: true, + false: false, + }, + [FieldTypes.AUTO]: { + parse: () => undefined, + }, + [FieldTypes.JSON]: { + parse: input => { + try { + if (input === "") { + return undefined + } + return JSON.parse(input) + } catch (err) { + return input + } + }, + }, +} diff --git a/packages/server/src/utilities/rowProcessor/utils.js b/packages/server/src/utilities/rowProcessor/utils.ts similarity index 78% rename from packages/server/src/utilities/rowProcessor/utils.js rename to packages/server/src/utilities/rowProcessor/utils.ts index d659cb6822..c22bc59419 100644 --- a/packages/server/src/utilities/rowProcessor/utils.js +++ b/packages/server/src/utilities/rowProcessor/utils.ts @@ -1,16 +1,17 @@ -const { +import { FieldTypes, FormulaTypes, AutoFieldDefaultNames, AutoFieldSubTypes, -} = require("../../constants") -const { processStringSync } = require("@budibase/string-templates") +} from "../../constants" +import { processStringSync } from "@budibase/string-templates" +import { FieldSchema, Table, Row } from "@budibase/types" /** * If the subtype has been lost for any reason this works out what * subtype the auto column should be. */ -exports.fixAutoColumnSubType = column => { +export function fixAutoColumnSubType(column: FieldSchema) { if (!column.autocolumn || !column.name || column.subtype) { return column } @@ -32,15 +33,18 @@ exports.fixAutoColumnSubType = column => { /** * Looks through the rows provided and finds formulas - which it then processes. */ -exports.processFormulas = ( - table, - rows, - { dynamic, contextRows } = { dynamic: true } -) => { +export function processFormulas( + table: Table, + rows: Row[] | Row, + { dynamic, contextRows }: any = { dynamic: true } +) { const single = !Array.isArray(rows) + let rowArray: Row[] if (single) { - rows = [rows] + rowArray = [rows] contextRows = contextRows ? [contextRows] : contextRows + } else { + rowArray = rows } for (let [column, schema] of Object.entries(table.schema)) { const isStatic = schema.formulaType === FormulaTypes.STATIC @@ -52,25 +56,25 @@ exports.processFormulas = ( continue } // iterate through rows and process formula - for (let i = 0; i < rows.length; i++) { + for (let i = 0; i < rowArray.length; i++) { if (schema.formula) { - let row = rows[i] + let row = rowArray[i] let context = contextRows ? contextRows[i] : row - rows[i] = { + rowArray[i] = { ...row, [column]: processStringSync(schema.formula, context), } } } } - return single ? rows[0] : rows + return single ? rowArray[0] : rowArray } /** * Processes any date columns and ensures that those without the ignoreTimezones * flag set are parsed as UTC rather than local time. */ -exports.processDates = (table, rows) => { +export function processDates(table: Table, rows: Row[]) { let datesWithTZ = [] for (let [column, schema] of Object.entries(table.schema)) { if (schema.type !== FieldTypes.DATETIME) { diff --git a/packages/server/src/utilities/security.js b/packages/server/src/utilities/security.js deleted file mode 100644 index 34d31ce8d0..0000000000 --- a/packages/server/src/utilities/security.js +++ /dev/null @@ -1,71 +0,0 @@ -const { - PermissionLevel, - PermissionType, - getBuiltinPermissionByID, - isPermissionLevelHigherThanRead, -} = require("@budibase/backend-core/permissions") -const { - lowerBuiltinRoleID, - getBuiltinRoles, -} = require("@budibase/backend-core/roles") -const { DocumentType } = require("../db/utils") - -const CURRENTLY_SUPPORTED_LEVELS = [ - PermissionLevel.WRITE, - PermissionLevel.READ, - PermissionLevel.EXECUTE, -] - -exports.getPermissionType = resourceId => { - const docType = Object.values(DocumentType).filter(docType => - resourceId.startsWith(docType) - )[0] - switch (docType) { - case DocumentType.TABLE: - case DocumentType.ROW: - return PermissionType.TABLE - case DocumentType.AUTOMATION: - return PermissionType.AUTOMATION - case DocumentType.WEBHOOK: - return PermissionType.WEBHOOK - case DocumentType.QUERY: - case DocumentType.DATASOURCE: - return PermissionType.QUERY - default: - // views don't have an ID, will end up here - return PermissionType.VIEW - } -} - -/** - * works out the basic permissions based on builtin roles for a resource, using its ID - * @param resourceId - * @returns {{}} - */ -exports.getBasePermissions = resourceId => { - const type = exports.getPermissionType(resourceId) - const permissions = {} - for (let [roleId, role] of Object.entries(getBuiltinRoles())) { - if (!role.permissionId) { - continue - } - const perms = getBuiltinPermissionByID(role.permissionId) - const typedPermission = perms.permissions.find(perm => perm.type === type) - if ( - typedPermission && - CURRENTLY_SUPPORTED_LEVELS.indexOf(typedPermission.level) !== -1 - ) { - const level = typedPermission.level - permissions[level] = lowerBuiltinRoleID(permissions[level], roleId) - if (isPermissionLevelHigherThanRead(level)) { - permissions[PermissionLevel.READ] = lowerBuiltinRoleID( - permissions[PermissionLevel.READ], - roleId - ) - } - } - } - return permissions -} - -exports.CURRENTLY_SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS diff --git a/packages/server/src/utilities/security.ts b/packages/server/src/utilities/security.ts new file mode 100644 index 0000000000..694dff4360 --- /dev/null +++ b/packages/server/src/utilities/security.ts @@ -0,0 +1,65 @@ +import { permissions, roles } from "@budibase/backend-core" +import { DocumentType } from "../db/utils" + +export const CURRENTLY_SUPPORTED_LEVELS: string[] = [ + permissions.PermissionLevel.WRITE, + permissions.PermissionLevel.READ, + permissions.PermissionLevel.EXECUTE, +] + +export function getPermissionType(resourceId: string) { + const docType = Object.values(DocumentType).filter(docType => + resourceId.startsWith(docType) + )[0] + switch (docType) { + case DocumentType.TABLE: + case DocumentType.ROW: + return permissions.PermissionType.TABLE + case DocumentType.AUTOMATION: + return permissions.PermissionType.AUTOMATION + case DocumentType.WEBHOOK: + return permissions.PermissionType.WEBHOOK + case DocumentType.QUERY: + case DocumentType.DATASOURCE: + return permissions.PermissionType.QUERY + default: + // views don't have an ID, will end up here + return permissions.PermissionType.VIEW + } +} + +/** + * works out the basic permissions based on builtin roles for a resource, using its ID + */ +export function getBasePermissions(resourceId: string) { + const type = getPermissionType(resourceId) + const basePermissions: { [key: string]: string } = {} + for (let [roleId, role] of Object.entries(roles.getBuiltinRoles())) { + if (!role.permissionId) { + continue + } + const perms = permissions.getBuiltinPermissionByID(role.permissionId) + if (!perms) { + continue + } + const typedPermission = perms.permissions.find(perm => perm.type === type) + if ( + typedPermission && + CURRENTLY_SUPPORTED_LEVELS.indexOf(typedPermission.level) !== -1 + ) { + const level = typedPermission.level + basePermissions[level] = roles.lowerBuiltinRoleID( + basePermissions[level], + roleId + ) + if (permissions.isPermissionLevelHigherThanRead(level)) { + basePermissions[permissions.PermissionLevel.READ] = + roles.lowerBuiltinRoleID( + basePermissions[permissions.PermissionLevel.READ], + roleId + ) + } + } + } + return basePermissions +} diff --git a/packages/server/src/utilities/usageQuota/rows.js b/packages/server/src/utilities/usageQuota/rows.ts similarity index 71% rename from packages/server/src/utilities/usageQuota/rows.js rename to packages/server/src/utilities/usageQuota/rows.ts index 0a92507a96..1bc7c8897d 100644 --- a/packages/server/src/utilities/usageQuota/rows.js +++ b/packages/server/src/utilities/usageQuota/rows.ts @@ -1,23 +1,19 @@ -const { getRowParams, USER_METDATA_PREFIX } = require("../../db/utils") -const { - isDevAppID, - getDevelopmentAppID, - getProdAppID, - doWithDB, -} = require("@budibase/backend-core/db") +import { getRowParams, USER_METDATA_PREFIX } from "../../db/utils" +import { db as dbCore } from "@budibase/backend-core" +import { Database, Row } from "@budibase/types" const ROW_EXCLUSIONS = [USER_METDATA_PREFIX] -const getAppPairs = appIds => { +function getAppPairs(appIds: string[]) { // collect the app ids into dev / prod pairs // keyed by the dev app id - const pairs = {} + const pairs: { [key: string]: { devId?: string; prodId?: string } } = {} for (let appId of appIds) { - const devId = getDevelopmentAppID(appId) + const devId = dbCore.getDevelopmentAppID(appId) if (!pairs[devId]) { pairs[devId] = {} } - if (isDevAppID(appId)) { + if (dbCore.isDevAppID(appId)) { pairs[devId].devId = appId } else { pairs[devId].prodId = appId @@ -26,9 +22,9 @@ const getAppPairs = appIds => { return pairs } -const getAppRows = async appId => { +async function getAppRows(appId: string) { // need to specify the app ID, as this is used for different apps in one call - return doWithDB(appId, async db => { + return dbCore.doWithDB(appId, async (db: Database) => { const response = await db.allDocs( getRowParams(null, null, { include_docs: false, @@ -52,13 +48,13 @@ const getAppRows = async appId => { * The returned rows will be unique on a per dev/prod app basis. * Rows duplicates may exist across apps due to data import so they are not filtered out. */ -exports.getUniqueRows = async appIds => { - let uniqueRows = [], - rowsByApp = {} +export async function getUniqueRows(appIds: string[]) { + let uniqueRows: Row[] = [], + rowsByApp: { [key: string]: Row[] } = {} const pairs = getAppPairs(appIds) for (let pair of Object.values(pairs)) { - let appRows = [] + let appRows: Row[] = [] for (let appId of [pair.devId, pair.prodId]) { if (!appId) { continue @@ -75,7 +71,7 @@ exports.getUniqueRows = async appIds => { // this can't be done on all rows because app import results in // duplicate row ids across apps // the array pre-concat is important to avoid stack overflow - const prodId = getProdAppID(pair.devId || pair.prodId) + const prodId = dbCore.getProdAppID((pair.devId || pair.prodId)!) rowsByApp[prodId] = [...new Set(appRows)] uniqueRows = uniqueRows.concat(rowsByApp[prodId]) } diff --git a/packages/server/src/utilities/users.js b/packages/server/src/utilities/users.ts similarity index 60% rename from packages/server/src/utilities/users.js rename to packages/server/src/utilities/users.ts index 498b934605..1498a79719 100644 --- a/packages/server/src/utilities/users.js +++ b/packages/server/src/utilities/users.ts @@ -1,12 +1,11 @@ -const { InternalTables } = require("../db/utils") -const { getGlobalUser } = require("../utilities/global") -const { getAppDB } = require("@budibase/backend-core/context") -const { getProdAppID } = require("@budibase/backend-core/db") -const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") +import { InternalTables } from "../db/utils" +import { getGlobalUser } from "./global" +import { context, db as dbCore, roles } from "@budibase/backend-core" +import { BBContext } from "@budibase/types" -exports.getFullUser = async (ctx, userId) => { +export async function getFullUser(ctx: BBContext, userId: string) { const global = await getGlobalUser(userId) - let metadata = {} + let metadata: any = {} // always prefer the user metadata _id and _rev delete global._id @@ -14,7 +13,7 @@ exports.getFullUser = async (ctx, userId) => { try { // this will throw an error if the db doesn't exist, or there is no appId - const db = getAppDB() + const db = context.getAppDB() metadata = await db.get(userId) } catch (err) { // it is fine if there is no user metadata yet @@ -23,14 +22,14 @@ exports.getFullUser = async (ctx, userId) => { return { ...metadata, ...global, - roleId: global.roleId || BUILTIN_ROLE_IDS.PUBLIC, + roleId: global.roleId || roles.BUILTIN_ROLE_IDS.PUBLIC, tableId: InternalTables.USER_METADATA, // make sure the ID is always a local ID, not a global one _id: userId, } } -exports.publicApiUserFix = ctx => { +export function publicApiUserFix(ctx: BBContext) { if (!ctx.request.body) { return ctx } @@ -40,10 +39,9 @@ exports.publicApiUserFix = ctx => { if (!ctx.request.body.roles) { ctx.request.body.roles = {} } else { - const newRoles = {} + const newRoles: { [key: string]: any } = {} for (let [appId, role] of Object.entries(ctx.request.body.roles)) { - // @ts-ignore - newRoles[getProdAppID(appId)] = role + newRoles[dbCore.getProdAppID(appId)] = role } ctx.request.body.roles = newRoles } diff --git a/packages/server/src/utilities/workerRequests.js b/packages/server/src/utilities/workerRequests.ts similarity index 66% rename from packages/server/src/utilities/workerRequests.js rename to packages/server/src/utilities/workerRequests.ts index e606ba9fa6..b5554bbe6f 100644 --- a/packages/server/src/utilities/workerRequests.js +++ b/packages/server/src/utilities/workerRequests.ts @@ -1,19 +1,18 @@ -const fetch = require("node-fetch") -const env = require("../environment") -const { checkSlashesInUrl } = require("./index") -const { getProdAppID } = require("@budibase/backend-core/db") -const { updateAppRole } = require("./global") -const { Header } = require("@budibase/backend-core/constants") -const { getTenantId, isTenantIdSet } = require("@budibase/backend-core/tenancy") +import fetch from "node-fetch" +import env from "../environment" +import { checkSlashesInUrl } from "./index" +import { db as dbCore, constants, tenancy } from "@budibase/backend-core" +import { updateAppRole } from "./global" +import { BBContext, Automation } from "@budibase/types" -function request(ctx, request) { +export function request(ctx?: BBContext, request?: any) { if (!request.headers) { request.headers = {} } if (!ctx) { - request.headers[Header.API_KEY] = env.INTERNAL_API_KEY - if (isTenantIdSet()) { - request.headers[Header.TENANT_ID] = getTenantId() + request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY + if (tenancy.isTenantIdSet()) { + request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId() } } if (request.body && Object.keys(request.body).length > 0) { @@ -31,7 +30,11 @@ function request(ctx, request) { return request } -async function checkResponse(response, errorMsg, { ctx } = {}) { +async function checkResponse( + response: any, + errorMsg: string, + { ctx }: { ctx?: BBContext } = {} +) { if (response.status !== 200) { let error try { @@ -51,22 +54,20 @@ async function checkResponse(response, errorMsg, { ctx } = {}) { return response.json() } -exports.request = request - // have to pass in the tenant ID as this could be coming from an automation -exports.sendSmtpEmail = async ( - to, - from, - subject, - contents, - cc, - bcc, - automation -) => { +export async function sendSmtpEmail( + to: string, + from: string, + subject: string, + contents: string, + cc: string, + bcc: string, + automation: Automation +) { // tenant ID will be set in header const response = await fetch( checkSlashesInUrl(env.WORKER_URL + `/api/global/email/send`), - request(null, { + request(undefined, { method: "POST", body: { email: to, @@ -83,7 +84,7 @@ exports.sendSmtpEmail = async ( return checkResponse(response, "send email") } -exports.getGlobalSelf = async (ctx, appId = null) => { +export async function getGlobalSelf(ctx: BBContext, appId?: string) { const endpoint = `/api/global/self` const response = await fetch( checkSlashesInUrl(env.WORKER_URL + endpoint), @@ -97,8 +98,8 @@ exports.getGlobalSelf = async (ctx, appId = null) => { return json } -exports.removeAppFromUserRoles = async (ctx, appId) => { - const prodAppId = getProdAppID(appId) +export async function removeAppFromUserRoles(ctx: BBContext, appId: string) { + const prodAppId = dbCore.getProdAppID(appId) const response = await fetch( checkSlashesInUrl(env.WORKER_URL + `/api/global/roles/${prodAppId}`), request(ctx, { @@ -108,7 +109,7 @@ exports.removeAppFromUserRoles = async (ctx, appId) => { return checkResponse(response, "remove app role") } -exports.allGlobalUsers = async ctx => { +export async function allGlobalUsers(ctx: BBContext) { const response = await fetch( checkSlashesInUrl(env.WORKER_URL + "/api/global/users"), // we don't want to use API key when getting self @@ -117,7 +118,7 @@ exports.allGlobalUsers = async ctx => { return checkResponse(response, "get users", { ctx }) } -exports.saveGlobalUser = async ctx => { +export async function saveGlobalUser(ctx: BBContext) { const response = await fetch( checkSlashesInUrl(env.WORKER_URL + "/api/global/users"), // we don't want to use API key when getting self @@ -126,7 +127,7 @@ exports.saveGlobalUser = async ctx => { return checkResponse(response, "save user", { ctx }) } -exports.deleteGlobalUser = async ctx => { +export async function deleteGlobalUser(ctx: BBContext) { const response = await fetch( checkSlashesInUrl( env.WORKER_URL + `/api/global/users/${ctx.params.userId}` @@ -134,10 +135,10 @@ exports.deleteGlobalUser = async ctx => { // we don't want to use API key when getting self request(ctx, { method: "DELETE" }) ) - return checkResponse(response, "delete user", { ctx, body: ctx.request.body }) + return checkResponse(response, "delete user", { ctx }) } -exports.readGlobalUser = async ctx => { +export async function readGlobalUser(ctx: BBContext) { const response = await fetch( checkSlashesInUrl( env.WORKER_URL + `/api/global/users/${ctx.params.userId}` @@ -148,26 +149,30 @@ exports.readGlobalUser = async ctx => { return checkResponse(response, "get user", { ctx }) } -exports.createAdminUser = async (email, password, tenantId) => { +export async function createAdminUser( + email: string, + password: string, + tenantId: string +) { const response = await fetch( checkSlashesInUrl(env.WORKER_URL + "/api/global/users/init"), - request(null, { method: "POST", body: { email, password, tenantId } }) + request(undefined, { method: "POST", body: { email, password, tenantId } }) ) return checkResponse(response, "create admin user") } -exports.getChecklist = async () => { +export async function getChecklist() { const response = await fetch( checkSlashesInUrl(env.WORKER_URL + "/api/global/configs/checklist"), - request(null, { method: "GET" }) + request(undefined, { method: "GET" }) ) return checkResponse(response, "get checklist") } -exports.generateApiKey = async userId => { +export async function generateApiKey(userId: string) { const response = await fetch( checkSlashesInUrl(env.WORKER_URL + "/api/global/self/api_key"), - request(null, { method: "POST", body: { userId } }) + request(undefined, { method: "POST", body: { userId } }) ) return checkResponse(response, "generate API key") } diff --git a/packages/server/src/watch.ts b/packages/server/src/watch.ts index 4beef5ddb8..2ef132c19d 100644 --- a/packages/server/src/watch.ts +++ b/packages/server/src/watch.ts @@ -2,8 +2,7 @@ import path from "path" import * as env from "./environment" import chokidar from "chokidar" import fs from "fs" -import { tenancy } from "@budibase/backend-core" -import { DEFAULT_TENANT_ID } from "@budibase/backend-core/constants" +import { constants, tenancy } from "@budibase/backend-core" import { processUploadedPlugin } from "./api/controllers/plugin" export function watch() { @@ -23,7 +22,7 @@ export function watch() { if (!path?.endsWith(".tar.gz") || !fs.existsSync(path)) { return } - await tenancy.doInTenant(DEFAULT_TENANT_ID, async () => { + await tenancy.doInTenant(constants.DEFAULT_TENANT_ID, async () => { try { const split = path.split("/") const name = split[split.length - 1] diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 9dddbe8ea3..9868074bd3 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1273,12 +1273,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.32-alpha.3": - version "2.1.32-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.32-alpha.3.tgz#d65f97f6c147e75aa83cfb4c22a5b3bd568bf62d" - integrity sha512-CSRn29z64bLEW3GLo6cTwpGjvTgIsfDOBwAiStEOlezzMNlDslvcOBCW0pQUo/0rf5C4zsTy4U1oHDElz7JJ2w== +"@budibase/backend-core@2.1.32-alpha.7": + version "2.1.32-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.32-alpha.7.tgz#36ec22a104bba166184609d31c8264c579352b2c" + integrity sha512-JQk3XWV9dpqwgYmkYZc78m7GmnyJQ1J533v2NbdWGorGQ+NKv2e+/p+yaZ+LEdnCWd+wFhTj8u1z1eP0vHKT8w== dependencies: - "@budibase/types" "2.1.32-alpha.3" + "@budibase/types" "2.1.32-alpha.7" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -1361,13 +1361,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@2.1.32-alpha.3": - version "2.1.32-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.32-alpha.3.tgz#32d67f5fe3aa6a993022e685f55a298b4fc364b5" - integrity sha512-FwaWZLI8NVSH3Y4RcSvD1ySJXa5yWFgfGha5Zz5rvy3Tw3oSr/AfH91VW4ZLyzpuBQrMJHxLtDmaz3Ywd/jSkQ== +"@budibase/pro@2.1.32-alpha.7": + version "2.1.32-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.32-alpha.7.tgz#e90321a1dc0c1e1dea1fed453e4ba09418588ab4" + integrity sha512-mfOz2khTyAiwLDgks6PW4lK4giqe6gJvtDc/jci58ixZfpWmuMZeelx1Wbuqf5t3yWvc/R2ntgq5qWCwkF1rIA== dependencies: - "@budibase/backend-core" "2.1.32-alpha.3" - "@budibase/types" "2.1.32-alpha.3" + "@budibase/backend-core" "2.1.32-alpha.7" + "@budibase/types" "2.1.32-alpha.7" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" @@ -1391,10 +1391,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@2.1.32-alpha.3": - version "2.1.32-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.32-alpha.3.tgz#87d53d7b5730f0067c1526a62f85afb13db82a51" - integrity sha512-4MGPfe63IKJMSS+4HdznyjEnExKGk1rMa2sfVOvRAZWzdVhgo5m0UarwoKf49lDF3NPiqTLdXQnB4p7AmwQbbg== +"@budibase/types@2.1.32-alpha.7": + version "2.1.32-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.32-alpha.7.tgz#732ff700b6464293acaffac67d417798a90ea364" + integrity sha512-pGl5ukFgMEBFcTPEzeR1t1VyhBxCOwViALWtN4VDtOVoapDKGOkiJStJz//JMtJXh0d+Uljw9fjr6N7LQUK8jg== "@bull-board/api@3.7.0": version "3.7.0" @@ -3168,10 +3168,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.13.4", "@types/node@>=13.7.0": - version "17.0.41" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.41.tgz#1607b2fd3da014ae5d4d1b31bc792a39348dfb9b" - integrity sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw== +"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12.12.47", "@types/node@>=13.13.4", "@types/node@>=13.7.0": + version "18.11.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" + integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== "@types/node@14.18.20": version "14.18.20" @@ -3183,11 +3183,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708" integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== -"@types/node@>=10.0.0": - version "18.7.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.18.tgz#633184f55c322e4fb08612307c274ee6d5ed3154" - integrity sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg== - "@types/node@>=8.0.0 <15": version "14.18.21" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41" @@ -6086,9 +6081,9 @@ engine.io-parser@~5.0.3: integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== engine.io@~6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0" - integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg== + version "6.2.1" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f" + integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA== dependencies: "@types/cookie" "^0.4.1" "@types/cors" "^2.8.12" diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index fd8fbbbeb1..e089cbb513 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index 2d689c56b7..8d35a3ce2f 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Budibase types", "main": "dist/index.js", "types": "dist/index.d.ts", 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/documents/app/role.ts b/packages/types/src/documents/app/role.ts index 1f5d9bbecf..2f558dfa45 100644 --- a/packages/types/src/documents/app/role.ts +++ b/packages/types/src/documents/app/role.ts @@ -2,6 +2,6 @@ import { Document } from "../document" export interface Role extends Document { permissionId: string - inherits: string + inherits?: string permissions: { [key: string]: string[] } } diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 6390c3b18c..a3778d140f 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -12,14 +12,16 @@ export interface ScreenProps extends Document { hAlign?: string } +export interface ScreenRouting { + route: string + roleId: string + homeScreen?: boolean +} + export interface Screen extends Document { layoutId?: string showNavigation?: boolean width?: string - routing: { - route: string - roleId: string - homeScreen?: boolean - } + routing: ScreenRouting props: ScreenProps } diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 1d559c5bef..6b9a46d59f 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -1,5 +1,6 @@ import { Document } from "../document" import { View } from "./view" +import { RenameColumn } from "../../sdk" export interface FieldSchema { // TODO: replace with field types enum when done @@ -19,6 +20,8 @@ export interface FieldSchema { formulaType?: string main?: boolean ignoreTimezones?: boolean + timeOnly?: boolean + lastID?: number meta?: { toTable: string toKey: string @@ -53,3 +56,8 @@ export interface Table extends Document { indexes?: { [key: string]: any } dataImport?: { [key: string]: any } } + +export interface TableRequest extends Table { + _rename?: RenameColumn + created?: boolean +} diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 6c93bac1ac..9a1fb472f0 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -17,6 +17,7 @@ export interface User extends Document { userGroups?: string[] forceResetPassword?: boolean dayPassRecordedAt?: string + userId?: string } export interface UserRoles { diff --git a/packages/types/src/documents/pouch.ts b/packages/types/src/documents/pouch.ts index 8ad4ace5b7..d484f4700d 100644 --- a/packages/types/src/documents/pouch.ts +++ b/packages/types/src/documents/pouch.ts @@ -7,7 +7,7 @@ export interface RowResponse { id: string key: string error: string - value: RowValue + value: T | RowValue doc?: T | any } diff --git a/packages/types/src/sdk/auth.ts b/packages/types/src/sdk/auth.ts index 766d18a606..edcaf197e2 100644 --- a/packages/types/src/sdk/auth.ts +++ b/packages/types/src/sdk/auth.ts @@ -1,4 +1,5 @@ import { BBContext } from "./koa" +import { Hosting } from "./hosting" export interface AuthToken { userId: string @@ -10,6 +11,7 @@ export interface CreateSession { sessionId: string tenantId: string csrfToken?: string + hosting?: Hosting } export interface Session extends CreateSession { diff --git a/packages/types/src/sdk/events/role.ts b/packages/types/src/sdk/events/role.ts index adeb169a53..b04b9b8ee5 100644 --- a/packages/types/src/sdk/events/role.ts +++ b/packages/types/src/sdk/events/role.ts @@ -3,19 +3,19 @@ import { BaseEvent } from "./event" export interface RoleCreatedEvent extends BaseEvent { roleId: string permissionId: string - inherits: string + inherits?: string } export interface RoleUpdatedEvent extends BaseEvent { roleId: string permissionId: string - inherits: string + inherits?: string } export interface RoleDeletedEvent extends BaseEvent { roleId: string permissionId: string - inherits: string + inherits?: string } export interface RoleAssignedEvent extends BaseEvent { diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts new file mode 100644 index 0000000000..53aa4842c4 --- /dev/null +++ b/packages/types/src/sdk/featureFlag.ts @@ -0,0 +1,7 @@ +export enum FeatureFlag { + LICENSING = "LICENSING", +} + +export interface TenantFeatureFlags { + [key: string]: FeatureFlag[] +} diff --git a/packages/types/src/sdk/index.ts b/packages/types/src/sdk/index.ts index a32c8e2077..e5512b062b 100644 --- a/packages/types/src/sdk/index.ts +++ b/packages/types/src/sdk/index.ts @@ -10,3 +10,4 @@ export * from "./auth" export * from "./locks" export * from "./db" export * from "./middleware" +export * from "./featureFlag" diff --git a/packages/types/src/sdk/koa.ts b/packages/types/src/sdk/koa.ts index ffbb1cf053..07d4a893c1 100644 --- a/packages/types/src/sdk/koa.ts +++ b/packages/types/src/sdk/koa.ts @@ -1,19 +1,23 @@ import { Context, Request } from "koa" -import { User, Role, UserRoles } from "../documents" -import { License } from "../sdk" +import { User, Role, UserRoles, Account } from "../documents" +import { FeatureFlag, License } from "../sdk" export interface ContextUser extends Omit { globalId?: string - license: License + license?: License userId?: string roleId?: string | null role?: Role roles?: UserRoles csrfToken?: string + featureFlags?: FeatureFlag[] + accountPortalAccess?: boolean + account?: Account } export interface BBRequest extends Request { body: any + files?: any } export interface BBContext extends Context { diff --git a/packages/types/src/sdk/search.ts b/packages/types/src/sdk/search.ts index 954ad42ac9..d5ea664c6b 100644 --- a/packages/types/src/sdk/search.ts +++ b/packages/types/src/sdk/search.ts @@ -72,7 +72,7 @@ export interface QueryJson { operation: Operation schema?: string } - resource: { + resource?: { fields: string[] } filters?: SearchFilters @@ -83,7 +83,7 @@ export interface QueryJson { meta?: { table?: Table tables?: Record - renamed: RenameColumn + renamed?: RenameColumn } extra?: { idFilter?: SearchFilters diff --git a/packages/worker/package.json b/packages/worker/package.json index 3e41075cb8..19358e94ae 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.1.32-alpha.3", + "version": "2.1.32-alpha.7", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.1.32-alpha.3", - "@budibase/pro": "2.1.32-alpha.3", - "@budibase/string-templates": "2.1.32-alpha.3", - "@budibase/types": "2.1.32-alpha.3", + "@budibase/backend-core": "2.1.32-alpha.7", + "@budibase/pro": "2.1.32-alpha.7", + "@budibase/string-templates": "2.1.32-alpha.7", + "@budibase/types": "2.1.32-alpha.7", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index ecc432d8e6..5592687102 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -470,12 +470,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.32-alpha.3": - version "2.1.32-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.32-alpha.3.tgz#d65f97f6c147e75aa83cfb4c22a5b3bd568bf62d" - integrity sha512-CSRn29z64bLEW3GLo6cTwpGjvTgIsfDOBwAiStEOlezzMNlDslvcOBCW0pQUo/0rf5C4zsTy4U1oHDElz7JJ2w== +"@budibase/backend-core@2.1.32-alpha.7": + version "2.1.32-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.32-alpha.7.tgz#36ec22a104bba166184609d31c8264c579352b2c" + integrity sha512-JQk3XWV9dpqwgYmkYZc78m7GmnyJQ1J533v2NbdWGorGQ+NKv2e+/p+yaZ+LEdnCWd+wFhTj8u1z1eP0vHKT8w== dependencies: - "@budibase/types" "2.1.32-alpha.3" + "@budibase/types" "2.1.32-alpha.7" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -508,22 +508,22 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@2.1.32-alpha.3": - version "2.1.32-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.32-alpha.3.tgz#32d67f5fe3aa6a993022e685f55a298b4fc364b5" - integrity sha512-FwaWZLI8NVSH3Y4RcSvD1ySJXa5yWFgfGha5Zz5rvy3Tw3oSr/AfH91VW4ZLyzpuBQrMJHxLtDmaz3Ywd/jSkQ== +"@budibase/pro@2.1.32-alpha.7": + version "2.1.32-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.32-alpha.7.tgz#e90321a1dc0c1e1dea1fed453e4ba09418588ab4" + integrity sha512-mfOz2khTyAiwLDgks6PW4lK4giqe6gJvtDc/jci58ixZfpWmuMZeelx1Wbuqf5t3yWvc/R2ntgq5qWCwkF1rIA== dependencies: - "@budibase/backend-core" "2.1.32-alpha.3" - "@budibase/types" "2.1.32-alpha.3" + "@budibase/backend-core" "2.1.32-alpha.7" + "@budibase/types" "2.1.32-alpha.7" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" node-fetch "^2.6.1" -"@budibase/types@2.1.32-alpha.3": - version "2.1.32-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.32-alpha.3.tgz#87d53d7b5730f0067c1526a62f85afb13db82a51" - integrity sha512-4MGPfe63IKJMSS+4HdznyjEnExKGk1rMa2sfVOvRAZWzdVhgo5m0UarwoKf49lDF3NPiqTLdXQnB4p7AmwQbbg== +"@budibase/types@2.1.32-alpha.7": + version "2.1.32-alpha.7" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.32-alpha.7.tgz#732ff700b6464293acaffac67d417798a90ea364" + integrity sha512-pGl5ukFgMEBFcTPEzeR1t1VyhBxCOwViALWtN4VDtOVoapDKGOkiJStJz//JMtJXh0d+Uljw9fjr6N7LQUK8jg== "@cspotcode/source-map-support@^0.8.0": version "0.8.1"