Skip to content

Commit 2edda0c

Browse files
committed
Comments
1 parent b040266 commit 2edda0c

3 files changed

Lines changed: 153 additions & 58 deletions

File tree

src/adapter/basic.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,15 +469,17 @@ export class Adapter<
469469
replySent,
470470
endpoint.transportRoutes.get(fallback.transportName),
471471
)
472+
.then((response) => ({ response }))
473+
.catch((error) => ({ error }))
472474

473475
try {
474476
return await this.handleSingleTransportRequest(req, replySent, transport)
475477
} catch (primaryError) {
476-
try {
477-
return await fallbackResponsePromise
478-
} catch (_fallbackError) {
479-
throw primaryError
478+
const fallbackResponse = await fallbackResponsePromise
479+
if ('response' in fallbackResponse) {
480+
return fallbackResponse.response
480481
}
482+
throw primaryError
481483
}
482484
}
483485

test/adapter/transport-fallback.test.ts

Lines changed: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import untypedTest, { TestFn } from 'ava'
22
import { Adapter, AdapterEndpoint } from '../../src/adapter'
33
import { AdapterConfig } from '../../src/config'
44
import { TransportRoutes } from '../../src/transports'
5-
import { AdapterRequest } from '../../src/util'
65
import { NopTransport, NopTransportTypes, TestAdapter } from '../../src/util/testing-utils'
76

87
const test = untypedTest as TestFn<{ testAdapter: TestAdapter }>
@@ -25,8 +24,8 @@ function tsResponse(result: unknown) {
2524
}
2625

2726
function buildFallbackAdapter(
28-
primary: NopTransport<NopTransportTypes>,
29-
fallback: NopTransport<NopTransportTypes>,
27+
primary?: () => Promise<ReturnType<typeof tsResponse>>,
28+
fallback?: () => Promise<ReturnType<typeof tsResponse>>,
3029
) {
3130
const config = new AdapterConfig(
3231
{},
@@ -44,61 +43,124 @@ function buildFallbackAdapter(
4443
new AdapterEndpoint<NopTransportTypes>({
4544
name: 'price',
4645
transportRoutes: new TransportRoutes<NopTransportTypes>()
47-
.register('primary', primary)
48-
.register('fallback', fallback),
46+
.register(
47+
'primary',
48+
primary
49+
? new (class extends NopTransport<NopTransportTypes> {
50+
override async foregroundExecute() {
51+
return primary()
52+
}
53+
})()
54+
: new NopTransport(),
55+
)
56+
.register(
57+
'fallback',
58+
fallback
59+
? new (class extends NopTransport<NopTransportTypes> {
60+
override async foregroundExecute() {
61+
return fallback()
62+
}
63+
})()
64+
: new NopTransport(),
65+
),
4966
defaultTransport: 'primary',
5067
fallbackTransport: { primary: 'fallback' },
5168
}),
5269
],
5370
})
5471
}
5572

