Skip to content

Commit

Permalink
Remove cache usage in AddressBooksDatasource, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorgomezv committed Nov 18, 2024
1 parent dc11111 commit 52e1b82
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,14 @@ import { AddressBooksDatasource } from '@/datasources/accounts/address-books/add
import { AddressBookDbMapper } from '@/datasources/accounts/address-books/entities/address-book.db.mapper';
import type { EncryptionApiManager } from '@/datasources/accounts/encryption/encryption-api.manager';
import { LocalEncryptionApiService } from '@/datasources/accounts/encryption/local-encryption-api.service';
import { FakeCacheService } from '@/datasources/cache/__tests__/fake.cache.service';
import { CachedQueryResolver } from '@/datasources/db/v1/cached-query-resolver';
import { PostgresDatabaseMigrator } from '@/datasources/db/v1/postgres-database.migrator';
import { createAddressBookItemDtoBuilder } from '@/domain/accounts/address-books/entities/__tests__/create-address-book.builder';
import { createAccountDtoBuilder } from '@/domain/accounts/entities/__tests__/create-account.dto.builder';
import type { Account } from '@/domain/accounts/entities/account.entity';
import type { ILoggingService } from '@/logging/logging.interface';
import { faker } from '@faker-js/faker/.';
import { randomBytes } from 'crypto';
import type postgres from 'postgres';

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

const mockConfigurationService = jest.mocked({
getOrThrow: jest.fn(),
} as jest.MockedObjectDeep<IConfigurationService>);
Expand All @@ -31,19 +21,16 @@ const mockEncryptionApiManager = jest.mocked({
} as jest.MockedObjectDeep<EncryptionApiManager>);

