diff --git a/package.json b/package.json index 53d60249f7..6815429f1e 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "private": true, "devDependencies": { "@rollup/plugin-json": "^4.0.2", + "@types/supertest": "^2.0.12", "@typescript-eslint/parser": "5.45.0", "babel-eslint": "^10.0.3", "eslint": "^7.28.0", diff --git a/packages/backend-core/src/cache/user.ts b/packages/backend-core/src/cache/user.ts index a128465cd6..6cc80da07d 100644 --- a/packages/backend-core/src/cache/user.ts +++ b/packages/backend-core/src/cache/user.ts @@ -49,10 +49,11 @@ export async function getUser( } const client = await redis.getUserClient() // try cache - let user = await client.get(userId) + const cacheKey = getCacheKey(tenantId!, userId) + let user = await client.get(cacheKey) if (!user) { user = await populateUser(userId, tenantId) - await client.store(userId, user, EXPIRY_SECONDS) + await client.store(cacheKey, user, EXPIRY_SECONDS) } if (user && !user.tenantId && tenantId) { // make sure the tenant ID is always correct/set @@ -62,6 +63,11 @@ export async function getUser( } export async function invalidateUser(userId: string) { + const tenantId = getTenantId() + const cacheKey = getCacheKey(tenantId, userId) const client = await redis.getUserClient() - await client.delete(userId) + await client.delete(cacheKey) } + +const getCacheKey = (tenantId: string, userId: string) => + `${tenantId}_${userId}` diff --git a/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.ts.snap b/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.ts.snap index 436818f089..7c45a8418f 100644 --- a/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.ts.snap +++ b/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.ts.snap @@ -7,7 +7,7 @@ Array [ "entities": Array [ Object { "_id": "ta_users", - "_rev": "1-6f4013e796887f1771bf7837598d87e7", + "_rev": "1-2375e1bc58aeec664dc1b1f04ad43e44", "createdAt": "2020-01-01T00:00:00.000Z", "name": "Users", "primaryDisplay": "email", diff --git a/packages/server/src/api/routes/tests/auth.spec.js b/packages/server/src/api/routes/tests/auth.spec.js index fa26eb83ac..06a1f52ad8 100644 --- a/packages/server/src/api/routes/tests/auth.spec.js +++ b/packages/server/src/api/routes/tests/auth.spec.js @@ -18,7 +18,7 @@ describe("/authenticate", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(res.body._id).toEqual(generateUserMetadataID("us_uuid1")) + expect(res.body._id).toEqual(generateUserMetadataID(config.user._id)) }) }) }) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/datasource.spec.ts b/packages/server/src/api/routes/tests/datasource.spec.ts index 66888023c4..bd03d151fa 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.ts +++ b/packages/server/src/api/routes/tests/datasource.spec.ts @@ -4,6 +4,10 @@ import { checkBuilderEndpoint } from "./utilities/TestFunctions" import { checkCacheForDynamicVariable } from "../../../threads/utils" import { events } from "@budibase/backend-core" +import tk from "timekeeper" +import { mocks } from "@budibase/backend-core/tests" +tk.freeze(mocks.date.MOCK_DATE) + let { basicDatasource } = setup.structures const pg = require("pg") @@ -55,7 +59,14 @@ describe("/datasources", () => { datasource: any, fields: { path: string; queryString: string } ) { - return config.previewQuery(request, config, datasource, fields) + return config.previewQuery( + request, + config, + datasource, + fields, + undefined, + "" + ) } it("should invalidate changed or removed variables", async () => { diff --git a/packages/server/src/api/routes/tests/utilities/index.ts b/packages/server/src/api/routes/tests/utilities/index.ts index 519e8a1459..bf69b91e3b 100644 --- a/packages/server/src/api/routes/tests/utilities/index.ts +++ b/packages/server/src/api/routes/tests/utilities/index.ts @@ -1,5 +1,6 @@ import TestConfig from "../../../../tests/utilities/TestConfiguration" import env from "../../../../environment" +import supertest from "supertest" export * as structures from "../../../../tests/utilities/structures" function user() { @@ -44,7 +45,8 @@ export function delay(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)) } -let request: any, config: any +let request: supertest.SuperTest | undefined | null, + config: TestConfig | null export function beforeAll() { config = new TestConfig() @@ -65,14 +67,14 @@ export function getRequest() { if (!request) { beforeAll() } - return request + return request! } export function getConfig() { if (!config) { beforeAll() } - return config + return config! } export async function switchToSelfHosted(func: any) { diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 75a56d695e..ff9c82d367 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -39,11 +39,17 @@ import { cleanup } from "../../utilities/fileSystem" import newid from "../../db/newid" import { generateUserMetadataID } from "../../db/utils" import { startup } from "../../startup" -import { db } from "@budibase/backend-core" -import Nano from "@budibase/nano" import { AuthToken } from "@budibase/types" const supertest = require("supertest") +type DefaultUserValues = { + globalUserId: string + email: string + firstName: string + lastName: string + csrfToken: string +} + class TestConfiguration { server: any request: any @@ -61,13 +67,7 @@ class TestConfiguration { automation: any datasource: any tenantId: string | null - defaultValues: { - globalUserId: string - email: string - firstName: string - lastName: string - csrfToken: string - } + defaultUserValues: DefaultUserValues constructor(openServer = true) { if (openServer) { @@ -83,10 +83,10 @@ class TestConfiguration { this.appId = null this.allApps = [] this.tenantId = null - this.defaultValues = this.populateDefaultValues() + this.defaultUserValues = this.populateDefaultUserValues() } - populateDefaultValues() { + populateDefaultUserValues(): DefaultUserValues { return { globalUserId: `us_${faker.datatype.uuid()}`, email: faker.internet.email(), @@ -118,10 +118,10 @@ class TestConfiguration { getUserDetails() { return { - globalId: this.defaultValues.globalUserId, - email: this.defaultValues.email, - firstName: this.defaultValues.firstName, - lastName: this.defaultValues.lastName, + globalId: this.defaultUserValues.globalUserId, + email: this.defaultUserValues.email, + firstName: this.defaultUserValues.firstName, + lastName: this.defaultUserValues.lastName, } } @@ -147,7 +147,7 @@ class TestConfiguration { // use a new id as the name to avoid name collisions async init(appName = newid()) { - this.defaultValues = this.populateDefaultValues() + this.defaultUserValues = this.populateDefaultUserValues() if (context.isMultiTenant()) { this.tenantId = `tenant-${newid()}` context.updateTenantId(this.tenantId) @@ -201,12 +201,12 @@ class TestConfiguration { // USER / AUTH async globalUser({ - id = this.defaultValues.globalUserId, - firstName = this.defaultValues.firstName, - lastName = this.defaultValues.lastName, + id = this.defaultUserValues.globalUserId, + firstName = this.defaultUserValues.firstName, + lastName = this.defaultUserValues.lastName, builder = true, admin = false, - email = this.defaultValues.email, + email = this.defaultUserValues.email, roles, }: any = {}) { return tenancy.doWithGlobalDB(this.getTenantId(), async (db: any) => { @@ -227,7 +227,7 @@ class TestConfiguration { await sessions.createASession(id, { sessionId: "sessionid", tenantId: this.getTenantId(), - csrfToken: this.defaultValues.csrfToken, + csrfToken: this.defaultUserValues.csrfToken, }) if (builder) { user.builder = { global: true } @@ -249,9 +249,9 @@ class TestConfiguration { async createUser( id = null, - firstName = this.defaultValues.firstName, - lastName = this.defaultValues.lastName, - email = this.defaultValues.email, + firstName = this.defaultUserValues.firstName, + lastName = this.defaultUserValues.lastName, + email = this.defaultUserValues.email, builder = true, admin = false, roles = {} @@ -321,7 +321,7 @@ class TestConfiguration { defaultHeaders(extras = {}) { const tenantId = this.getTenantId() const authObj: AuthToken = { - userId: this.defaultValues.globalUserId, + userId: this.defaultUserValues.globalUserId, sessionId: "sessionid", tenantId, } @@ -337,7 +337,7 @@ class TestConfiguration { `${constants.Cookie.Auth}=${authToken}`, `${constants.Cookie.CurrentApp}=${appToken}`, ], - [constants.Header.CSRF_TOKEN]: this.defaultValues.csrfToken, + [constants.Header.CSRF_TOKEN]: this.defaultUserValues.csrfToken, ...extras, } if (this.appId) { @@ -366,7 +366,7 @@ class TestConfiguration { } async roleHeaders({ - email = this.defaultValues.email, + email = this.defaultUserValues.email, roleId = roles.BUILTIN_ROLE_IDS.ADMIN, builder = false, prodApp = true, @@ -376,7 +376,7 @@ class TestConfiguration { // API - async generateApiKey(userId = this.defaultValues.globalUserId) { + async generateApiKey(userId = this.defaultUserValues.globalUserId) { return tenancy.doWithGlobalDB(this.getTenantId(), async (db: any) => { const id = dbCore.generateDevInfoID(userId) let devInfo diff --git a/yarn.lock b/yarn.lock index 60cb598a60..f80cde1b6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -986,6 +986,11 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@types/cookiejar@*": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" + integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -1001,6 +1006,11 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== +"@types/node@*": + version "18.11.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" + integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== + "@types/node@>= 8": version "18.0.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" @@ -1011,6 +1021,21 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/superagent@*": + version "4.1.16" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.16.tgz#12c9c16f232f9d89beab91d69368f96ce8e2d881" + integrity sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ== + dependencies: + "@types/cookiejar" "*" + "@types/node" "*" + +"@types/supertest@^2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" + integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== + dependencies: + "@types/superagent" "*" + "@typescript-eslint/parser@5.45.0": version "5.45.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.0.tgz#b18a5f6b3cf1c2b3e399e9d2df4be40d6b0ddd0e"