From 1cfd3ca130199e29f5136caf16cb8a054a747af6 Mon Sep 17 00:00:00 2001 From: William Calderipe Date: Tue, 20 Feb 2024 09:56:36 +0100 Subject: [PATCH] Refactor database seed procedure (#125) --- .../module/persistence/persistence.module.ts | 5 +- .../src/shared/module/persistence/seed.ts | 10 +-- .../__test__/unit/seeder.service.spec.ts | 53 ++++++++++++ .../persistence/service/seed.service.ts | 7 ++ .../persistence/service/seeder.service.ts | 38 +++++++++ .../entity/persistence/entity-store.seed.ts | 16 ++-- .../persistence/transfer-tracking.seed.ts | 81 +++++++++++++++++++ .../persistence/transfer.seed.ts | 75 ----------------- .../transfer-tracking.module.ts | 3 +- 9 files changed, 194 insertions(+), 94 deletions(-) create mode 100644 apps/orchestration/src/shared/module/persistence/service/__test__/unit/seeder.service.spec.ts create mode 100644 apps/orchestration/src/shared/module/persistence/service/seed.service.ts create mode 100644 apps/orchestration/src/shared/module/persistence/service/seeder.service.ts create mode 100644 apps/orchestration/src/transfer-tracking/persistence/transfer-tracking.seed.ts delete mode 100644 apps/orchestration/src/transfer-tracking/persistence/transfer.seed.ts diff --git a/apps/orchestration/src/shared/module/persistence/persistence.module.ts b/apps/orchestration/src/shared/module/persistence/persistence.module.ts index 95d5dcd4c..d1a70ceb9 100644 --- a/apps/orchestration/src/shared/module/persistence/persistence.module.ts +++ b/apps/orchestration/src/shared/module/persistence/persistence.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common' import { PrismaService } from './service/prisma.service' +import { SeederService } from './service/seeder.service' import { TestPrismaService } from './service/test-prisma.service' @Module({ - exports: [PrismaService, TestPrismaService], - providers: [PrismaService, TestPrismaService] + exports: [PrismaService, TestPrismaService, SeederService], + providers: [PrismaService, TestPrismaService, SeederService] }) export class PersistenceModule {} diff --git a/apps/orchestration/src/shared/module/persistence/seed.ts b/apps/orchestration/src/shared/module/persistence/seed.ts index 5cdf6539f..622fc10bb 100644 --- a/apps/orchestration/src/shared/module/persistence/seed.ts +++ b/apps/orchestration/src/shared/module/persistence/seed.ts @@ -3,8 +3,7 @@ import { NestFactory } from '@nestjs/core' import { Organization, PrismaClient } from '@prisma/client/orchestration' import { ORGANIZATION } from 'packages/authz-shared/src/lib/dev.fixture' import { OrchestrationModule } from '../../../orchestration.module' -import { EntityStoreSeed } from '../../../store/entity/persistence/entity-store.seed' -import { germinate as germinateTransferTrackingModule } from '../../../transfer-tracking/persistence/transfer.seed' +import { SeederService } from './service/seeder.service' const now = new Date() const prisma = new PrismaClient() @@ -24,7 +23,7 @@ async function main() { // // See https://docs.nestjs.com/standalone-applications const application = await NestFactory.createApplicationContext(OrchestrationModule) - const entityStoreSeed = application.get(EntityStoreSeed) + const seeder = application.get(SeederService) logger.log('Seeding Orchestration database') @@ -36,11 +35,8 @@ async function main() { } try { - await entityStoreSeed.germinate() - // TODO (@wcalderipe, 15/02/24): Refactor to a seeder provider like entity store. - await germinateTransferTrackingModule(prisma) - logger.log('Orchestration database germinated 🌱') + await seeder.seed() } finally { await application.close() } diff --git a/apps/orchestration/src/shared/module/persistence/service/__test__/unit/seeder.service.spec.ts b/apps/orchestration/src/shared/module/persistence/service/__test__/unit/seeder.service.spec.ts new file mode 100644 index 000000000..b7f64df1e --- /dev/null +++ b/apps/orchestration/src/shared/module/persistence/service/__test__/unit/seeder.service.spec.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common' +import { Test } from '@nestjs/testing' +import { SeedService } from '../../seed.service' +import { SeederService } from '../../seeder.service' + +@Injectable() +class TestSeedServiceOne extends SeedService { + override async germinate(): Promise {} +} + +@Injectable() +class TestSeedServiceTwo extends SeedService { + override async germinate(): Promise {} +} + +describe(SeederService.name, () => { + let service: SeederService + let seedServiceOne: TestSeedServiceOne + let seedServiceTwo: TestSeedServiceTwo + + beforeEach(async () => { + seedServiceOne = new TestSeedServiceOne() + seedServiceTwo = new TestSeedServiceTwo() + + const module = await Test.createTestingModule({ + providers: [ + SeederService, + { + provide: TestSeedServiceOne, + useValue: seedServiceOne + }, + { + provide: TestSeedServiceTwo, + useValue: seedServiceTwo + } + ] + }).compile() + + service = module.get(SeederService) + }) + + describe('seed', () => { + it('discovers providers instance of SeedService and calls their germinate method', async () => { + jest.spyOn(seedServiceOne, 'germinate') + jest.spyOn(seedServiceTwo, 'germinate') + + await service.seed() + + expect(seedServiceOne.germinate).toHaveBeenCalledTimes(1) + expect(seedServiceTwo.germinate).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/apps/orchestration/src/shared/module/persistence/service/seed.service.ts b/apps/orchestration/src/shared/module/persistence/service/seed.service.ts new file mode 100644 index 000000000..194bdce1d --- /dev/null +++ b/apps/orchestration/src/shared/module/persistence/service/seed.service.ts @@ -0,0 +1,7 @@ +import { NotImplementedException } from '@nestjs/common' + +export abstract class SeedService { + germinate(): Promise { + throw new NotImplementedException() + } +} diff --git a/apps/orchestration/src/shared/module/persistence/service/seeder.service.ts b/apps/orchestration/src/shared/module/persistence/service/seeder.service.ts new file mode 100644 index 000000000..7f461b734 --- /dev/null +++ b/apps/orchestration/src/shared/module/persistence/service/seeder.service.ts @@ -0,0 +1,38 @@ +import { Inject, Injectable, Logger } from '@nestjs/common' +import { ModulesContainer } from '@nestjs/core' +import { SeedService } from './seed.service' + +@Injectable() +export class SeederService { + private logger = new Logger(SeedService.name) + + constructor(@Inject(ModulesContainer) private modulesContainer: ModulesContainer) {} + + async seed() { + for (const seed of this.getSeedServices()) { + const name = seed.constructor.name + + this.logger.log(`Germinating ${name}`) + + let error: unknown | null = null + try { + await seed.germinate() + } catch (err) { + this.logger.error('Error while germinating ${name}') + + error = err + } + + if (!error) { + this.logger.log(`${name} germinated`) + } + } + } + + private getSeedServices(): SeedService[] { + return Array.from(this.modulesContainer.values()) + .flatMap((module) => Array.from(module.providers.values())) + .map((provider) => provider.instance) + .filter((instance): instance is SeedService => instance instanceof SeedService) + } +} diff --git a/apps/orchestration/src/store/entity/persistence/entity-store.seed.ts b/apps/orchestration/src/store/entity/persistence/entity-store.seed.ts index 0c9fd9cd5..2802f126e 100644 --- a/apps/orchestration/src/store/entity/persistence/entity-store.seed.ts +++ b/apps/orchestration/src/store/entity/persistence/entity-store.seed.ts @@ -1,8 +1,8 @@ import { FIXTURE } from '@narval/authz-shared' -import { Injectable, Logger } from '@nestjs/common' +import { Injectable } from '@nestjs/common' import { compact } from 'lodash/fp' import { ORGANIZATION } from 'packages/authz-shared/src/lib/dev.fixture' -import { Seeder } from '../../../shared/module/persistence/persistence.type' +import { SeedService } from '../../../shared/module/persistence/service/seed.service' import { AddressBookRepository } from './repository/address-book.repository' import { CredentialRepository } from './repository/credential.repository' import { TokenRepository } from './repository/token.repository' @@ -13,9 +13,7 @@ import { WalletGroupRepository } from './repository/wallet-group.repository' import { WalletRepository } from './repository/wallet.repository' @Injectable() -export class EntityStoreSeed implements Seeder { - private logger = new Logger(EntityStoreSeed.name) - +export class EntityStoreSeed extends SeedService { constructor( private addressBookRepository: AddressBookRepository, private credentialRepository: CredentialRepository, @@ -25,11 +23,11 @@ export class EntityStoreSeed implements Seeder { private userWalletRepository: UserWalletRepository, private walletGroupRepository: WalletGroupRepository, private walletRepository: WalletRepository - ) {} - - async germinate(): Promise { - this.logger.log('Germinating the Entity Store module database') + ) { + super() + } + override async germinate(): Promise { await Promise.all( Object.values(FIXTURE.USER).map((entity) => this.userRepository.create(FIXTURE.ORGANIZATION.uid, entity)) ) diff --git a/apps/orchestration/src/transfer-tracking/persistence/transfer-tracking.seed.ts b/apps/orchestration/src/transfer-tracking/persistence/transfer-tracking.seed.ts new file mode 100644 index 000000000..d8eb12bd8 --- /dev/null +++ b/apps/orchestration/src/transfer-tracking/persistence/transfer-tracking.seed.ts @@ -0,0 +1,81 @@ +import { Injectable } from '@nestjs/common' +import { getTime, subHours } from 'date-fns' +import { POLYGON } from '../../orchestration.constant' +import { PrismaService } from '../../shared/module/persistence/service/prisma.service' +import { SeedService } from '../../shared/module/persistence/service/seed.service' + +@Injectable() +export class TransferTrackingSeed extends SeedService { + constructor(private prismaService: PrismaService) { + super() + } + + override async germinate(): Promise { + const now = getTime(new Date()) + const twentyHoursAgo = subHours(now, 20) + const orgId = '7d704a62-d15e-4382-a826-1eb41563043b' + const rates = { + 'fiat:usd': '0.99', + 'fiat:eur': '1.10' + } + + // TODO (@wcalderipe, 19/02/24): Refactor to use the repository instead of + // writing directly through the model. + await this.prismaService.approvedTransfer.createMany({ + data: [ + { + orgId, + rates, + id: '107b07ec-3e8d-440f-9e4a-d145dcd53324', + requestId: '623121f4-439c-42ac-aee3-d38f94f6f886', + amount: '3000000000', + from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', + to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', + chainId: 137, + token: POLYGON.coin.id, + initiatedBy: 'matt@narval.xyz', + createdAt: twentyHoursAgo + }, + { + orgId, + rates, + id: '2b697c4b-4675-4762-b68d-89baaa1b5cb8', + requestId: 'e615f495-fa7b-4ee8-b4c6-927ce72a9107', + amount: '2000000000', + from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', + to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', + chainId: 137, + token: POLYGON.coin.id, + initiatedBy: 'matt@narval.xyz', + createdAt: twentyHoursAgo + }, + { + orgId, + rates, + id: '4810c9de-ebc6-4788-ba5c-e9a899273c86', + requestId: '1d88e7e4-49ab-4f83-9e55-c3e089a4a252', + amount: '1500000000', + from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', + to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', + chainId: 137, + token: POLYGON.coin.id, + initiatedBy: 'matt@narval.xyz', + createdAt: twentyHoursAgo + }, + { + orgId, + rates, + id: 'a8116490-2c0e-4315-892f-f4795ff7eda9', + requestId: 'c91393e3-1cbb-49e6-888c-50f188f22f7e', + amount: '1000000000', + from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', + to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', + chainId: 137, + token: POLYGON.coin.id, + initiatedBy: 'matt@narval.xyz', + createdAt: twentyHoursAgo + } + ] + }) + } +} diff --git a/apps/orchestration/src/transfer-tracking/persistence/transfer.seed.ts b/apps/orchestration/src/transfer-tracking/persistence/transfer.seed.ts deleted file mode 100644 index 23cab0265..000000000 --- a/apps/orchestration/src/transfer-tracking/persistence/transfer.seed.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Logger } from '@nestjs/common' -import { PrismaClient } from '@prisma/client/orchestration' -import { getTime, subHours } from 'date-fns' -import { POLYGON } from '../../orchestration.constant' - -export const germinate = async (prisma: PrismaClient): Promise => { - const logger = new Logger('TransferFeedSeed') - - logger.log('Germinating the Transfer Feed module database') - - const now = getTime(new Date()) - const twentyHoursAgo = subHours(now, 20) - const orgId = '7d704a62-d15e-4382-a826-1eb41563043b' - const rates = { - 'fiat:usd': '0.99', - 'fiat:eur': '1.10' - } - - await prisma.approvedTransfer.createMany({ - data: [ - { - orgId, - rates, - id: '107b07ec-3e8d-440f-9e4a-d145dcd53324', - requestId: '623121f4-439c-42ac-aee3-d38f94f6f886', - amount: '3000000000', - from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', - to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', - chainId: 137, - token: POLYGON.coin.id, - initiatedBy: 'matt@narval.xyz', - createdAt: twentyHoursAgo - }, - { - orgId, - rates, - id: '2b697c4b-4675-4762-b68d-89baaa1b5cb8', - requestId: 'e615f495-fa7b-4ee8-b4c6-927ce72a9107', - amount: '2000000000', - from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', - to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', - chainId: 137, - token: POLYGON.coin.id, - initiatedBy: 'matt@narval.xyz', - createdAt: twentyHoursAgo - }, - { - orgId, - rates, - id: '4810c9de-ebc6-4788-ba5c-e9a899273c86', - requestId: '1d88e7e4-49ab-4f83-9e55-c3e089a4a252', - amount: '1500000000', - from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', - to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', - chainId: 137, - token: POLYGON.coin.id, - initiatedBy: 'matt@narval.xyz', - createdAt: twentyHoursAgo - }, - { - orgId, - rates, - id: 'a8116490-2c0e-4315-892f-f4795ff7eda9', - requestId: 'c91393e3-1cbb-49e6-888c-50f188f22f7e', - amount: '1000000000', - from: 'eip155:137:0x90d03a8971a2faa19a9d7ffdcbca28fe826a289b', - to: 'eip155:137:0x08a08d0504d4f3363a5b7fda1f5fff1c7bca8ad4', - chainId: 137, - token: POLYGON.coin.id, - initiatedBy: 'matt@narval.xyz', - createdAt: twentyHoursAgo - } - ] - }) -} diff --git a/apps/orchestration/src/transfer-tracking/transfer-tracking.module.ts b/apps/orchestration/src/transfer-tracking/transfer-tracking.module.ts index b60e09724..a2bb0ec08 100644 --- a/apps/orchestration/src/transfer-tracking/transfer-tracking.module.ts +++ b/apps/orchestration/src/transfer-tracking/transfer-tracking.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common' import { PersistenceModule } from '../shared/module/persistence/persistence.module' import { TransferTrackingService } from './core/service/transfer-tracking.service' import { TransferRepository } from './persistence/repository/transfer.repository' +import { TransferTrackingSeed } from './persistence/transfer-tracking.seed' @Module({ imports: [PersistenceModule], - providers: [TransferRepository, TransferTrackingService], + providers: [TransferRepository, TransferTrackingService, TransferTrackingSeed], exports: [TransferTrackingService] }) export class TransferTrackingModule {}