describe('AddressBooksDataSource', () => {
let fakeCacheService: FakeCacheService;
let sql: postgres.Sql;
let migrator: PostgresDatabaseMigrator;
let target: AddressBooksDatasource;
const testDbFactory = new TestDbFactory();

beforeAll(async () => {
fakeCacheService = new FakeCacheService();
sql = await testDbFactory.createTestDatabase(faker.string.uuid());
migrator = new PostgresDatabaseMigrator(sql);
await migrator.migrate();
mockConfigurationService.getOrThrow.mockImplementation((key) => {
if (key === 'expirationTimeInSeconds.default') return faker.number.int();
if (key === 'application.isProduction') return false;
if (key === 'accounts.encryption.local.algorithm') return 'aes-256-cbc';
if (key === 'accounts.encryption.local.key')
Expand All @@ -56,18 +43,14 @@ describe('AddressBooksDataSource', () => {
);

target = new AddressBooksDatasource(
fakeCacheService,
sql,
new CachedQueryResolver(mockLoggingService, fakeCacheService),
mockEncryptionApiManager,
mockConfigurationService,
new AddressBookDbMapper(mockEncryptionApiManager),
);
});

beforeEach(async () => {
await sql`TRUNCATE TABLE accounts, account_data_settings, address_books CASCADE`;
fakeCacheService.clear();
jest.clearAllMocks();
});

Expand All @@ -78,12 +61,45 @@ describe('AddressBooksDataSource', () => {
describe('createAddressBookItem', () => {
it('should create an address book if it does not exist when adding a new item', async () => {
const createAccountDto = createAccountDtoBuilder().build();
const [account] = await sql<
Account[]
>`INSERT INTO accounts (address, name, name_hash) VALUES (${createAccountDto.address}, ${createAccountDto.name}, ${faker.string.alphanumeric(32)}) RETURNING *`;
const [account] = await sql<Account[]>`
INSERT INTO accounts (address, name, name_hash)
VALUES (
${createAccountDto.address},
${createAccountDto.name},
${faker.string.alphanumeric(32)}
) RETURNING *`;
const createAddressBookItemDto =
createAddressBookItemDtoBuilder().build();

const addressBookItem = await target.createAddressBookItem({
account,
createAddressBookItemDto,
});

expect(addressBookItem).toMatchObject({
id: expect.any(Number),
address: createAddressBookItemDto.address,
name: createAddressBookItemDto.name,
});
expect(await target.getOrCreateAddressBook(account)).toMatchObject({
data: [createAddressBookItemDto],
accountId: account.id,
});
});

it('should create a several address book items', async () => {
const createAccountDto = createAccountDtoBuilder().build();
const [account] = await sql<Account[]>`
INSERT INTO accounts (address, name, name_hash)
VALUES (
${createAccountDto.address},
${createAccountDto.name},
${faker.string.alphanumeric(32)}
) RETURNING *`;
const createAddressBookItemDtos = [
createAddressBookItemDtoBuilder().build(),
createAddressBookItemDtoBuilder().build(),
createAddressBookItemDtoBuilder().build(),
];
await target.createAddressBookItem({
account,
Expand All @@ -93,23 +109,21 @@ describe('AddressBooksDataSource', () => {
data: [createAddressBookItemDtos[0]],
accountId: account.id,
});
});

it('should create a new address book item', async () => {
const createAccountDto = createAccountDtoBuilder().build();
const [account] = await sql<
Account[]
>`INSERT INTO accounts (address, name, name_hash) VALUES (${createAccountDto.address}, ${createAccountDto.name}, ${faker.string.alphanumeric(32)}) RETURNING *`;
const createAddressBookItemDto =
createAddressBookItemDtoBuilder().build();
const addressBookItem = await target.createAddressBookItem({
await target.createAddressBookItem({
account,
createAddressBookItemDto,
createAddressBookItemDto: createAddressBookItemDtos[1],
});
expect(addressBookItem).toMatchObject({
id: expect.any(Number),
address: createAddressBookItemDto.address,
name: createAddressBookItemDto.name,
expect(await target.getOrCreateAddressBook(account)).toMatchObject({
data: [createAddressBookItemDtos[0], createAddressBookItemDtos[1]],
accountId: account.id,
});
await target.createAddressBookItem({
account,
createAddressBookItemDto: createAddressBookItemDtos[2],
});
expect(await target.getOrCreateAddressBook(account)).toMatchObject({
data: createAddressBookItemDtos,
accountId: account.id,
});
});
});
Expand Down
50 changes: 12 additions & 38 deletions src/datasources/accounts/address-books/address-books.datasource.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { IConfigurationService } from '@/config/configuration.service.interface';
import { AddressBookDbMapper } from '@/datasources/accounts/address-books/entities/address-book.db.mapper';
import { AddressBook as DbAddressBook } from '@/datasources/accounts/address-books/entities/address-book.entity';
import { CacheRouter } from '@/datasources/cache/cache.router';
import {
CacheService,
ICacheService,
} from '@/datasources/cache/cache.service.interface';
import { CachedQueryResolver } from '@/datasources/db/v1/cached-query-resolver';
import { ICachedQueryResolver } from '@/datasources/db/v1/cached-query-resolver.interface';
import {
AddressBook,
AddressBookItem,
Expand All @@ -20,25 +12,12 @@ import postgres from 'postgres';

@Injectable()
export class AddressBooksDatasource {
private readonly defaultExpirationTimeInSeconds: number;

constructor(
@Inject(CacheService) private readonly cacheService: ICacheService,
@Inject('DB_INSTANCE') private readonly sql: postgres.Sql,
@Inject(ICachedQueryResolver)
private readonly cachedQueryResolver: CachedQueryResolver,
@Inject(IEncryptionApiManager)
private readonly encryptionApiManager: IEncryptionApiManager,
// @Inject(LoggingService) private readonly loggingService: ILoggingService,
@Inject(IConfigurationService)
private readonly configurationService: IConfigurationService,
private readonly addressBookMapper: AddressBookDbMapper,
) {
this.defaultExpirationTimeInSeconds =
this.configurationService.getOrThrow<number>(
'expirationTimeInSeconds.default',
);
}
) {}

async createAddressBookItem(args: {
account: Account;
Expand All @@ -47,24 +26,18 @@ export class AddressBooksDatasource {
const addressBook = await this.getOrCreateAddressBook(args.account);
const newItem = {
id: addressBook.data.length + 1,
...args.createAddressBookItemDto,
address: args.createAddressBookItemDto.address,
name: args.createAddressBookItemDto.name,
};
addressBook.data.push(newItem);
await this.updateAddressBook(addressBook);
return newItem;
}

async getOrCreateAddressBook(account: Account): Promise<AddressBook> {
const cacheDir = CacheRouter.getAddressBookCacheDir(account.id);
const [dbAddressBook] = await this.cachedQueryResolver.get<DbAddressBook[]>(
{
cacheDir,
query: this.sql<
DbAddressBook[]
>`SELECT * FROM address_books WHERE account_id = ${account.id}`,
ttl: this.defaultExpirationTimeInSeconds,
},
);
const [dbAddressBook] = await this.sql<
DbAddressBook[]
>`SELECT * FROM address_books WHERE account_id = ${account.id}`;
return dbAddressBook
? this.addressBookMapper.map(dbAddressBook)
: this.addressBookMapper.map(await this.createAddressBook({ account }));
Expand All @@ -87,18 +60,19 @@ export class AddressBooksDatasource {
return dbAddressBook;
}

private async updateAddressBook(addressBook: AddressBook): Promise<void> {
private async updateAddressBook(
addressBook: AddressBook,
): Promise<DbAddressBook> {
const encryptionApi = await this.encryptionApiManager.getApi();
const encryptedBlob = await encryptionApi.encryptBlob(addressBook.data);
await this.sql`
const [dbAddressBook] = await this.sql<DbAddressBook[]>`
UPDATE address_books
SET data = ${encryptedBlob.encryptedData},
key = ${encryptedBlob.encryptedDataKey},
iv = ${encryptedBlob.iv}
WHERE id = ${addressBook.id}
RETURNING *;
`;
await this.cacheService.deleteByKey(
CacheRouter.getAddressBookCacheKey(addressBook.accountId),
);
return dbAddressBook;
}
}
9 changes: 0 additions & 9 deletions src/datasources/cache/cache.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export class CacheRouter {
private static readonly ACCOUNT_DATA_SETTINGS_KEY = 'account_data_settings';
private static readonly ACCOUNT_DATA_TYPES_KEY = 'account_data_types';
private static readonly ACCOUNT_KEY = 'account';
private static readonly ADDRESS_BOOK_KEY = 'address_book';
private static readonly ALL_TRANSACTIONS_KEY = 'all_transactions';
private static readonly AUTH_NONCE_KEY = 'auth_nonce';
private static readonly BACKBONE_KEY = 'backbone';
Expand Down Expand Up @@ -545,14 +544,6 @@ export class CacheRouter {
);
}

static getAddressBookCacheKey(accountId: number): string {
return `${CacheRouter.ADDRESS_BOOK_KEY}_${accountId}`;
}

static getAddressBookCacheDir(accountId: number): CacheDir {
return new CacheDir(CacheRouter.getAddressBookCacheKey(accountId), '');
}

static getCounterfactualSafeCacheDir(
chainId: string,
predictedAddress: `0x${string}`,
Expand Down

0 comments on commit 52e1b82

Please sign in to comment.