Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: get peering details #1805

Merged
merged 1 commit into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions packages/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -203,13 +205,16 @@ export interface AppServices {
redis: Promise<Redis>
combinedPaymentService: Promise<CombinedPaymentService>
feeService: Promise<FeeService>
autoPeeringService: Promise<AutoPeeringService>
autoPeeringRoutes: Promise<AutoPeeringRoutes>
}

export type AppContainer = IocContract<AppServices>

export class App {
private openPaymentsServer!: Server
private adminServer!: Server
private autoPeeringServer!: Server
public apolloServer!: ApolloServer
public closeEmitter!: EventEmitter
public isShuttingDown = false
Expand Down Expand Up @@ -477,19 +482,42 @@ export class App {
this.openPaymentsServer = koa.listen(port)
}

public async startAutoPeeringServer(port: number | string): Promise<void> {
const koa = await this.createKoaServer()

const autoPeeringRoutes = await this.container.use('autoPeeringRoutes')
const router = new Router<DefaultState, AppContext>()

router.use(bodyParser())

router.get('/', autoPeeringRoutes.get)

koa.use(router.routes())

this.autoPeeringServer = koa.listen(port)
}

public async shutdown(): Promise<void> {
return new Promise((resolve): void => {
Comment on lines 500 to 501
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little bit confused by this. Why is the promise getting resolved multiple times instead of just once? If there is an OP server, wouldn't the promise be resolved in that specific if block, making any further resolves irrelevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been trying to update this code, but it's behaving weirdly when I update it to be "correct". I wonder why it was built this way originally, tbh.

I'm going to make a separate PR that hopefully fixes this and makes it more clear

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()
}
})
}
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/asset/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,20 @@ describe('Asset Service', (): void => {
getPage: (pagination?: Pagination) => assetService.getPage(pagination)
})
})

describe('getAll', (): void => {
test('returns all assets', async (): Promise<void> => {
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<void> => {
await expect(assetService.getAll()).resolves.toEqual([])
})
})
})
8 changes: 7 additions & 1 deletion packages/backend/src/asset/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface AssetService {
update(options: UpdateOptions): Promise<Asset | AssetError>
get(id: string): Promise<void | Asset>
getPage(pagination?: Pagination): Promise<Asset[]>
getAll(): Promise<Asset[]>
}

interface ServiceDependencies extends BaseService {
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -118,3 +120,7 @@ async function getAssetsPage(
): Promise<Asset[]> {
return await Asset.query(deps.knex).getPage(pagination)
}

async function getAll(deps: ServiceDependencies): Promise<Asset[]> {
return await Asset.query(deps.knex)
}
68 changes: 68 additions & 0 deletions packages/backend/src/auto-peering/routes.test.ts
Original file line number Diff line number Diff line change
@@ -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<AppServices>
let appContainer: TestContainer
let autoPeeringRoutes: AutoPeeringRoutes

beforeAll(async (): Promise<void> => {
deps = initIocContainer({ ...Config, enableAutoPeering: true })
appContainer = await createTestApp(deps)
autoPeeringRoutes = await deps.use('autoPeeringRoutes')
})

afterEach(async (): Promise<void> => {
await truncateTables(appContainer.knex)
})

afterAll(async (): Promise<void> => {
await appContainer.shutdown()
})

describe('get', (): void => {
test('returns peering details with assets', async (): Promise<void> => {
const assets = await Promise.all([
createAsset(deps),
createAsset(deps),
createAsset(deps)
])

const ctx = createContext<AppContext>({
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<void> => {
const ctx = createContext<AppContext>({
headers: { Accept: 'application/json' },
url: `/`
})

await expect(autoPeeringRoutes.get(ctx)).resolves.toBeUndefined()
expect(ctx.body).toEqual({
ilpAddress: Config.ilpAddress,
assets: []
})
})
})
})
35 changes: 35 additions & 0 deletions packages/backend/src/auto-peering/routes.ts
Original file line number Diff line number Diff line change
@@ -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<void>
}

export async function createAutoPeeringRoutes(
deps_: ServiceDependencies
): Promise<AutoPeeringRoutes> {
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<void> {
const peeringDetails = await deps.autoPeeringService.getPeeringDetails()

ctx.body = peeringDetails
}
55 changes: 55 additions & 0 deletions packages/backend/src/auto-peering/service.test.ts
Original file line number Diff line number Diff line change
@@ -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<AppServices>
let appContainer: TestContainer
let autoPeeringService: AutoPeeringService

beforeAll(async (): Promise<void> => {
deps = initIocContainer({ ...Config, enableAutoPeering: true })
appContainer = await createTestApp(deps)
autoPeeringService = await deps.use('autoPeeringService')
})

afterEach(async (): Promise<void> => {
await truncateTables(appContainer.knex)
})

afterAll(async (): Promise<void> => {
await appContainer.shutdown()
})

describe('getPeeringDetails', (): void => {
test('returns peering details', async (): Promise<void> => {
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<void> => {
expect(autoPeeringService.getPeeringDetails()).resolves.toEqual({
ilpAddress: Config.ilpAddress,
assets: []
})
})
})
})
46 changes: 46 additions & 0 deletions packages/backend/src/auto-peering/service.ts
Original file line number Diff line number Diff line change
@@ -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<PeeringDetails>
}

export interface ServiceDependencies extends BaseService {
assetService: AssetService
config: IAppConfig
}

export async function createAutoPeeringService(
deps_: ServiceDependencies
): Promise<AutoPeeringService> {
const deps: ServiceDependencies = {
...deps_,
logger: deps_.logger.child({
service: 'AutoPeeringService'
})
}

return {
getPeeringDetails: () => getPeeringDetails(deps)
}
}

async function getPeeringDetails(
deps: ServiceDependencies
): Promise<PeeringDetails> {
const assets = await deps.assetService.getAll()

return {
ilpAddress: deps.config.ilpAddress,
assets: assets.map((asset) => ({
code: asset.code,
scale: asset.scale
}))
}
}
2 changes: 2 additions & 0 deletions packages/backend/src/config/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
Expand Down
26 changes: 26 additions & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Down