From 6f0caab6542470e89e32c814b87c9060bfd55dc3 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Wed, 30 Aug 2023 14:53:20 +0200 Subject: [PATCH] feat: get peering details --- packages/backend/src/app.ts | 38 +++++++++-- packages/backend/src/asset/service.test.ts | 16 +++++ packages/backend/src/asset/service.ts | 8 ++- .../backend/src/auto-peering/routes.test.ts | 68 +++++++++++++++++++ packages/backend/src/auto-peering/routes.ts | 35 ++++++++++ .../backend/src/auto-peering/service.test.ts | 55 +++++++++++++++ packages/backend/src/auto-peering/service.ts | 46 +++++++++++++ packages/backend/src/config/app.ts | 2 + packages/backend/src/index.ts | 26 +++++++ 9 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 packages/backend/src/auto-peering/routes.test.ts create mode 100644 packages/backend/src/auto-peering/routes.ts create mode 100644 packages/backend/src/auto-peering/service.test.ts create mode 100644 packages/backend/src/auto-peering/service.ts diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts index 8265df4239..4a001d9190 100644 --- a/packages/backend/src/app.ts +++ b/packages/backend/src/app.ts @@ -72,6 +72,8 @@ import { createRedisDataStore } from './middleware/cache/data-stores/redis' import { createRedisLock } from './middleware/lock/redis' import { CombinedPaymentService } from './open_payments/payment/combined/service' import { FeeService } from './fee/service' +import { AutoPeeringService } from './auto-peering/service' +import { AutoPeeringRoutes } from './auto-peering/routes' export interface AppContextData { logger: Logger @@ -203,6 +205,8 @@ export interface AppServices { redis: Promise combinedPaymentService: Promise feeService: Promise + autoPeeringService: Promise + autoPeeringRoutes: Promise } export type AppContainer = IocContract @@ -210,6 +214,7 @@ export type AppContainer = IocContract export class App { private openPaymentsServer!: Server private adminServer!: Server + private autoPeeringServer!: Server public apolloServer!: ApolloServer public closeEmitter!: EventEmitter public isShuttingDown = false @@ -477,19 +482,42 @@ export class App { this.openPaymentsServer = koa.listen(port) } + public async startAutoPeeringServer(port: number | string): Promise { + const koa = await this.createKoaServer() + + const autoPeeringRoutes = await this.container.use('autoPeeringRoutes') + const router = new Router() + + router.use(bodyParser()) + + router.get('/', autoPeeringRoutes.get) + + koa.use(router.routes()) + + this.autoPeeringServer = koa.listen(port) + } + public async shutdown(): Promise { return new Promise((resolve): void => { + this.isShuttingDown = true + this.closeEmitter.emit('shutdown') + if (this.openPaymentsServer) { - this.isShuttingDown = true - this.closeEmitter.emit('shutdown') + this.openPaymentsServer.close((): void => { + resolve() + }) + } + + if (this.adminServer) { this.adminServer.close((): void => { resolve() }) - this.openPaymentsServer.close((): void => { + } + + if (this.autoPeeringServer) { + this.autoPeeringServer.close((): void => { resolve() }) - } else { - resolve() } }) } diff --git a/packages/backend/src/asset/service.test.ts b/packages/backend/src/asset/service.test.ts index 5d8068d7a4..3e92f381ad 100644 --- a/packages/backend/src/asset/service.test.ts +++ b/packages/backend/src/asset/service.test.ts @@ -193,4 +193,20 @@ describe('Asset Service', (): void => { getPage: (pagination?: Pagination) => assetService.getPage(pagination) }) }) + + describe('getAll', (): void => { + test('returns all assets', async (): Promise => { + const assets = await Promise.all([ + assetService.create(randomAsset()), + assetService.create(randomAsset()), + assetService.create(randomAsset()) + ]) + + await expect(assetService.getAll()).resolves.toEqual(assets) + }) + + test('returns empty array if no assets', async (): Promise => { + await expect(assetService.getAll()).resolves.toEqual([]) + }) + }) }) diff --git a/packages/backend/src/asset/service.ts b/packages/backend/src/asset/service.ts index 31f928bec0..092fa4c1e0 100644 --- a/packages/backend/src/asset/service.ts +++ b/packages/backend/src/asset/service.ts @@ -25,6 +25,7 @@ export interface AssetService { update(options: UpdateOptions): Promise get(id: string): Promise getPage(pagination?: Pagination): Promise + getAll(): Promise } interface ServiceDependencies extends BaseService { @@ -48,7 +49,8 @@ export async function createAssetService({ create: (options) => createAsset(deps, options), update: (options) => updateAsset(deps, options), get: (id) => getAsset(deps, id), - getPage: (pagination?) => getAssetsPage(deps, pagination) + getPage: (pagination?) => getAssetsPage(deps, pagination), + getAll: () => getAll(deps) } } @@ -118,3 +120,7 @@ async function getAssetsPage( ): Promise { return await Asset.query(deps.knex).getPage(pagination) } + +async function getAll(deps: ServiceDependencies): Promise { + return await Asset.query(deps.knex) +} diff --git a/packages/backend/src/auto-peering/routes.test.ts b/packages/backend/src/auto-peering/routes.test.ts new file mode 100644 index 0000000000..f674780a06 --- /dev/null +++ b/packages/backend/src/auto-peering/routes.test.ts @@ -0,0 +1,68 @@ +import { IocContract } from '@adonisjs/fold' +import { initIocContainer } from '..' +import { AppContext, AppServices } from '../app' +import { Config } from '../config/app' +import { createTestApp, TestContainer } from '../tests/app' +import { createAsset } from '../tests/asset' +import { createContext } from '../tests/context' +import { truncateTables } from '../tests/tableManager' +import { AutoPeeringRoutes } from './routes' + +describe('Auto Peering Routes', (): void => { + let deps: IocContract + let appContainer: TestContainer + let autoPeeringRoutes: AutoPeeringRoutes + + beforeAll(async (): Promise => { + deps = initIocContainer({ ...Config, enableAutoPeering: true }) + appContainer = await createTestApp(deps) + autoPeeringRoutes = await deps.use('autoPeeringRoutes') + }) + + afterEach(async (): Promise => { + await truncateTables(appContainer.knex) + }) + + afterAll(async (): Promise => { + await appContainer.shutdown() + }) + + describe('get', (): void => { + test('returns peering details with assets', async (): Promise => { + const assets = await Promise.all([ + createAsset(deps), + createAsset(deps), + createAsset(deps) + ]) + + const ctx = createContext({ + headers: { Accept: 'application/json' }, + url: `/` + }) + + await expect(autoPeeringRoutes.get(ctx)).resolves.toBeUndefined() + expect(ctx.body).toEqual({ + ilpAddress: Config.ilpAddress, + assets: expect.arrayContaining( + assets.map((asset) => ({ + code: asset.code, + scale: asset.scale + })) + ) + }) + }) + + test('returns peering details without assets', async (): Promise => { + const ctx = createContext({ + headers: { Accept: 'application/json' }, + url: `/` + }) + + await expect(autoPeeringRoutes.get(ctx)).resolves.toBeUndefined() + expect(ctx.body).toEqual({ + ilpAddress: Config.ilpAddress, + assets: [] + }) + }) + }) +}) diff --git a/packages/backend/src/auto-peering/routes.ts b/packages/backend/src/auto-peering/routes.ts new file mode 100644 index 0000000000..7c89dcaca2 --- /dev/null +++ b/packages/backend/src/auto-peering/routes.ts @@ -0,0 +1,35 @@ +import { AppContext } from '../app' +import { BaseService } from '../shared/baseService' +import { AutoPeeringService } from './service' + +export interface ServiceDependencies extends BaseService { + autoPeeringService: AutoPeeringService +} + +export interface AutoPeeringRoutes { + get(ctx: AppContext): Promise +} + +export async function createAutoPeeringRoutes( + deps_: ServiceDependencies +): Promise { + const deps: ServiceDependencies = { + ...deps_, + logger: deps_.logger.child({ + service: 'AutoPeeringRoutes' + }) + } + + return { + get: (ctx: AppContext) => getPeeringDetails(deps, ctx) + } +} + +async function getPeeringDetails( + deps: ServiceDependencies, + ctx: AppContext +): Promise { + const peeringDetails = await deps.autoPeeringService.getPeeringDetails() + + ctx.body = peeringDetails +} diff --git a/packages/backend/src/auto-peering/service.test.ts b/packages/backend/src/auto-peering/service.test.ts new file mode 100644 index 0000000000..9414d5f33a --- /dev/null +++ b/packages/backend/src/auto-peering/service.test.ts @@ -0,0 +1,55 @@ +import { IocContract } from '@adonisjs/fold' +import { initIocContainer } from '..' +import { AppServices } from '../app' +import { Config } from '../config/app' +import { createTestApp, TestContainer } from '../tests/app' +import { createAsset } from '../tests/asset' +import { truncateTables } from '../tests/tableManager' +import { AutoPeeringService } from './service' + +describe('Auto Peering Service', (): void => { + let deps: IocContract + let appContainer: TestContainer + let autoPeeringService: AutoPeeringService + + beforeAll(async (): Promise => { + deps = initIocContainer({ ...Config, enableAutoPeering: true }) + appContainer = await createTestApp(deps) + autoPeeringService = await deps.use('autoPeeringService') + }) + + afterEach(async (): Promise => { + await truncateTables(appContainer.knex) + }) + + afterAll(async (): Promise => { + await appContainer.shutdown() + }) + + describe('getPeeringDetails', (): void => { + test('returns peering details', async (): Promise => { + const assets = await Promise.all([ + createAsset(deps), + createAsset(deps), + createAsset(deps) + ]) + + expect(autoPeeringService.getPeeringDetails()).resolves.toEqual({ + ilpAddress: Config.ilpAddress, + assets: expect.arrayContaining( + assets.map((asset) => ({ + code: asset.code, + scale: asset.scale + })) + ) + }) + }) + + test('returns peering details with no assets', async (): Promise => { + expect(autoPeeringService.getPeeringDetails()).resolves.toEqual({ + ilpAddress: Config.ilpAddress, + assets: [] + }) + }) + }) +}) diff --git a/packages/backend/src/auto-peering/service.ts b/packages/backend/src/auto-peering/service.ts new file mode 100644 index 0000000000..462db1e672 --- /dev/null +++ b/packages/backend/src/auto-peering/service.ts @@ -0,0 +1,46 @@ +import { AssetService } from '../asset/service' +import { IAppConfig } from '../config/app' +import { BaseService } from '../shared/baseService' + +export interface PeeringDetails { + ilpAddress: string + assets: { code: string; scale: number }[] +} + +export interface AutoPeeringService { + getPeeringDetails(): Promise +} + +export interface ServiceDependencies extends BaseService { + assetService: AssetService + config: IAppConfig +} + +export async function createAutoPeeringService( + deps_: ServiceDependencies +): Promise { + const deps: ServiceDependencies = { + ...deps_, + logger: deps_.logger.child({ + service: 'AutoPeeringService' + }) + } + + return { + getPeeringDetails: () => getPeeringDetails(deps) + } +} + +async function getPeeringDetails( + deps: ServiceDependencies +): Promise { + const assets = await deps.assetService.getAll() + + return { + ilpAddress: deps.config.ilpAddress, + assets: assets.map((asset) => ({ + code: asset.code, + scale: asset.scale + })) + } +} diff --git a/packages/backend/src/config/app.ts b/packages/backend/src/config/app.ts index a51d086bcd..1ae72952fa 100644 --- a/packages/backend/src/config/app.ts +++ b/packages/backend/src/config/app.ts @@ -33,6 +33,8 @@ export const Config = { openPaymentsUrl: envString('OPEN_PAYMENTS_URL', 'http://127.0.0.1:3003'), openPaymentsPort: envInt('OPEN_PAYMENTS_PORT', 3003), connectorPort: envInt('CONNECTOR_PORT', 3002), + autoPeeringServerPort: envInt('AUTO_PEERING_SERVER_PORT', 3005), + enableAutoPeering: envBool('ENABLE_AUTO_PEERING', false), databaseUrl: process.env.NODE_ENV === 'test' ? `${process.env.DATABASE_URL}_${process.env.JEST_WORKER_ID}` diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index c27c9d6418..4ab7b19188 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -46,6 +46,8 @@ import { createReceiverService } from './open_payments/receiver/service' import { createRemoteIncomingPaymentService } from './open_payments/payment/incoming_remote/service' import { createCombinedPaymentService } from './open_payments/payment/combined/service' import { createFeeService } from './fee/service' +import { createAutoPeeringService } from './auto-peering/service' +import { createAutoPeeringRoutes } from './auto-peering/routes' BigInt.prototype.toJSON = function () { return this.toString() @@ -416,6 +418,23 @@ export function initIocContainer( }) }) + container.singleton('autoPeeringService', async (deps) => { + return createAutoPeeringService({ + logger: await deps.use('logger'), + knex: await deps.use('knex'), + assetService: await deps.use('assetService'), + config: await deps.use('config') + }) + }) + + container.singleton('autoPeeringRoutes', async (deps) => { + return await createAutoPeeringRoutes({ + logger: await deps.use('logger'), + knex: await deps.use('knex'), + autoPeeringService: await deps.use('autoPeeringService') + }) + }) + return container } @@ -507,6 +526,13 @@ export const start = async ( connectorServer = connectorApp.listenPublic(config.connectorPort) logger.info(`Connector listening on ${config.connectorPort}`) logger.info('🐒 has 🚀. Get ready for 🍌🍌🍌🍌🍌') + + if (config.enableAutoPeering) { + await app.startAutoPeeringServer(config.autoPeeringServerPort) + logger.info( + `Auto-peering server listening on ${config.autoPeeringServerPort}` + ) + } } // If this script is run directly, start the server