56-
test.serial('uses primary transport when it returns before fallback', async (t) => {
73+
test.serial('uses primary when both primary and fallback succeed', async (t) => {
5774
const adapter = buildFallbackAdapter(
58-
new (class extends NopTransport<NopTransportTypes> {
59-
override async foregroundExecute(_req: AdapterRequest<NopTransportTypes['Parameters']>) {
60-
return tsResponse('primary')
61-
}
62-
})(),
63-
new (class extends NopTransport<NopTransportTypes> {
64-
override async foregroundExecute(_req: AdapterRequest<NopTransportTypes['Parameters']>) {
65-
await new Promise((r) => {
66-
setTimeout(r, 500)
67-
})
68-
return tsResponse('fallback')
69-
}
70-
})(),
75+
() => Promise.resolve(tsResponse('primary')),
76+
() => Promise.resolve(tsResponse('fallback')),
7177
)
78+
const testAdapter = await TestAdapter.start(adapter, t.context)
79+
80+
const response = await testAdapter.request({})
7281

82+
t.is(response.statusCode, 200)
83+
t.is(response.json().result, 'primary')
84+
})
85+
86+
test.serial('uses primary when fallback stalls', async (t) => {
87+
const adapter = buildFallbackAdapter(
88+
() => Promise.resolve(tsResponse('primary')),
89+
async () => {
90+
await new Promise((r) => {
91+
setTimeout(r, 500)
92+
})
93+
return tsResponse('fallback')
94+
},
95+
)
7396
const testAdapter = await TestAdapter.start(adapter, t.context)
7497
const started = Date.now()
98+
7599
const response = await testAdapter.request({})
100+
76101
t.is(response.statusCode, 200)
77102
t.is(response.json().result, 'primary')
78103
t.true(Date.now() - started < 200, 'should not wait for slow fallback path')
79104
})
80105

81-
test.serial('uses fallback when primary does not return data', async (t) => {
106+
test.serial('uses primary when fallback failed', async (t) => {
107+
const adapter = buildFallbackAdapter(
108+
() => Promise.resolve(tsResponse('primary')),
109+
() => Promise.reject(new Error('Fallback fail')),
110+
)
111+
const testAdapter = await TestAdapter.start(adapter, t.context)
112+
113+
const response = await testAdapter.request({})
114+
115+
t.is(response.statusCode, 200)
116+
t.is(response.json().result, 'primary')
117+
})
118+
119+
test.serial('uses primary when primary stalls', async (t) => {
120+
const adapter = buildFallbackAdapter(
121+
async () => {
122+
await new Promise((r) => {
123+
setTimeout(r, 500)
124+
})
125+
return tsResponse('primary')
126+
},
127+
() => Promise.resolve(tsResponse('fallback')),
128+
)
129+
const testAdapter = await TestAdapter.start(adapter, t.context)
130+
131+
const response = await testAdapter.request({})
132+
133+
t.is(response.statusCode, 200)
134+
t.is(response.json().result, 'primary')
135+
})
136+
137+
test.serial('uses fallback when primary throw exception', async (t) => {
82138
const adapter = buildFallbackAdapter(
83-
new NopTransport(),
84-
new (class extends NopTransport<NopTransportTypes> {
85-
override async foregroundExecute(_req: AdapterRequest<NopTransportTypes['Parameters']>) {
86-
return tsResponse('from-fallback')
87-
}
88-
})(),
139+
() => Promise.reject(new Error('Primary fail')),
140+
() => Promise.resolve(tsResponse('fallback')),
89141
)
142+
const testAdapter = await TestAdapter.start(adapter, t.context)
143+
144+
const response = await testAdapter.request({})
90145

146+
t.is(response.statusCode, 200)
147+
t.is(response.json().result, 'fallback')
148+
})
149+
150+
test.serial('uses fallback when primary does not return data', async (t) => {
151+
const adapter = buildFallbackAdapter(undefined, () => Promise.resolve(tsResponse('fallback')))
91152
const testAdapter = await TestAdapter.start(adapter, t.context)
153+
92154
const response = await testAdapter.request({})
93155

94156
t.is(response.statusCode, 200)
95-
t.is(response.json().result, 'from-fallback')
157+
t.is(response.json().result, 'fallback')
96158
})
97159

98160
test.serial('returns timeout when primary and fallback both fail', async (t) => {
99-
const adapter = buildFallbackAdapter(new NopTransport(), new NopTransport())
100-
161+
const adapter = buildFallbackAdapter()
101162
const testAdapter = await TestAdapter.start(adapter, t.context)
163+
102164
const response = await testAdapter.request({})
103165

104166
t.is(response.statusCode, 504)

test/metrics/metrics.test.ts

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import MockAdapter from 'axios-mock-adapter'
66
import { Adapter, AdapterEndpoint } from '../../src/adapter'
77
import { AdapterConfig, EmptyCustomSettings } from '../../src/config'
88
import { Metrics, retrieveCost } from '../../src/metrics'
9-
import { HttpTransport } from '../../src/transports'
9+
import { HttpTransport, TransportRoutes } from '../../src/transports'
1010
import { InputParameters } from '../../src/validation'
1111
import { TestAdapter } from '../../src/util/testing-utils'
1212

@@ -56,8 +56,8 @@ type RestEndpointTypes = {
5656

5757
const endpoint = '/price'
5858

59-
const createAdapterEndpoint = (): AdapterEndpoint<RestEndpointTypes> => {
60-
const restEndpointTransport = new HttpTransport<RestEndpointTypes>({
59+
const createRestTransport = (shouldFail: boolean) =>
60+
new HttpTransport<RestEndpointTypes>({
6161
prepareRequests: (params) => {
6262
return params.map((req) => ({
6363
params: [req],
@@ -73,28 +73,35 @@ const createAdapterEndpoint = (): AdapterEndpoint<RestEndpointTypes> => {
7373
}))
7474
},
7575
parseResponse: (params, res) => {
76-
return [
77-
{
78-
params: params[0],
79-
response: {
80-
data: res.data,
81-
statusCode: 200,
82-
result: res.data.price,
83-
timestamps: {
84-
providerIndicatedTimeUnixMs: Date.now() - 100,
76+
return shouldFail && params[0].from === 'fail'
77+
? []
78+
: [
79+
{
80+
params: params[0],
81+
response: {
82+
data: res.data,
83+
statusCode: 200,
84+
result: res.data.price,
85+
timestamps: {
86+
providerIndicatedTimeUnixMs: Date.now() - 100,
87+
},
88+
},
8589
},
86-
},
87-
},
88-
]
90+
]
8991
},
9092
})
9193

92-
return new AdapterEndpoint({
94+
const createAdapterEndpoint = () =>
95+
new AdapterEndpoint({
9396
name: 'TEST',
9497
inputParameters,
95-
transport: restEndpointTransport,
98+
transportRoutes: new TransportRoutes<RestEndpointTypes>()
99+
.register('rest', createRestTransport(true))
100+
.register('restprimary', createRestTransport(true))
101+
.register('restfallback', createRestTransport(false)),
102+
defaultTransport: 'rest',
103+
fallbackTransport: { restprimary: 'restfallback' },
96104
})
97-
}
98105

99106
const from = 'ETH'
100107
const to = 'USD'
@@ -103,6 +110,7 @@ const price = 1234
103110
test.before(async (t) => {
104111
t.context.clock = installTimers()
105112
process.env['METRICS_ENABLED'] = 'true'
113+
process.env['TRANSPORT_FALLBACK_ENABLED'] = 'true'
106114
const config = new AdapterConfig(
107115
{},
108116
{
@@ -192,7 +200,7 @@ test.serial('test basic metrics', async (t) => {
192200
name: 'cache_data_set_count',
193201
labels: {
194202
feed_id,
195-
participant_id: `TEST-test-default_single_transport-${feed_id}`,
203+
participant_id: `TEST-test-rest-${feed_id}`,
196204
cache_type: 'local',
197205
},
198206
expectedValue: 2,
@@ -202,7 +210,7 @@ test.serial('test basic metrics', async (t) => {
202210
name: 'cache_data_max_age',
203211
labels: {
204212
feed_id,
205-
participant_id: `TEST-test-default_single_transport-${feed_id}`,
213+
participant_id: `TEST-test-rest-${feed_id}`,
206214
cache_type: 'local',
207215
},
208216
expectedValue: 90000,
@@ -212,7 +220,7 @@ test.serial('test basic metrics', async (t) => {
212220
name: 'cache_data_staleness_seconds',
213221
labels: {
214222
feed_id,
215-
participant_id: `TEST-test-default_single_transport-${feed_id}`,
223+
participant_id: `TEST-test-rest-${feed_id}`,
216224
cache_type: 'local',
217225
},
218226
expectedValue: 0,
@@ -265,7 +273,7 @@ test.serial('test basic metrics', async (t) => {
265273
name: 'cache_data_get_count',
266274
labels: {
267275
feed_id,
268-
participant_id: `TEST-test-default_single_transport-${feed_id}`,
276+
participant_id: `TEST-test-rest-${feed_id}`,
269277
cache_type: 'local',
270278
},
271279
expectedValue: 1,
@@ -275,7 +283,7 @@ test.serial('test basic metrics', async (t) => {
275283
name: 'cache_data_get_values',
276284
labels: {
277285
feed_id,
278-
participant_id: `TEST-test-default_single_transport-${feed_id}`,
286+
participant_id: `TEST-test-rest-${feed_id}`,
279287
cache_type: 'local',
280288
},
281289
expectedValue: 1234,
@@ -285,7 +293,7 @@ test.serial('test basic metrics', async (t) => {
285293
name: 'cache_data_staleness_seconds',
286294
labels: {
287295
feed_id,
288-
participant_id: `TEST-test-default_single_transport-${feed_id}`,
296+
participant_id: `TEST-test-rest-${feed_id}`,
289297
cache_type: 'local',
290298
},
291299
})
@@ -332,7 +340,30 @@ test.serial('validate response.meta has the correct properties', async (t) => {
332340
t.deepEqual(response.meta, {
333341
adapterName: 'TEST',
334342
metrics: { feedId: '{"from":"eth","to":"usd"}' },
335-
transportName: 'default_single_transport',
343+
transportName: 'rest',
344+
})
345+
})
346+
347+
test.serial('validate response.meta uses fallback transport', async (t) => {
348+
axiosMock
349+
.onGet(`${URL}${endpoint}`, {
350+
params: {
351+
base: 'fail',
352+
quote: to,
353+
},
354+
})
355+
.reply(200, {
356+
price,
357+
})
358+
359+
const response = (
360+
await t.context.testAdapter.request({ from: 'fail', to, transport: 'restprimary' })
361+
).json()
362+
363+
t.deepEqual(response.meta, {
364+
adapterName: 'TEST',
365+
metrics: { feedId: '{"from":"fail","to":"usd"}' },
366+
transportName: 'restfallback',
336367
})
337368
})
338369

0 commit comments

Comments
 (0)