diff --git a/packages/server/src/api/controllers/auth.ts b/packages/server/src/api/controllers/auth.ts index ef2cb29385..88dc581020 100644 --- a/packages/server/src/api/controllers/auth.ts +++ b/packages/server/src/api/controllers/auth.ts @@ -3,6 +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" const PUBLIC_ROLE = roles.BUILTIN_ROLE_IDS.PUBLIC @@ -24,7 +25,7 @@ export async function fetchSelf(ctx: any) { } const appId = context.getAppId() - const user = await getFullUser(ctx, userId) + const user: ContextUser = await getFullUser(ctx, userId) // this shouldn't be returned by the app self delete user.roles // forward the csrf token from the session @@ -34,7 +35,7 @@ export async function fetchSelf(ctx: any) { const db = context.getAppDB() // check for group permissions if (!user.roleId || user.roleId === PUBLIC_ROLE) { - const groupRoleId = await groups.getGroupRoleId(user, appId) + const groupRoleId = await groups.getGroupRoleId(user as User, appId) user.roleId = groupRoleId || user.roleId } // remove the full roles structure diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index c92f942986..fa40320e91 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -5,11 +5,8 @@ import { save as saveDatasource } from "../datasource" import { RestImporter } from "./import" import { invalidateDynamicVariables } from "../../../threads/utils" import { QUERY_THREAD_TIMEOUT } from "../../../environment" -import { getAppDB } from "@budibase/backend-core/context" import { quotas } from "@budibase/pro" -import { events } from "@budibase/backend-core" -import { getCookie } from "@budibase/backend-core/utils" -import { Cookies, Configs } from "@budibase/backend-core/constants" +import { events, context, utils, constants } from "@budibase/backend-core" const Runner = new Thread(ThreadType.QUERY, { timeoutMs: QUERY_THREAD_TIMEOUT || 10000, @@ -28,7 +25,7 @@ function enrichQueries(input: any) { } export async function fetch(ctx: any) { - const db = getAppDB() + const db = context.getAppDB() const body = await db.allDocs( getQueryParams(null, { @@ -81,7 +78,7 @@ const _import = async (ctx: any) => { export { _import as import } export async function save(ctx: any) { - const db = getAppDB() + const db = context.getAppDB() const query = ctx.request.body const datasource = await db.get(query.datasourceId) @@ -103,7 +100,7 @@ export async function save(ctx: any) { } export async function find(ctx: any) { - const db = getAppDB() + const db = context.getAppDB() const query = enrichQueries(await db.get(ctx.params.queryId)) // remove properties that could be dangerous in real app if (isProdAppID(ctx.appId)) { @@ -115,13 +112,13 @@ export async function find(ctx: any) { //Required to discern between OIDC OAuth config entries function getOAuthConfigCookieId(ctx: any) { - if (ctx.user.providerType === Configs.OIDC) { - return getCookie(ctx, Cookies.OIDC_CONFIG) + if (ctx.user.providerType === constants.Configs.OIDC) { + return utils.getCookie(ctx, constants.Cookies.OIDC_CONFIG) } } function getAuthConfig(ctx: any) { - const authCookie = getCookie(ctx, Cookies.Auth) + const authCookie = utils.getCookie(ctx, constants.Cookies.Auth) let authConfigCtx: any = {} authConfigCtx["configId"] = getOAuthConfigCookieId(ctx) authConfigCtx["sessionId"] = authCookie ? authCookie.sessionId : null @@ -129,7 +126,7 @@ function getAuthConfig(ctx: any) { } export async function preview(ctx: any) { - const db = getAppDB() + const db = context.getAppDB() const datasource = await db.get(ctx.request.body.datasourceId) const query = ctx.request.body @@ -201,7 +198,7 @@ async function execute( ctx: any, opts: any = { rowsOnly: false, isAutomation: false } ) { - const db = getAppDB() + const db = context.getAppDB() const query = await db.get(ctx.params.queryId) const datasource = await db.get(query.datasourceId) @@ -267,7 +264,7 @@ export async function executeV2( } const removeDynamicVariables = async (queryId: any) => { - const db = getAppDB() + const db = context.getAppDB() const query = await db.get(queryId) const datasource = await db.get(query.datasourceId) const dynamicVariables = datasource.config.dynamicVariables @@ -288,7 +285,7 @@ const removeDynamicVariables = async (queryId: any) => { } export async function destroy(ctx: any) { - const db = getAppDB() + const db = context.getAppDB() const queryId = ctx.params.queryId await removeDynamicVariables(queryId) const query = await db.get(queryId) diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 897659e13f..f9f2fa8347 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -4,16 +4,21 @@ jest.mock("node-fetch") // Mock isProdAppID to we can later mock the implementation and pretend we are // using prod app IDs -const authDb = require("@budibase/backend-core/db") -const { isProdAppID } = authDb -const mockIsProdAppID = jest.fn(isProdAppID) -authDb.isProdAppID = mockIsProdAppID - +jest.mock("@budibase/backend-core", () => { + const core = jest.requireActual("@budibase/backend-core") + return { + ...core, + db: { + ...core.db, + isProdAppID: jest.fn(), + } + } +}) const setup = require("./utilities") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkCacheForDynamicVariable } = require("../../../threads/utils") const { basicQuery, basicDatasource } = setup.structures -const { events } = require("@budibase/backend-core") +const { events, db: dbCore } = require("@budibase/backend-core") describe("/queries", () => { let request = setup.getRequest() @@ -152,8 +157,8 @@ describe("/queries", () => { it("should remove sensitive info for prod apps", async () => { // Mock isProdAppID to pretend we are using a prod app - mockIsProdAppID.mockClear() - mockIsProdAppID.mockImplementation(() => true) + dbCore.isProdAppID.mockClear() + dbCore.isProdAppID.mockImplementation(() => true) const query = await config.createQuery() const res = await request @@ -167,8 +172,8 @@ describe("/queries", () => { expect(res.body.schema).toBeDefined() // Reset isProdAppID mock - expect(mockIsProdAppID).toHaveBeenCalledTimes(1) - mockIsProdAppID.mockImplementation(isProdAppID) + expect(dbCore.isProdAppID).toHaveBeenCalledTimes(1) + dbCore.isProdAppID.mockImplementation(() => false) }) }) diff --git a/packages/server/src/api/routes/tests/table.spec.js b/packages/server/src/api/routes/tests/table.spec.js index ca1a067a4d..e42aaf07e4 100644 --- a/packages/server/src/api/routes/tests/table.spec.js +++ b/packages/server/src/api/routes/tests/table.spec.js @@ -1,17 +1,18 @@ const { checkBuilderEndpoint } = require("./utilities/TestFunctions") -const { getAppDB } = require("@budibase/backend-core/context") const setup = require("./utilities") const { basicTable } = setup.structures -const { events } = require("@budibase/backend-core") +const { events, context } = require("@budibase/backend-core") describe("/tables", () => { let request = setup.getRequest() let config = setup.getConfig() + let appId afterAll(setup.afterAll) beforeEach(async () => { - await config.init() + const app = await config.init() + appId = app.appId }) describe("create", () => { @@ -199,38 +200,6 @@ describe("/tables", () => { }) }) - describe("indexing", () => { - it("should be able to create a table with indexes", async () => { - const db = getAppDB(config) - const indexCount = (await db.getIndexes()).total_rows - const table = basicTable() - table.indexes = ["name"] - const res = await request - .post(`/api/tables`) - .send(table) - .set(config.defaultHeaders()) - .expect('Content-Type', /json/) - .expect(200) - expect(res.body._id).toBeDefined() - expect(res.body._rev).toBeDefined() - expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1) - // update index to see what happens - table.indexes = ["name", "description"] - await request - .post(`/api/tables`) - .send({ - ...table, - _id: res.body._id, - _rev: res.body._rev, - }) - .set(config.defaultHeaders()) - .expect('Content-Type', /json/) - .expect(200) - // shouldn't have created a new index - expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1) - }) - }) - describe("validate csv", () => { it("should be able to validate a CSV layout", async () => { const res = await request @@ -249,6 +218,40 @@ describe("/tables", () => { }) }) + describe("indexing", () => { + it("should be able to create a table with indexes", async () => { + await context.doInAppContext(appId, async () => { + const db = context.getAppDB() + const indexCount = (await db.getIndexes()).total_rows + const table = basicTable() + table.indexes = ["name"] + const res = await request + .post(`/api/tables`) + .send(table) + .set(config.defaultHeaders()) + .expect('Content-Type', /json/) + .expect(200) + expect(res.body._id).toBeDefined() + expect(res.body._rev).toBeDefined() + expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1) + // update index to see what happens + table.indexes = ["name", "description"] + await request + .post(`/api/tables`) + .send({ + ...table, + _id: res.body._id, + _rev: res.body._rev, + }) + .set(config.defaultHeaders()) + .expect('Content-Type', /json/) + .expect(200) + // shouldn't have created a new index + expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1) + }) + }) + }) + describe("destroy", () => { let testTable diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.ts similarity index 61% rename from packages/server/src/db/utils.js rename to packages/server/src/db/utils.ts index acd72cbf66..58ea76c5ae 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.ts @@ -1,44 +1,19 @@ -const newid = require("./newid") -const { - DocumentType: CoreDocType, - InternalTable, - getRoleParams, - generateRoleID, - APP_DEV_PREFIX, - APP_PREFIX, - SEPARATOR, - StaticDatabases, - isDevAppID, - isProdAppID, - getDevelopmentAppID, - generateAppID, - getQueryIndex, - ViewName, - getDocParams, - getRowParams, - generateRowID, - getUserMetadataParams, - generateUserMetadataID, - getGlobalIDFromUserMetadataID, -} = require("@budibase/backend-core/db") +import newid from "./newid" +import { db as dbCore } from "@budibase/backend-core" -const UNICODE_MAX = "\ufff0" +type Optional = string | null -const AppStatus = { +export const AppStatus = { DEV: "development", ALL: "all", DEPLOYED: "published", } -const DocumentType = CoreDocType - -const SearchIndexes = { +export const SearchIndexes = { ROWS: "rows", } -exports.StaticDatabases = StaticDatabases - -const BudibaseInternalDB = { +export const BudibaseInternalDB = { _id: "bb_internal", type: "budibase", name: "Budibase DB", @@ -46,37 +21,36 @@ const BudibaseInternalDB = { config: {}, } -exports.APP_PREFIX = APP_PREFIX -exports.APP_DEV_PREFIX = APP_DEV_PREFIX -exports.isDevAppID = isDevAppID -exports.isProdAppID = isProdAppID -exports.USER_METDATA_PREFIX = `${DocumentType.ROW}${SEPARATOR}${InternalTable.USER_METADATA}${SEPARATOR}` -exports.LINK_USER_METADATA_PREFIX = `${DocumentType.LINK}${SEPARATOR}${InternalTable.USER_METADATA}${SEPARATOR}` -exports.TABLE_ROW_PREFIX = `${DocumentType.ROW}${SEPARATOR}${DocumentType.TABLE}` -exports.ViewName = ViewName -exports.InternalTables = InternalTable -exports.DocumentType = DocumentType -exports.SEPARATOR = SEPARATOR -exports.UNICODE_MAX = UNICODE_MAX -exports.SearchIndexes = SearchIndexes -exports.AppStatus = AppStatus -exports.BudibaseInternalDB = BudibaseInternalDB -exports.generateAppID = generateAppID -exports.generateDevAppID = getDevelopmentAppID -exports.generateRoleID = generateRoleID -exports.getRoleParams = getRoleParams -exports.getQueryIndex = getQueryIndex -exports.getDocParams = getDocParams -exports.getRowParams = getRowParams -exports.generateRowID = generateRowID -exports.getUserMetadataParams = getUserMetadataParams -exports.generateUserMetadataID = generateUserMetadataID -exports.getGlobalIDFromUserMetadataID = getGlobalIDFromUserMetadataID +export const SEPARATOR = dbCore.SEPARATOR +export const StaticDatabases = dbCore.StaticDatabases +export const DocumentType = dbCore.DocumentType +export const APP_PREFIX = dbCore.APP_PREFIX +export const APP_DEV_PREFIX = dbCore.APP_DEV_PREFIX +export const isDevAppID = dbCore.isDevAppID +export const isProdAppID = dbCore.isProdAppID +export const USER_METDATA_PREFIX = `${DocumentType.ROW}${SEPARATOR}${dbCore.InternalTable.USER_METADATA}${SEPARATOR}` +export const LINK_USER_METADATA_PREFIX = `${DocumentType.LINK}${SEPARATOR}${dbCore.InternalTable.USER_METADATA}${SEPARATOR}` +export const TABLE_ROW_PREFIX = `${DocumentType.ROW}${SEPARATOR}${DocumentType.TABLE}` +export const ViewName = dbCore.ViewName +export const InternalTables = dbCore.InternalTable +export const UNICODE_MAX = dbCore.UNICODE_MAX +export const generateAppID = dbCore.generateAppID +export const generateDevAppID = dbCore.getDevelopmentAppID +export const generateRoleID = dbCore.generateRoleID +export const getRoleParams = dbCore.getRoleParams +export const getQueryIndex = dbCore.getQueryIndex +export const getDocParams = dbCore.getDocParams +export const getRowParams = dbCore.getRowParams +export const generateRowID = dbCore.generateRowID +export const getUserMetadataParams = dbCore.getUserMetadataParams +export const generateUserMetadataID = dbCore.generateUserMetadataID +export const getGlobalIDFromUserMetadataID = + dbCore.getGlobalIDFromUserMetadataID /** * Gets parameters for retrieving tables, this is a utility function for the getDocParams function. */ -exports.getTableParams = (tableId = null, otherProps = {}) => { +export function getTableParams(tableId?: Optional, otherProps = {}) { return getDocParams(DocumentType.TABLE, tableId, otherProps) } @@ -84,7 +58,7 @@ exports.getTableParams = (tableId = null, otherProps = {}) => { * Generates a new table ID. * @returns {string} The new table ID which the table doc can be stored under. */ -exports.generateTableID = () => { +export function generateTableID() { return `${DocumentType.TABLE}${SEPARATOR}${newid()}` } @@ -93,7 +67,7 @@ exports.generateTableID = () => { * @param {string} rowId The ID of the row. * @returns {string} The table ID. */ -exports.getTableIDFromRowID = rowId => { +export function getTableIDFromRowID(rowId: string) { const components = rowId .split(DocumentType.TABLE + SEPARATOR)[1] .split(SEPARATOR) @@ -103,7 +77,10 @@ exports.getTableIDFromRowID = rowId => { /** * Gets parameters for retrieving automations, this is a utility function for the getDocParams function. */ -exports.getAutomationParams = (automationId = null, otherProps = {}) => { +export function getAutomationParams( + automationId?: Optional, + otherProps: any = {} +) { return getDocParams(DocumentType.AUTOMATION, automationId, otherProps) } @@ -111,7 +88,7 @@ exports.getAutomationParams = (automationId = null, otherProps = {}) => { * Generates a new automation ID. * @returns {string} The new automation ID which the automation doc can be stored under. */ -exports.generateAutomationID = () => { +export function generateAutomationID() { return `${DocumentType.AUTOMATION}${SEPARATOR}${newid()}` } @@ -126,14 +103,14 @@ exports.generateAutomationID = () => { * @param {string} fieldName2 the name of the field in the linked row. * @returns {string} The new link doc ID which the automation doc can be stored under. */ -exports.generateLinkID = ( - tableId1, - tableId2, - rowId1, - rowId2, - fieldName1, - fieldName2 -) => { +export function generateLinkID( + tableId1: string, + tableId2: string, + rowId1: string, + rowId2: string, + fieldName1: string, + fieldName2: string +) { const tables = `${SEPARATOR}${tableId1}${SEPARATOR}${tableId2}` const rows = `${SEPARATOR}${rowId1}${SEPARATOR}${rowId2}` const fields = `${SEPARATOR}${fieldName1}${SEPARATOR}${fieldName2}` @@ -143,7 +120,7 @@ exports.generateLinkID = ( /** * Gets parameters for retrieving link docs, this is a utility function for the getDocParams function. */ -exports.getLinkParams = (otherProps = {}) => { +export function getLinkParams(otherProps: any = {}) { return getDocParams(DocumentType.LINK, null, otherProps) } @@ -151,14 +128,14 @@ exports.getLinkParams = (otherProps = {}) => { * Generates a new layout ID. * @returns {string} The new layout ID which the layout doc can be stored under. */ -exports.generateLayoutID = id => { +export function generateLayoutID(id: string) { return `${DocumentType.LAYOUT}${SEPARATOR}${id || newid()}` } /** * Gets parameters for retrieving layout, this is a utility function for the getDocParams function. */ -exports.getLayoutParams = (layoutId = null, otherProps = {}) => { +export function getLayoutParams(layoutId?: Optional, otherProps: any = {}) { return getDocParams(DocumentType.LAYOUT, layoutId, otherProps) } @@ -166,14 +143,14 @@ exports.getLayoutParams = (layoutId = null, otherProps = {}) => { * Generates a new screen ID. * @returns {string} The new screen ID which the screen doc can be stored under. */ -exports.generateScreenID = () => { +export function generateScreenID() { return `${DocumentType.SCREEN}${SEPARATOR}${newid()}` } /** * Gets parameters for retrieving screens, this is a utility function for the getDocParams function. */ -exports.getScreenParams = (screenId = null, otherProps = {}) => { +export function getScreenParams(screenId?: Optional, otherProps: any = {}) { return getDocParams(DocumentType.SCREEN, screenId, otherProps) } @@ -181,14 +158,14 @@ exports.getScreenParams = (screenId = null, otherProps = {}) => { * Generates a new webhook ID. * @returns {string} The new webhook ID which the webhook doc can be stored under. */ -exports.generateWebhookID = () => { +export function generateWebhookID() { return `${DocumentType.WEBHOOK}${SEPARATOR}${newid()}` } /** * Gets parameters for retrieving a webhook, this is a utility function for the getDocParams function. */ -exports.getWebhookParams = (webhookId = null, otherProps = {}) => { +export function getWebhookParams(webhookId?: Optional, otherProps: any = {}) { return getDocParams(DocumentType.WEBHOOK, webhookId, otherProps) } @@ -196,7 +173,7 @@ exports.getWebhookParams = (webhookId = null, otherProps = {}) => { * Generates a new datasource ID. * @returns {string} The new datasource ID which the webhook doc can be stored under. */ -exports.generateDatasourceID = ({ plus = false } = {}) => { +export function generateDatasourceID({ plus = false } = {}) { return `${ plus ? DocumentType.DATASOURCE_PLUS : DocumentType.DATASOURCE }${SEPARATOR}${newid()}` @@ -205,7 +182,10 @@ exports.generateDatasourceID = ({ plus = false } = {}) => { /** * Gets parameters for retrieving a datasource, this is a utility function for the getDocParams function. */ -exports.getDatasourceParams = (datasourceId = null, otherProps = {}) => { +export function getDatasourceParams( + datasourceId?: Optional, + otherProps: any = {} +) { return getDocParams(DocumentType.DATASOURCE, datasourceId, otherProps) } @@ -213,7 +193,7 @@ exports.getDatasourceParams = (datasourceId = null, otherProps = {}) => { * Generates a new query ID. * @returns {string} The new query ID which the query doc can be stored under. */ -exports.generateQueryID = datasourceId => { +export function generateQueryID(datasourceId: string) { return `${ DocumentType.QUERY }${SEPARATOR}${datasourceId}${SEPARATOR}${newid()}` @@ -223,21 +203,21 @@ exports.generateQueryID = datasourceId => { * Generates a metadata ID for automations, used to track errors in recurring * automations etc. */ -exports.generateAutomationMetadataID = automationId => { +export function generateAutomationMetadataID(automationId: string) { return `${DocumentType.AUTOMATION_METADATA}${SEPARATOR}${automationId}` } /** * Retrieve all automation metadata in an app database. */ -exports.getAutomationMetadataParams = (otherProps = {}) => { +export function getAutomationMetadataParams(otherProps: any = {}) { return getDocParams(DocumentType.AUTOMATION_METADATA, null, otherProps) } /** * Gets parameters for retrieving a query, this is a utility function for the getDocParams function. */ -exports.getQueryParams = (datasourceId = null, otherProps = {}) => { +export function getQueryParams(datasourceId?: Optional, otherProps: any = {}) { if (datasourceId == null) { return getDocParams(DocumentType.QUERY, null, otherProps) } @@ -253,15 +233,19 @@ exports.getQueryParams = (datasourceId = null, otherProps = {}) => { * Generates a new flag document ID. * @returns {string} The ID of the flag document that was generated. */ -exports.generateUserFlagID = userId => { +export function generateUserFlagID(userId: string) { return `${DocumentType.USER_FLAG}${SEPARATOR}${userId}` } -exports.generateMetadataID = (type, entityId) => { +export function generateMetadataID(type: string, entityId: string) { return `${DocumentType.METADATA}${SEPARATOR}${type}${SEPARATOR}${entityId}` } -exports.getMetadataParams = (type, entityId = null, otherProps = {}) => { +export function getMetadataParams( + type: string, + entityId?: Optional, + otherProps: any = {} +) { let docId = `${type}${SEPARATOR}` if (entityId != null) { docId += entityId @@ -269,22 +253,22 @@ exports.getMetadataParams = (type, entityId = null, otherProps = {}) => { return getDocParams(DocumentType.METADATA, docId, otherProps) } -exports.generateMemoryViewID = viewName => { +export function generateMemoryViewID(viewName: string) { return `${DocumentType.MEM_VIEW}${SEPARATOR}${viewName}` } -exports.getMemoryViewParams = (otherProps = {}) => { +export function getMemoryViewParams(otherProps: any = {}) { return getDocParams(DocumentType.MEM_VIEW, null, otherProps) } -exports.generatePluginID = name => { +export function generatePluginID(name: string) { return `${DocumentType.PLUGIN}${SEPARATOR}${name}` } /** * This can be used with the db.allDocs to get a list of IDs */ -exports.getMultiIDParams = ids => { +export function getMultiIDParams(ids: string[]) { return { keys: ids, include_docs: true, diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index b4b290462e..3552ffa0f3 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -120,7 +120,7 @@ class Orchestrator { } async getMetadata(): Promise { - const metadataId = generateAutomationMetadataID(this._automation._id) + const metadataId = generateAutomationMetadataID(this._automation._id!) const db = getAppDB() let metadata: AutomationMetadata try { diff --git a/packages/server/src/utilities/global.ts b/packages/server/src/utilities/global.ts index f9f6b376ce..c9161aa426 100644 --- a/packages/server/src/utilities/global.ts +++ b/packages/server/src/utilities/global.ts @@ -94,7 +94,9 @@ export async function getGlobalUsers(users?: ContextUser[]) { const db = tenancy.getGlobalDB() let globalUsers if (users) { - const globalIds = users.map(user => getGlobalIDFromUserMetadataID(user._id)) + const globalIds = users.map(user => + getGlobalIDFromUserMetadataID(user._id!) + ) globalUsers = (await db.allDocs(getMultiIDParams(globalIds))).rows.map( row => row.doc ) diff --git a/packages/types/src/sdk/koa.ts b/packages/types/src/sdk/koa.ts index cd171bfe9e..ffbb1cf053 100644 --- a/packages/types/src/sdk/koa.ts +++ b/packages/types/src/sdk/koa.ts @@ -9,6 +9,7 @@ export interface ContextUser extends Omit { roleId?: string | null role?: Role roles?: UserRoles + csrfToken?: string } export interface BBRequest extends Request {