Skip to content

Commit 9147dc6

Browse files
chore(backend): rate service fixes; import into backend (interledger#139)
* fix(rates): mock test http request * chore(backend): add rates service The rates service was previously used by multiple packages. * fix(backend): fix rate service cache tests Co-authored-by: Brandon Wilson <[email protected]>
1 parent e30830f commit 9147dc6

18 files changed

+114
-55
lines changed

.github/workflows/lint_test_build.yml

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ jobs:
3636
- run: yarn workspace backend lint
3737
- run: yarn workspace backend format:check
3838
- run: yarn install --immutable --immutable-cache
39-
- run: yarn build
4039
- run: yarn workspace backend test
4140

4241
frontend:

.pnp.js

-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@
8282
"pg": "^8.6.0",
8383
"pino": "^6.11.3",
8484
"pino-pretty": "^4.7.1",
85-
"rates": "workspace:packages/rates",
8685
"raw-body": "^2.4.1",
8786
"tigerbeetle-node": "^0.4.1",
8887
"uuid": "^8.3.2"

packages/backend/src/app.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import bodyParser from 'koa-bodyparser'
99
import { Logger } from 'pino'
1010
import Router from '@koa/router'
1111
import { ApolloServer } from 'apollo-server-koa'
12-
import { RatesService } from 'rates'
1312

1413
import { IAppConfig } from './config/app'
1514
import { MessageProducer } from './messaging/messageProducer'
@@ -28,6 +27,7 @@ import { AccountService } from './account/service'
2827
import { DepositService } from './deposit/service'
2928
import { WithdrawalService } from './withdrawal/service'
3029
import { CreditService } from './credit/service'
30+
import { RatesService } from './rates/service'
3131
import { SPSPService } from './spsp/service'
3232
import { InvoiceService } from './invoice/service'
3333
import { StreamServer } from '@interledger/stream-receiver'

packages/backend/src/connector/core/rafiki.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { LoggingService } from './services/logger'
1818
import { IncomingMessage, ServerResponse } from 'http'
1919
import { IlpReply, IlpReject, IlpFulfill } from 'ilp-packet'
2020
import { DebugLogger } from './services/logger/debug'
21-
import { RatesService } from 'rates'
21+
import { RatesService } from '../../rates/service'
2222
import { createAccountMiddleware } from './middleware/account'
2323
import { createStreamAddressMiddleware } from './middleware/stream-address'
2424
import { AccountTransfer } from '../../account/service'

packages/backend/src/connector/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import compose = require('koa-compose')
22
import IORedis from 'ioredis'
3-
import { RatesService } from 'rates'
43
import { StreamServer } from '@interledger/stream-receiver'
54

65
import {
@@ -22,6 +21,7 @@ import {
2221
} from './core'
2322
import { Logger } from '../logger/service'
2423
import { AccountService } from '../account/service'
24+
import { RatesService } from '../rates/service'
2525

2626
interface ServiceDependencies {
2727
redis: IORedis.Redis

packages/backend/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import { Ioc, IocContract } from '@adonisjs/fold'
88
import IORedis from 'ioredis'
99
import { createClient } from 'tigerbeetle-node'
1010

11-
import { createRatesService } from 'rates'
1211
import { App, AppServices } from './app'
1312
import { Config } from './config/app'
1413
import { GraphileProducer } from './messaging/graphileProducer'
14+
import { createRatesService } from './rates/service'
1515
import { createOutgoingPaymentService } from './outgoing_payment/service'
1616
import { createIlpPlugin, IlpPlugin } from './outgoing_payment/ilp_plugin'
1717
import { createHttpTokenService } from './httpToken/service'

packages/backend/src/outgoing_payment/service.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import nock from 'nock'
22
import Knex from 'knex'
33
import * as Pay from '@interledger/pay'
44
import { StreamServer, StreamCredentials } from '@interledger/stream-receiver'
5-
import { RatesService } from 'rates'
65
import { v4 as uuid } from 'uuid'
76

87
import { OutgoingPaymentService } from './service'
@@ -20,6 +19,7 @@ import { RETRY_BACKOFF_SECONDS } from './worker'
2019
import { AccountBalance, AccountService } from '../account/service'
2120
import { CreditService } from '../credit/service'
2221
import { CreditError } from '../credit/errors'
22+
import { RatesService } from '../rates/service'
2323
import { WithdrawalService } from '../withdrawal/service'
2424
import { isWithdrawalError } from '../withdrawal/errors'
2525

packages/backend/src/outgoing_payment/service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { TransactionOrKnex } from 'objection'
22
import * as Pay from '@interledger/pay'
3-
import { RatesService } from 'rates'
43
import { BaseService } from '../shared/baseService'
54
import { OutgoingPayment, PaymentIntent, PaymentState } from './model'
65
import { AccountService } from '../account/service'
76
import { isAccountError } from '../account/errors'
87
import { CreditService } from '../credit/service'
8+
import { RatesService } from '../rates/service'
99
import { IlpPlugin } from './ilp_plugin'
1010
import * as lifecycle from './lifecycle'
1111
import * as worker from './worker'

packages/rates/src/index.test.ts packages/backend/src/rates/service.test.ts

+42-16
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import Koa from 'koa'
2-
import { createRatesService, RatesService, ConvertError } from '.'
3-
import { logger } from './mocks'
42

5-
jest.useFakeTimers('modern')
3+
import { RatesService, ConvertError } from './service'
4+
import { createTestApp, TestContainer } from '../tests/app'
5+
import { Config } from '../config/app'
6+
import { IocContract } from '@adonisjs/fold'
7+
import { initIocContainer } from '../'
8+
import { AppServices } from '../app'
9+
610
describe('Rates service', function () {
11+
let deps: IocContract<AppServices>
12+
let appContainer: TestContainer
713
let service: RatesService
814
let requestCount = 0
9-
15+
const mockMessageProducer = {
16+
send: jest.fn()
17+
}
1018
const pricesLifetime = 100
1119
const koa = new Koa()
1220
koa.use(function (ctx) {
@@ -21,19 +29,37 @@ describe('Rates service', function () {
2129
})
2230
const server = koa.listen(3210)
2331

24-
beforeEach(() => {
25-
jest.setSystemTime(1600000000000)
26-
service = createRatesService({
27-
pricesUrl: 'http://127.0.0.1:3210/',
28-
pricesLifetime,
29-
logger
30-
})
31-
requestCount = 0
32-
})
32+
beforeAll(
33+
async (): Promise<void> => {
34+
jest.useFakeTimers('modern')
35+
jest.setSystemTime(1600000000000)
36+
const config = Config
37+
config.pricesLifetime = pricesLifetime
38+
config.pricesUrl = 'http://127.0.0.1:3210/'
39+
deps = await initIocContainer(config)
40+
deps.bind('messageProducer', async () => mockMessageProducer)
41+
appContainer = await createTestApp(deps)
42+
}
43+
)
3344

34-
afterAll(() => {
35-
server.close()
36-
})
45+
beforeEach(
46+
async (): Promise<void> => {
47+
// Fast-forward to reset the cache between tests.
48+
jest.setSystemTime(Date.now() + pricesLifetime + 1)
49+
service = await deps.use('ratesService')
50+
requestCount = 0
51+
}
52+
)
53+
54+
afterAll(
55+
async (): Promise<void> => {
56+
jest.useRealTimers()
57+
await new Promise((resolve, reject) => {
58+
server.close((err?: Error) => (err ? reject(err) : resolve(null)))
59+
})
60+
await appContainer.shutdown()
61+
}
62+
)
3763

3864
describe('convert', function () {
3965
it('returns the source amount when assets are alike', async () => {

packages/rates/src/index.ts packages/backend/src/rates/service.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { BaseService } from '../shared/baseService'
12
import Axios, { AxiosInstance } from 'axios'
2-
import { convert, ConvertOptions, LoggingService } from './util'
3+
import { convert, ConvertOptions } from './util'
34

45
const REQUEST_TIMEOUT = 5_000 // millseconds
56

@@ -10,8 +11,7 @@ export interface RatesService {
1011
): Promise<bigint | ConvertError>
1112
}
1213

13-
interface ServiceDependencies {
14-
logger: LoggingService
14+
interface ServiceDependencies extends BaseService {
1515
// If `url` is not set, the connector cannot convert between currencies.
1616
pricesUrl?: string
1717
// Duration (milliseconds) that the fetched prices are valid.
@@ -108,7 +108,7 @@ class RatesServiceImpl implements RatesService {
108108
if (!url) return {}
109109

110110
const res = await this.axios.get(url).catch((err) => {
111-
this.deps.logger.warn('price request error', { err: err.message })
111+
this.deps.logger.warn({ err: err.message }, 'price request error')
112112
throw err
113113
})
114114
this.pricesRequest = Promise.resolve(res.data)
File renamed without changes.

packages/rates/src/util.ts packages/backend/src/rates/util.ts

-14
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,3 @@ export function convert(opts: ConvertOptions): bigint {
2020
(opts.sourceAmount * BigInt(scaledExchangeRate * shiftUp)) / BigInt(shiftUp)
2121
)
2222
}
23-
24-
export interface LoggingService {
25-
fatal: LogFn
26-
error: LogFn
27-
warn: LogFn
28-
info: LogFn
29-
debug: LogFn
30-
trace: LogFn
31-
}
32-
33-
export interface LogFn {
34-
(msg: string, ...args: unknown[]): void
35-
(obj: Record<string, unknown>, msg?: string, ...args: unknown[]): void
36-
}

packages/rates/package.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "rates",
3-
"main": "dist/index.js",
4-
"types": "dist/index.d.ts",
3+
"main": "dist/server/index.js",
4+
"types": "dist/server/index.d.ts",
55
"scripts": {
66
"lint": "yarn lint:local",
77
"format": "yarn format:local",
@@ -10,7 +10,6 @@
1010
"test": "yarn test:local"
1111
},
1212
"devDependencies": {
13-
"@adonisjs/fold": "^8.1.0",
1413
"@koa/router": "^10.0.0",
1514
"@types/jest": "^26.0.22",
1615
"@types/koa": "^2.13.1",
@@ -20,7 +19,6 @@
2019
"@types/sax": "^1.2.1",
2120
"koa": "^2.13.1",
2221
"nock": "^13.1.0",
23-
"node-mocks-http": "^1.10.1",
2422
"sax": "^1.2.4"
2523
},
2624
"dependencies": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
3+
<gesmes:subject>Reference rates</gesmes:subject>
4+
<gesmes:Sender>
5+
<gesmes:name>European Central Bank</gesmes:name>
6+
</gesmes:Sender>
7+
<Cube>
8+
<Cube time='2021-10-05'>
9+
<Cube currency='USD' rate='1.1602'/>
10+
<Cube currency='JPY' rate='128.99'/>
11+
<Cube currency='BGN' rate='1.9558'/>
12+
<Cube currency='CZK' rate='25.308'/>
13+
<Cube currency='DKK' rate='7.4379'/>
14+
<Cube currency='GBP' rate='0.85173'/>
15+
<Cube currency='HUF' rate='356.90'/>
16+
<Cube currency='PLN' rate='4.5998'/>
17+
<Cube currency='RON' rate='4.9477'/>
18+
<Cube currency='SEK' rate='10.1310'/>
19+
<Cube currency='CHF' rate='1.0752'/>
20+
<Cube currency='ISK' rate='148.00'/>
21+
<Cube currency='NOK' rate='9.9155'/>
22+
<Cube currency='HRK' rate='7.5050'/>
23+
<Cube currency='RUB' rate='84.1541'/>
24+
<Cube currency='TRY' rate='10.2707'/>
25+
<Cube currency='AUD' rate='1.5937'/>
26+
<Cube currency='BRL' rate='6.3106'/>
27+
<Cube currency='CAD' rate='1.4612'/>
28+
<Cube currency='CNY' rate='7.4805'/>
29+
<Cube currency='HKD' rate='9.0324'/>
30+
<Cube currency='IDR' rate='16506.62'/>
31+
<Cube currency='ILS' rate='3.7477'/>
32+
<Cube currency='INR' rate='86.4880'/>
33+
<Cube currency='KRW' rate='1376.12'/>
34+
<Cube currency='MXN' rate='23.7904'/>
35+
<Cube currency='MYR' rate='4.8482'/>
36+
<Cube currency='NZD' rate='1.6650'/>
37+
<Cube currency='PHP' rate='58.893'/>
38+
<Cube currency='SGD' rate='1.5743'/>
39+
<Cube currency='THB' rate='39.203'/>
40+
<Cube currency='ZAR' rate='17.3792'/>
41+
</Cube>
42+
</Cube>
43+
</gesmes:Envelope>
+14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1+
import nock from 'nock'
12
import { createApp, shutdownApp } from '.'
23
import { config } from './config'
4+
import { RATES_API } from './ecb/service'
35

46
describe('App', function () {
7+
afterEach(() => {
8+
nock.cleanAll()
9+
})
10+
511
it('starts and closes cleanly', async () => {
12+
const scope = nock(RATES_API)
13+
.get('')
14+
.replyWithFile(200, __dirname + '/fixtures/ecb-eurofxref-daily.xml', {
15+
'Content-Type': 'text/xml'
16+
})
17+
618
const app = await createApp(config)
719
await shutdownApp(app)
20+
21+
scope.done()
822
})
923
})

tsconfig.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"references": [
3-
{
4-
"path": "packages/rates"
5-
},
63
{
74
"path": "packages/backend"
85
},
96
{
107
"path": "packages/frontend"
8+
},
9+
{
10+
"path": "packages/rates"
1111
}
1212
],
1313
"files": [],

yarn.lock

-3
Original file line numberDiff line numberDiff line change
@@ -4486,7 +4486,6 @@ __metadata:
44864486
pg: ^8.6.0
44874487
pino: ^6.11.3
44884488
pino-pretty: ^4.7.1
4489-
rates: "workspace:packages/rates"
44904489
raw-body: ^2.4.1
44914490
rosie: ^2.1.0
44924491
testcontainers: ^7.20.4
@@ -11909,7 +11908,6 @@ __metadata:
1190911908
version: 0.0.0-use.local
1191011909
resolution: "rates@workspace:packages/rates"
1191111910
dependencies:
11912-
"@adonisjs/fold": ^8.1.0
1191311911
"@koa/router": ^10.0.0
1191411912
"@types/jest": ^26.0.22
1191511913
"@types/koa": ^2.13.1
@@ -11920,7 +11918,6 @@ __metadata:
1192011918
axios: ^0.21.1
1192111919
koa: ^2.13.1
1192211920
nock: ^13.1.0
11923-
node-mocks-http: ^1.10.1
1192411921
pino: ^6.11.3
1192511922
sax: ^1.2.4
1192611923
languageName: unknown

0 commit comments

Comments
 (0)