import { Injectable , Logger } from '@nestjs/common' ; import { InjectRepository } from '@nestjs/typeorm' ; import { Subject } from 'rxjs' ; import { concatMap } from 'rxjs/operators' ; import { Connection , EntityManager , Repository } from 'typeorm' ; import { AccountDeployerService } from '../account-deployer/account-deployer.service' ; import { AccountAlreadyExistsError } from './account-already-exists.error' ; import { Account } from './account.entity' ; @ Injectable ( ) export class AccountService { private readonly logger = new Logger ( AccountService . name ) ; private readonly requestQueue = new Subject < string > ( ) ; constructor ( @ InjectRepository ( Account ) private readonly repository , private readonly connection , private readonly accountDeployerService ) { this . requestQueue . pipe ( concatMap ( ( id ) => this . process ( id ) ) ) . subscribe ( ) ; } public async create ( userId ) { this . logger . debug ( `User with userId=${ userId } requesting account creation` ) ; const account = await this . repository . findOne ( null , { where : { userId } } ) ; if ( account ) { throw new AccountAlreadyExistsError ( userId ) ; } this . requestQueue . next ( userId ) ; } public async createSynchronously ( userId ) { this . logger . debug ( `User with userId=${ userId } requesting account creation` ) ; await this . process ( userId ) ; } private async process ( userId ) { this . logger . debug ( `Processing account creation for user with userId=${ userId }` ) ; const accountExists = await this . repository . findOne ( null , { where : { userId } } ) ; if ( accountExists ) { this . logger . error ( `Account for user with userId=${ userId } has already been deployed. Skipping.` ) ; return ; } const address = await this . accountDeployerService . deployAccount ( ) ; await this . repository . save ( { userId , address } ) ; this . logger . debug ( `Account deployed ${ JSON . stringify ( address ) } ` ) ; } public async findByAddress ( address , transaction ? ) { this . logger . debug ( `Requesting account for address ${ address }` ) ; const manager = transaction || this . connection . manager ; const repository = manager . getRepository < Account > ( Account ) ; const account = await repository . findOne ( { where : { address } } ) ; this . logger . debug ( `Returning ${ JSON . stringify ( account ) }` ) ; return account ; } public async getAccount ( userId ) { return this . repository . findOne ( null , { where : { userId } } ) ; } }