From d775ccd8bc976d66a0a22b4d15b61039e23dc560 Mon Sep 17 00:00:00 2001 From: Ayobami Akingbade Date: Tue, 19 Dec 2023 16:00:04 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(cache):=20add=20d?= =?UTF-8?q?omain=20to=20cache=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/lib/cache/AbstractCacheService.ts | 17 +++++++++----- src/backend/lib/cache/MemoryCacheAdaptor.ts | 8 ++++++- .../lib/cache/__tests__/CacheAdaptor.spec.ts | 12 +++++----- .../AbstractConfigDataPersistenceService.ts | 22 +++++++++++-------- .../DatabaseConfigDataPersistenceAdaptor.ts | 2 +- .../JsonFileConfigDataPersistenceAdaptor.ts | 2 +- .../MemoryConfigDataPersistenceAdaptor.ts | 2 +- .../RedisConfigDataPersistenceAdaptor.ts | 2 +- src/backend/users/users.service.ts | 12 +++++----- 9 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/backend/lib/cache/AbstractCacheService.ts b/src/backend/lib/cache/AbstractCacheService.ts index 3e2f8cafb..f9fe1992e 100644 --- a/src/backend/lib/cache/AbstractCacheService.ts +++ b/src/backend/lib/cache/AbstractCacheService.ts @@ -1,3 +1,4 @@ +import { ConfigDomain } from "../config-persistence/types"; import { ConfigApiService } from "../config/config.service"; export abstract class AbstractCacheService { @@ -9,8 +10,8 @@ export abstract class AbstractCacheService { public abstract setup(): Promise; - private prefixKey(key: string) { - return `__dp__:${key}`; + private prefixKey(key: string, domain: ConfigDomain) { + return `__dp__:${domain}:${key}`; } protected abstract pullItem(key: string): Promise; @@ -19,14 +20,18 @@ export abstract class AbstractCacheService { protected abstract _clearItem(key: string): Promise; - async clearItem(rawKey: string) { - await this._clearItem(this.prefixKey(rawKey)); + async clearItem(rawKey: string, domain: ConfigDomain) { + await this._clearItem(this.prefixKey(rawKey, domain)); } abstract purge(): Promise; - async getItem(rawKey: string, fetcher: () => Promise) { - const key = this.prefixKey(rawKey); + async getItem( + rawKey: string, + domain: ConfigDomain, + fetcher: () => Promise + ) { + const key = this.prefixKey(rawKey, domain); const data = await this.pullItem(key); diff --git a/src/backend/lib/cache/MemoryCacheAdaptor.ts b/src/backend/lib/cache/MemoryCacheAdaptor.ts index 91c173966..c97b7cd19 100644 --- a/src/backend/lib/cache/MemoryCacheAdaptor.ts +++ b/src/backend/lib/cache/MemoryCacheAdaptor.ts @@ -15,7 +15,13 @@ export class MemoryCacheAdaptor extends AbstractCacheService { } async pullItem(key: string): Promise { - return MemoryCacheAdaptor.data[key] as T; + const data = MemoryCacheAdaptor.data[key] as T; + + if (!data) { + return data; + } + + return JSON.parse(JSON.stringify(data)); } async persistData(key: string, data: unknown): Promise { diff --git a/src/backend/lib/cache/__tests__/CacheAdaptor.spec.ts b/src/backend/lib/cache/__tests__/CacheAdaptor.spec.ts index c79406b67..361ec4aa8 100644 --- a/src/backend/lib/cache/__tests__/CacheAdaptor.spec.ts +++ b/src/backend/lib/cache/__tests__/CacheAdaptor.spec.ts @@ -27,13 +27,13 @@ const CACHE_ADAPTORS: { describe.each(CACHE_ADAPTORS)("$title cache adaptor", ({ adaptor }) => { beforeAll(async () => { await adaptor.setup(); - await adaptor.clearItem("foo"); + await adaptor.clearItem("foo", "temp-storage"); }); it("should call fetcher the first time", async () => { const implementationMock = jest.fn(); - const data = await adaptor.getItem("foo", async () => { + const data = await adaptor.getItem("foo", "temp-storage", async () => { implementationMock(); return { foo: "bar" }; }); @@ -45,7 +45,7 @@ describe.each(CACHE_ADAPTORS)("$title cache adaptor", ({ adaptor }) => { it("should not call fetcher the next time", async () => { const implementationMock = jest.fn(); - const data = await adaptor.getItem("foo", async () => { + const data = await adaptor.getItem("foo", "temp-storage", async () => { implementationMock(); return { foo: "wrong-value" }; }); @@ -57,9 +57,9 @@ describe.each(CACHE_ADAPTORS)("$title cache adaptor", ({ adaptor }) => { it("should call fetcher after clearing key", async () => { const implementationMock = jest.fn(); - await adaptor.clearItem("foo"); + await adaptor.clearItem("foo", "temp-storage"); - const data = await adaptor.getItem("foo", async () => { + const data = await adaptor.getItem("foo", "temp-storage", async () => { implementationMock(); return { foo: "new-value" }; }); @@ -73,7 +73,7 @@ describe.each(CACHE_ADAPTORS)("$title cache adaptor", ({ adaptor }) => { await adaptor.purge(); - const data = await adaptor.getItem("foo", async () => { + const data = await adaptor.getItem("foo", "temp-storage", async () => { implementationMock(); return { foo: "new-value" }; }); diff --git a/src/backend/lib/config-persistence/AbstractConfigDataPersistenceService.ts b/src/backend/lib/config-persistence/AbstractConfigDataPersistenceService.ts index a51a76465..553545a6e 100644 --- a/src/backend/lib/config-persistence/AbstractConfigDataPersistenceService.ts +++ b/src/backend/lib/config-persistence/AbstractConfigDataPersistenceService.ts @@ -24,25 +24,25 @@ export abstract class AbstractConfigDataPersistenceService { return `${key}__${secondaryKey}`; } - public abstract _getItem(key: string): Promise; + protected abstract _getItem(key: string): Promise; - public abstract _persistItem(key: string, data: T): Promise; + protected abstract _persistItem(key: string, data: T): Promise; - public abstract _removeItem(key: string): Promise; + protected abstract _removeItem(key: string): Promise; public async getItem(key: string) { - return await cacheService.getItem(key, async () => { - return this._getItem(key); + return await cacheService.getItem(key, this._configDomain, async () => { + return await this._getItem(key); }); } public async persistItem(key: string, data: T) { - await cacheService.clearItem(key); + await cacheService.clearItem(key, this._configDomain); await this._persistItem(key, data); } public async removeItem(key: string) { - await cacheService.clearItem(key); + await cacheService.clearItem(key, this._configDomain); await this._removeItem(key); } @@ -71,12 +71,16 @@ export abstract class AbstractConfigDataPersistenceService { } public async resetState(keyField: keyof T, data: T[]) { - await cacheService.purge(); await this.resetToEmpty(); await this.saveAllItems(keyField, data); } protected abstract saveAllItems(keyField: keyof T, data: T[]): Promise; - public abstract resetToEmpty(): Promise; + public async resetToEmpty() { + await cacheService.purge(); + await this._resetToEmpty(); + } + + protected abstract _resetToEmpty(): Promise; } diff --git a/src/backend/lib/config-persistence/DatabaseConfigDataPersistenceAdaptor.ts b/src/backend/lib/config-persistence/DatabaseConfigDataPersistenceAdaptor.ts index ee66ba91d..f428975ee 100644 --- a/src/backend/lib/config-persistence/DatabaseConfigDataPersistenceAdaptor.ts +++ b/src/backend/lib/config-persistence/DatabaseConfigDataPersistenceAdaptor.ts @@ -61,7 +61,7 @@ export class DatabaseConfigDataPersistenceAdaptor< await this.getDbInstance(); } - async resetToEmpty() { + async _resetToEmpty() { await (await this.getDbInstance())(CONFIG_TABLE_NAME) .where("domain", "=", this._configDomain) .del(); diff --git a/src/backend/lib/config-persistence/JsonFileConfigDataPersistenceAdaptor.ts b/src/backend/lib/config-persistence/JsonFileConfigDataPersistenceAdaptor.ts index a4a2dfd31..a4573d80e 100644 --- a/src/backend/lib/config-persistence/JsonFileConfigDataPersistenceAdaptor.ts +++ b/src/backend/lib/config-persistence/JsonFileConfigDataPersistenceAdaptor.ts @@ -29,7 +29,7 @@ export class JsonFileConfigDataPersistenceAdaptor< ); }; - async resetToEmpty() { + async _resetToEmpty() { fs.removeSync(this.pathToConfigDomain(this._configDomain)); } diff --git a/src/backend/lib/config-persistence/MemoryConfigDataPersistenceAdaptor.ts b/src/backend/lib/config-persistence/MemoryConfigDataPersistenceAdaptor.ts index ce5b2df4a..40f7141bf 100644 --- a/src/backend/lib/config-persistence/MemoryConfigDataPersistenceAdaptor.ts +++ b/src/backend/lib/config-persistence/MemoryConfigDataPersistenceAdaptor.ts @@ -30,7 +30,7 @@ export class MemoryConfigDataPersistenceAdaptor< return MemoryConfigDataPersistenceAdaptor.data[configDomain]; } - async resetToEmpty() { + async _resetToEmpty() { MemoryConfigDataPersistenceAdaptor.data[this._configDomain] = {}; } diff --git a/src/backend/lib/config-persistence/RedisConfigDataPersistenceAdaptor.ts b/src/backend/lib/config-persistence/RedisConfigDataPersistenceAdaptor.ts index 65e622758..2c978d51b 100644 --- a/src/backend/lib/config-persistence/RedisConfigDataPersistenceAdaptor.ts +++ b/src/backend/lib/config-persistence/RedisConfigDataPersistenceAdaptor.ts @@ -40,7 +40,7 @@ export class RedisConfigDataPersistenceAdaptor< super(configDomain, _configApiService); } - async resetToEmpty() { + async _resetToEmpty() { await (await this.getRedisInstance()).del(this.wrapWithConfigDomain()); } diff --git a/src/backend/users/users.service.ts b/src/backend/users/users.service.ts index 6e7b86391..b1ae5b12c 100644 --- a/src/backend/users/users.service.ts +++ b/src/backend/users/users.service.ts @@ -4,6 +4,7 @@ import { HashService } from "backend/lib/hash/hash.service"; import { IApplicationService } from "backend/types"; import { IAccountUser, IAccountProfile } from "shared/types/user"; import { ISuccessfullAuthenticationResponse } from "shared/types/auth/portal"; +import { noop } from "shared/lib/noop"; import { getPortalAuthenticationResponse } from "./portal"; import { generateAuthTokenForUsername } from "./utils"; import { usersPersistenceService } from "./shared"; @@ -54,10 +55,8 @@ export class UsersApiService implements IApplicationService { async listUsers() { const users = await this._usersPersistenceService.getAllItems(); - return users.map((user) => { - const userCopy = { ...user }; - delete userCopy.password; - + return users.map(({ password, ...userCopy }) => { + noop(password); return userCopy; }); } @@ -73,8 +72,9 @@ export class UsersApiService implements IApplicationService { } async getUser(username: string): Promise { - const user = await this._usersPersistenceService.getItemOrFail(username); - delete user.password; + const { password, ...user } = + await this._usersPersistenceService.getItemOrFail(username); + noop(password); return user; }