Skip to content

Commit

Permalink
Inject CachedQueryResolver, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorgomezv committed Jul 23, 2024
1 parent c3d9ba4 commit 530700c
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/datasources/accounts/accounts.datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from '@/datasources/cache/cache.service.interface';
import { MAX_TTL } from '@/datasources/cache/constants';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';
import { ICachedQueryResolver } from '@/datasources/db/cached-query-resolver.interface';
import { AccountDataSetting } from '@/domain/accounts/entities/account-data-setting.entity';
import { AccountDataType } from '@/domain/accounts/entities/account-data-type.entity';
import { Account } from '@/domain/accounts/entities/account.entity';
Expand All @@ -29,6 +30,7 @@ export class AccountsDatasource implements IAccountsDatasource, OnModuleInit {
constructor(
@Inject(CacheService) private readonly cacheService: ICacheService,
@Inject('DB_INSTANCE') private readonly sql: postgres.Sql,
@Inject(ICachedQueryResolver)
private readonly cachedQueryResolver: CachedQueryResolver,
@Inject(LoggingService) private readonly loggingService: ILoggingService,
@Inject(IConfigurationService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ICacheService,
} from '@/datasources/cache/cache.service.interface';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';
import { ICachedQueryResolver } from '@/datasources/db/cached-query-resolver.interface';
import { CounterfactualSafe } from '@/domain/accounts/counterfactual-safes/entities/counterfactual-safe.entity';
import { CreateCounterfactualSafeDto } from '@/domain/accounts/counterfactual-safes/entities/create-counterfactual-safe.dto.entity';
import { Account } from '@/domain/accounts/entities/account.entity';
Expand All @@ -22,6 +23,7 @@ export class CounterfactualSafesDatasource
constructor(
@Inject(CacheService) private readonly cacheService: ICacheService,
@Inject('DB_INSTANCE') private readonly sql: postgres.Sql,
@Inject(ICachedQueryResolver)
private readonly cachedQueryResolver: CachedQueryResolver,
@Inject(LoggingService) private readonly loggingService: ILoggingService,
@Inject(IConfigurationService)
Expand Down
12 changes: 12 additions & 0 deletions src/datasources/db/cached-query-resolver.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CacheDir } from '@/datasources/cache/entities/cache-dir.entity';
import postgres from 'postgres';

export const ICachedQueryResolver = Symbol('ICachedQueryResolver');

export interface ICachedQueryResolver {
get<T extends postgres.MaybeRow[]>(args: {
cacheDir: CacheDir;
query: postgres.PendingQuery<T>;
ttl: number;
}): Promise<T>;
}
74 changes: 74 additions & 0 deletions src/datasources/db/cached-query-resolver.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { fakeJson } from '@/__tests__/faker';
import { FakeCacheService } from '@/datasources/cache/__tests__/fake.cache.service';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';
import { ILoggingService } from '@/logging/logging.interface';
import { faker } from '@faker-js/faker';
import postgres, { MaybeRow } from 'postgres';

const mockLoggingService = jest.mocked({
debug: jest.fn(),
} as jest.MockedObjectDeep<ILoggingService>);

const mockQuery = jest.mocked({ catch: jest.fn() } as jest.MockedObjectDeep<
postgres.PendingQuery<MaybeRow[]>
>);

describe('CachedQueryResolver', () => {
let fakeCacheService: FakeCacheService;
let target: CachedQueryResolver;

beforeAll(() => {
fakeCacheService = new FakeCacheService();
target = new CachedQueryResolver(mockLoggingService, fakeCacheService);
});

afterEach(() => {
jest.clearAllMocks();
fakeCacheService.clear();
});

describe('get', () => {
it('should return the content from cache if it exists', async () => {
const cacheDir = { key: 'key', field: 'field' };
const ttl = faker.number.int({ min: 1, max: 1000 });
const value = fakeJson();
await fakeCacheService.set(cacheDir, JSON.stringify(value), ttl);

const actual = await target.get({
cacheDir,
query: mockQuery,
ttl,
});

expect(actual).toBe(value);
expect(mockLoggingService.debug).toHaveBeenCalledWith({
type: 'cache_hit',
key: 'key',
field: 'field',
});
expect(mockQuery.catch).not.toHaveBeenCalled();
});

it('should execute the query and cache the result if the cache is empty', async () => {
const cacheDir = { key: 'key', field: 'field' };
const ttl = faker.number.int({ min: 1, max: 1000 });
const dbResult = { ...JSON.parse(fakeJson()), count: 1 };
mockQuery.catch.mockResolvedValue(dbResult);

const actual = await target.get({
cacheDir,
query: mockQuery,
ttl,
});

expect(actual).toBe(dbResult);
expect(mockLoggingService.debug).toHaveBeenCalledWith({
type: 'cache_miss',
key: 'key',
field: 'field',
});
const cacheContent = await fakeCacheService.get(cacheDir);
expect(cacheContent).toBe(JSON.stringify(dbResult));
});
});
});
4 changes: 2 additions & 2 deletions src/datasources/db/cached-query-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ICacheService,
} from '@/datasources/cache/cache.service.interface';
import { CacheDir } from '@/datasources/cache/entities/cache-dir.entity';
import { ICachedQueryResolver } from '@/datasources/db/cached-query-resolver.interface';
import { ILoggingService, LoggingService } from '@/logging/logging.interface';
import { asError } from '@/logging/utils';
import {
Expand All @@ -13,8 +14,7 @@ import {
import postgres from 'postgres';

@Injectable()
// TODO: add/implement interface
export class CachedQueryResolver {
export class CachedQueryResolver implements ICachedQueryResolver {
constructor(
@Inject(LoggingService) private readonly loggingService: ILoggingService,
@Inject(CacheService) private readonly cacheService: ICacheService,
Expand Down
8 changes: 7 additions & 1 deletion src/datasources/db/postgres-database.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { IConfigurationService } from '@/config/configuration.service.interface'
import { PostgresDatabaseMigrationHook } from '@/datasources/db/postgres-database.migration.hook';
import fs from 'fs';
import { PostgresDatabaseMigrator } from '@/datasources/db/postgres-database.migrator';
import { ICachedQueryResolver } from '@/datasources/db/cached-query-resolver.interface';
import { CachedQueryResolver } from '@/datasources/db/cached-query-resolver';

function dbFactory(configurationService: IConfigurationService): postgres.Sql {
const caPath = configurationService.get<string>('db.postgres.ssl.caPath');
Expand Down Expand Up @@ -48,9 +50,13 @@ function migratorFactory(sql: postgres.Sql): PostgresDatabaseMigrator {
useFactory: migratorFactory,
inject: ['DB_INSTANCE'],
},
{
provide: ICachedQueryResolver,
useClass: CachedQueryResolver,
},
PostgresDatabaseShutdownHook,
PostgresDatabaseMigrationHook,
],
exports: ['DB_INSTANCE'],
exports: ['DB_INSTANCE', ICachedQueryResolver],
})
export class PostgresDatabaseModule {}

0 comments on commit 530700c

Please sign in to comment.