diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index 75cd4ae7ec..08ed9c54b2 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -10,7 +10,6 @@ import { isCloudAccount, isSSOAccount, TenantGroup, - SettingsConfig, CloudAccount, UserIdentity, InstallationGroup, diff --git a/packages/server/src/api/controllers/cloud.ts b/packages/server/src/api/controllers/cloud.ts index 7f43f21fc9..7be00e3a1d 100644 --- a/packages/server/src/api/controllers/cloud.ts +++ b/packages/server/src/api/controllers/cloud.ts @@ -58,7 +58,7 @@ export async function exportApps(ctx: Ctx) { } async function checkHasBeenImported() { - if (!env.SELF_HOSTED || env.MULTI_TENANCY) { + if (!env.SELF_HOSTED) { return true } const apps = await dbCore.getAllApps({ all: true }) @@ -72,7 +72,7 @@ export async function hasBeenImported(ctx: Ctx) { } export async function importApps(ctx: Ctx) { - if (!env.SELF_HOSTED || env.MULTI_TENANCY) { + if (!env.SELF_HOSTED) { ctx.throw(400, "Importing only allowed in self hosted environments.") } const beenImported = await checkHasBeenImported() diff --git a/packages/server/src/api/routes/tests/cloud.seq.spec.ts b/packages/server/src/api/routes/tests/cloud.spec.ts similarity index 70% rename from packages/server/src/api/routes/tests/cloud.seq.spec.ts rename to packages/server/src/api/routes/tests/cloud.spec.ts index d9bd6221ad..aad1214a31 100644 --- a/packages/server/src/api/routes/tests/cloud.seq.spec.ts +++ b/packages/server/src/api/routes/tests/cloud.spec.ts @@ -1,3 +1,5 @@ +import { App } from "@budibase/types" + jest.setTimeout(30000) import { AppStatus } from "../../../db/utils" @@ -5,6 +7,7 @@ import { AppStatus } from "../../../db/utils" import * as setup from "./utilities" import { wipeDb } from "./utilities/TestFunctions" +import { tenancy } from "@budibase/backend-core" describe("/cloud", () => { let request = setup.getRequest()! @@ -12,18 +15,10 @@ describe("/cloud", () => { afterAll(setup.afterAll) - beforeAll(() => { + beforeAll(async () => { // Importing is only allowed in self hosted environments - config.modeSelf() - }) - - beforeEach(async () => { await config.init() - }) - - afterEach(async () => { - // clear all mocks - jest.clearAllMocks() + config.modeSelf() }) describe("import", () => { @@ -32,30 +27,28 @@ describe("/cloud", () => { // import will not run await wipeDb() - // get a count of apps before the import - const preImportApps = await request - .get(`/api/applications?status=${AppStatus.ALL}`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - // Perform the import const res = await request .post(`/api/cloud/import`) + .set(config.publicHeaders()) .attach("importFile", "src/api/routes/tests/data/export-test.tar.gz") - .set(config.defaultHeaders()) .expect(200) expect(res.body.message).toEqual("Apps successfully imported.") // get a count of apps after the import const postImportApps = await request .get(`/api/applications?status=${AppStatus.ALL}`) - .set(config.defaultHeaders()) + .set(config.publicHeaders()) .expect("Content-Type", /json/) .expect(200) + const apps = postImportApps.body as App[] // There are two apps in the file that was imported so check for this - expect(postImportApps.body.length).toEqual(2) + expect(apps.length).toEqual(2) + // The new tenant id was assigned to the imported apps + expect(tenancy.getTenantIDFromAppID(apps[0].appId)).toBe( + config.getTenantId() + ) }) }) }) diff --git a/packages/server/src/api/routes/tests/utilities/TestFunctions.ts b/packages/server/src/api/routes/tests/utilities/TestFunctions.ts index 3f57c1e8ef..5a1ed5210e 100644 --- a/packages/server/src/api/routes/tests/utilities/TestFunctions.ts +++ b/packages/server/src/api/routes/tests/utilities/TestFunctions.ts @@ -2,7 +2,6 @@ import * as rowController from "../../../controllers/row" import * as appController from "../../../controllers/application" import { AppStatus } from "../../../../db/utils" import { roles, tenancy, context } from "@budibase/backend-core" -import { TENANT_ID } from "../../../../tests/utilities/structures" import env from "../../../../environment" import { db } from "@budibase/backend-core" import Nano from "@budibase/nano" @@ -33,7 +32,7 @@ export const getAllTableRows = async (config: any) => { } export const clearAllApps = async ( - tenantId = TENANT_ID, + tenantId: string, exceptions: Array = [] ) => { await tenancy.doInTenant(tenantId, async () => { diff --git a/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js b/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js index c0066b1c71..15ddcfd1ef 100644 --- a/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js +++ b/packages/server/src/migrations/functions/tests/userEmailViewCasing.spec.js @@ -8,9 +8,8 @@ jest.mock("@budibase/backend-core", () => { } } }) -const { tenancy, db: dbCore } = require("@budibase/backend-core") +const { context, db: dbCore } = require("@budibase/backend-core") const TestConfig = require("../../../tests/utilities/TestConfiguration") -const { TENANT_ID } = require("../../../tests/utilities/structures") // mock email view creation @@ -26,8 +25,8 @@ describe("run", () => { afterAll(config.end) it("runs successfully", async () => { - await tenancy.doInTenant(TENANT_ID, async () => { - const globalDb = tenancy.getGlobalDB() + await config.doInTenant(async () => { + const globalDb = context.getGlobalDB() await migration.run(globalDb) expect(dbCore.createNewUserEmailView).toHaveBeenCalledTimes(1) }) diff --git a/packages/server/src/tests/jestEnv.ts b/packages/server/src/tests/jestEnv.ts index c567b260b3..7727bb6007 100644 --- a/packages/server/src/tests/jestEnv.ts +++ b/packages/server/src/tests/jestEnv.ts @@ -8,3 +8,4 @@ process.env.BUDIBASE_DIR = tmpdir("budibase-unittests") process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error" process.env.ENABLE_4XX_HTTP_LOGGING = "0" process.env.MOCK_REDIS = "1" +process.env.PLATFORM_URL = "http://localhost:10000" diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 88545dbcbb..e9b770229f 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -21,7 +21,6 @@ import { basicScreen, basicLayout, basicWebhook, - TENANT_ID, } from "./structures" import { constants, @@ -41,8 +40,8 @@ import { generateUserMetadataID } from "../../db/utils" import { startup } from "../../startup" import supertest from "supertest" import { + App, AuthToken, - Database, Datasource, Row, SourceName, @@ -63,7 +62,7 @@ class TestConfiguration { started: boolean appId: string | null allApps: any[] - app: any + app?: App prodApp: any prodAppId: any user: any @@ -73,7 +72,7 @@ class TestConfiguration { linkedTable: any automation: any datasource: any - tenantId: string | null + tenantId?: string defaultUserValues: DefaultUserValues constructor(openServer = true) { @@ -89,7 +88,6 @@ class TestConfiguration { } this.appId = null this.allApps = [] - this.tenantId = null this.defaultUserValues = this.populateDefaultUserValues() } @@ -154,19 +152,10 @@ class TestConfiguration { // use a new id as the name to avoid name collisions async init(appName = newid()) { - this.defaultUserValues = this.populateDefaultUserValues() - if (context.isMultiTenant()) { - this.tenantId = structures.tenant.id() - } - if (!this.started) { await startup() } - this.user = await this.globalUser() - this.globalUserId = this.user._id - this.userMetadataId = generateUserMetadataID(this.globalUserId) - - return this.createApp(appName) + return this.newTenant(appName) } end() { @@ -182,24 +171,22 @@ class TestConfiguration { } // MODES - #setMultiTenancy = (value: boolean) => { + setMultiTenancy = (value: boolean) => { env._set("MULTI_TENANCY", value) coreEnv._set("MULTI_TENANCY", value) } - #setSelfHosted = (value: boolean) => { + setSelfHosted = (value: boolean) => { env._set("SELF_HOSTED", value) coreEnv._set("SELF_HOSTED", value) } modeCloud = () => { - this.#setSelfHosted(false) - this.#setMultiTenancy(true) + this.setSelfHosted(false) } modeSelf = () => { - this.#setSelfHosted(true) - this.#setMultiTenancy(false) + this.setSelfHosted(true) } // UTILS @@ -354,6 +341,8 @@ class TestConfiguration { }) } + // HEADERS + defaultHeaders(extras = {}) { const tenantId = this.getTenantId() const authObj: AuthToken = { @@ -374,6 +363,7 @@ class TestConfiguration { `${constants.Cookie.CurrentApp}=${appToken}`, ], [constants.Header.CSRF_TOKEN]: this.defaultUserValues.csrfToken, + Host: this.tenantHost(), ...extras, } @@ -383,10 +373,6 @@ class TestConfiguration { return headers } - getTenantId() { - return this.tenantId || TENANT_ID - } - publicHeaders({ prodApp = true } = {}) { const appId = prodApp ? this.prodAppId : this.appId @@ -397,9 +383,7 @@ class TestConfiguration { headers[constants.Header.APP_ID] = appId } - if (this.tenantId) { - headers[constants.Header.TENANT_ID] = this.tenantId - } + headers[constants.Header.TENANT_ID] = this.getTenantId() return headers } @@ -413,6 +397,34 @@ class TestConfiguration { return this.login({ email, roleId, builder, prodApp }) } + // TENANCY + + tenantHost() { + const tenantId = this.getTenantId() + const platformHost = new URL(coreEnv.PLATFORM_URL).host.split(":")[0] + return `${tenantId}.${platformHost}` + } + + getTenantId() { + if (!this.tenantId) { + throw new Error("no test tenant id - init has not been called") + } + return this.tenantId + } + + async newTenant(appName = newid()): Promise { + this.defaultUserValues = this.populateDefaultUserValues() + this.tenantId = structures.tenant.id() + this.user = await this.globalUser() + this.globalUserId = this.user._id + this.userMetadataId = generateUserMetadataID(this.globalUserId) + return this.createApp(appName) + } + + doInTenant(task: any) { + return context.doInTenant(this.getTenantId(), task) + } + // API async generateApiKey(userId = this.defaultUserValues.globalUserId) { @@ -432,7 +444,7 @@ class TestConfiguration { } // APP - async createApp(appName: string) { + async createApp(appName: string): Promise { // create dev app // clear any old app this.appId = null @@ -442,7 +454,7 @@ class TestConfiguration { null, controllers.app.create ) - this.appId = this.app.appId + this.appId = this.app?.appId! }) return await context.doInAppContext(this.appId, async () => { // create production app diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index c1959dc791..e38c0a5275 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -13,8 +13,6 @@ import { const { v4: uuidv4 } = require("uuid") -export const TENANT_ID = "default" - export function basicTable() { return { name: "TestTable",