Skip to content

Commit

Permalink
Fix transaction handler and update mocks to use the new transaction m…
Browse files Browse the repository at this point in the history
…ethod. (#56)

Signed-off-by: Jeff Kennedy <[email protected]>
  • Loading branch information
Jeff Kennedy authored Mar 16, 2021
1 parent ca049e2 commit be92e00
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 64 deletions.
27 changes: 0 additions & 27 deletions src/db/base..db.gateway.ts

This file was deleted.

17 changes: 7 additions & 10 deletions src/db/external.id.db.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@ import { SecurityUtility } from 'protocol-common/security.utility';
import { ProtocolException } from 'protocol-common/protocol.exception';
import { ProtocolErrorCode } from 'protocol-common/protocol.errorcode';
import { CreateFiltersDto } from '../escrow/dto/create.filters.dto';
import { BaseDbGateway } from './base..db.gateway';

@Injectable()
export class ExternalIdDbGateway extends BaseDbGateway<ExternalId> {
export class ExternalIdDbGateway {

constructor(
@InjectRepository(ExternalId)
externalIdRepository: Repository<ExternalId>
) {
super(externalIdRepository);
}
private readonly externalIdRepository: Repository<ExternalId>
) { }

/**
* This function will attempt to retrieve all external IDs that correspond to an id type -> value entry in the ids map. If the throwIfEmpty flag
Expand All @@ -42,7 +39,7 @@ export class ExternalIdDbGateway extends BaseDbGateway<ExternalId> {
Array.from(hashedIds.entries())
.map(async ([idType, idValues]: [string, string[]]) => {
try {
return this.repository.find({
return this.externalIdRepository.find({
external_id: In(idValues),
external_id_type: idType
});
Expand Down Expand Up @@ -82,7 +79,7 @@ export class ExternalIdDbGateway extends BaseDbGateway<ExternalId> {
const externalIds: ExternalId[] = this.buildExternalIds(did, filters);
let results: ExternalId[] = [];
try {
results = await this.repository.save(externalIds);
results = await this.externalIdRepository.save(externalIds);
} catch (e) {
const msg = externalIds.map((id: ExternalId) => `${id.external_id_type}`).join('; ');
throw new ProtocolException(ProtocolErrorCode.DUPLICATE_ENTRY, `Entry already exists for ${msg}`);
Expand All @@ -96,15 +93,15 @@ export class ExternalIdDbGateway extends BaseDbGateway<ExternalId> {
* will throw a ProtocolException.
*/
private async getOrCreateExternalId(externalId: ExternalId): Promise<ExternalId> {
return this.runInTransaction(async (entityManager: EntityManager) => {
return this.externalIdRepository.manager.transaction(async (entityManager: EntityManager) => {
const dbExternalId: ExternalId | undefined = await entityManager.findOne(ExternalId, {
did: externalId.did,
external_id: externalId.external_id,
external_id_type: externalId.external_id_type
});
if (!dbExternalId) {
try {
return await this.repository.save(externalId);
return await entityManager.save(externalId);
} catch (e) {
throw new ProtocolException(ProtocolErrorCode.DUPLICATE_ENTRY, `Entry already exists for ${externalId.external_id_type}`);
}
Expand Down
17 changes: 7 additions & 10 deletions src/db/sms.otp.db.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,20 @@ import { SecurityUtility } from 'protocol-common/security.utility';
import { ProtocolException } from 'protocol-common/protocol.exception';
import { ProtocolErrorCode } from 'protocol-common/protocol.errorcode';
import { Injectable } from '@nestjs/common';
import { BaseDbGateway } from './base..db.gateway';

@Injectable()
export class SmsOtpDbGateway extends BaseDbGateway<SmsOtp> {
export class SmsOtpDbGateway {

constructor(
@InjectRepository(SmsOtp)
smsOtpRepository: Repository<SmsOtp>
) {
super(smsOtpRepository);
}
private readonly smsOtpRepository: Repository<SmsOtp>
) {}

/**
* Given a did, return the entry corresponding to that did, if one exists. If one does not exist, throw a ProtocolException.
*/
public async fetchSmsOtp(did: string): Promise<SmsOtp> {
const smsOtp: SmsOtp | undefined = await this.repository.findOne({did});
const smsOtp: SmsOtp | undefined = await this.smsOtpRepository.findOne({did});
if (!smsOtp) {
throw new ProtocolException(ProtocolErrorCode.NO_CITIZEN_FOUND, 'No citizen found for given filters');
}
Expand All @@ -34,7 +31,7 @@ export class SmsOtpDbGateway extends BaseDbGateway<SmsOtp> {
*/
public async savePhoneNumber(did: string, phoneNumber: string): Promise<SmsOtp> {
const phoneNumberHash = SecurityUtility.hash32(phoneNumber + process.env.HASH_PEPPER);
return this.runInTransaction(async (entityManager: EntityManager) => {
return this.smsOtpRepository.manager.transaction(async (entityManager: EntityManager) => {
let smsOtp: SmsOtp | undefined = await entityManager.findOne(SmsOtp, {did});
if (!smsOtp) {
smsOtp = new SmsOtp();
Expand All @@ -53,7 +50,7 @@ export class SmsOtpDbGateway extends BaseDbGateway<SmsOtp> {
public saveOtp(did: string, phoneNumber: string, otp: number): Promise<SmsOtp> {
const phoneNumberHash = SecurityUtility.hash32(phoneNumber + process.env.HASH_PEPPER);
const otpExpirationTime = new Date(Date.now() + 15000); // 15 min
return this.runInTransaction(async (entityManager: EntityManager) => {
return this.smsOtpRepository.manager.transaction(async (entityManager: EntityManager) => {
const smsOtp: SmsOtp | undefined = await entityManager.findOne(SmsOtp, {
did,
phone_number_hash: phoneNumberHash
Expand All @@ -78,6 +75,6 @@ export class SmsOtpDbGateway extends BaseDbGateway<SmsOtp> {
otp: null,
otp_expiration_time: null
};
return this.repository.save(expiredSmsOtp);
return this.smsOtpRepository.save(expiredSmsOtp);
}
}
24 changes: 7 additions & 17 deletions test/mock/mock.repository.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { FindConditions } from 'typeorm/find-options/FindConditions';
import { EntityTarget } from 'typeorm/common/EntityTarget';
import { EntityManager } from 'typeorm';

/**
* Provides a mock repository that holds onto a single immutable entity of type T. This mimics how an in-memory update to an entity wouldn't affect
* the underlying stored entity in the db.
*/
export class MockRepository<Entity> {

public readonly queryRunner: MockQueryRunner<Entity>;
public readonly manager: MockEntityManager<Entity>;

constructor(protected entities: Readonly<Entity>[]) {
if (entities.length === 0) {
throw new Error('Must provide Repository with at least 1 entity');
}
this.queryRunner = new MockQueryRunner<Entity>(this);
this.manager = new MockEntityManager<Entity>(this);
}

findOne(conditions?: FindConditions<Entity>): Promise<Entity | undefined> {
Expand All @@ -34,21 +35,6 @@ export class MockRepository<Entity> {
}
}

class MockQueryRunner<Entity> {

public manager: MockEntityManager<Entity>;

constructor(repository: MockRepository<Entity>) {
this.manager = new MockEntityManager<Entity>(repository);
}

startTransaction(): void {}

rollbackTransaction(): void {}

release(): void {}
}

class MockEntityManager<Entity> {

constructor(private readonly repository: MockRepository<Entity>) {}
Expand All @@ -60,4 +46,8 @@ class MockEntityManager<Entity> {
save(entityClass: EntityTarget<Entity>, entity: Entity) {
return this.repository.save(entity);
}

transaction(fun: (entityManager: EntityManager) => Promise<Entity>) {
return fun(this as unknown as EntityManager);
}
}

0 comments on commit be92e00

Please sign in to comment.