Skip to content

Commit 3e860fe

Browse files
authored
Allow hyphen in override adapter name (#442)
1 parent f91652d commit 3e860fe

File tree

5 files changed

+179
-12
lines changed

5 files changed

+179
-12
lines changed

src/adapter/endpoint.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { ResponseCache } from '../cache/response'
22
import { AdapterSettings } from '../config'
33
import { TransportRoutes } from '../transports'
4-
import { AdapterRequest, AdapterRequestData, Overrides, makeLogger } from '../util'
4+
import {
5+
AdapterRequest,
6+
AdapterRequestData,
7+
Overrides,
8+
makeLogger,
9+
getCanonicalAdapterName,
10+
canonicalizeAdapterNameKeys,
11+
} from '../util'
512
import { InputParameters } from '../validation'
613
import { AdapterError } from '../validation/error'
714
import { TypeFromDefinition } from '../validation/input-params'
@@ -111,8 +118,10 @@ export class AdapterEndpoint<T extends EndpointGenerics> implements AdapterEndpo
111118
}
112119

113120
getRequestOverrides(data: Record<string, string>, overrides?: Overrides) {
114-
const overrideAdapterName = data['adapterNameOverride']
115-
return overrides?.[overrideAdapterName] || overrides?.[this.adapterName.toLowerCase()]
121+
const overrideAdapterName = getCanonicalAdapterName(data['adapterNameOverride'])
122+
const adapterName = getCanonicalAdapterName(this.adapterName)
123+
const canonicalOverrides: Overrides | undefined = canonicalizeAdapterNameKeys(overrides)
124+
return canonicalOverrides?.[overrideAdapterName] || canonicalOverrides?.[adapterName]
116125
}
117126

118127
/**
@@ -185,8 +194,11 @@ export class AdapterEndpoint<T extends EndpointGenerics> implements AdapterEndpo
185194
* @returns the transport param or override if present
186195
*/
187196
private defaultRouter(req: AdapterRequest<TypeFromDefinition<T['Parameters']>>) {
197+
// DefaultRouter is called before customInputValidation, so we don't have
198+
// the validation data on the requestContext yet.
199+
const data: Record<string, string> = {}
188200
const rawRequestBody = req.body as unknown as { data: AdapterRequestData }
189-
const requestOverrides = rawRequestBody.data?.overrides?.[this.adapterName.toLowerCase()]
201+
const requestOverrides = this.getRequestOverrides(data, rawRequestBody.data?.overrides)
190202
// Transport override
191203
if (requestOverrides?.['transport']) {
192204
return requestOverrides['transport']

src/util/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,34 @@ export const groupArrayByKey = <T extends Record<string, string>, K extends keyo
176176
{} as Record<T[K], T[]>,
177177
)
178178
}
179+
180+
/**
181+
* Returns adapter name in lower case and with hyphens replaced by underscores.
182+
* Types are declared such that the output type is defined iff the input type
183+
* is defined.
184+
* @param name - The name to canonicalize
185+
* @returns The canonical name
186+
*/
187+
export function getCanonicalAdapterName(name: string): string
188+
export function getCanonicalAdapterName(name: undefined): undefined
189+
export function getCanonicalAdapterName(name: string | undefined): string | undefined {
190+
return name?.toLowerCase().replace(/-/g, '_')
191+
}
192+
193+
type StringKeys<T> = { [key: string]: T }
194+
195+
/**
196+
* Returns the record with adapter names as keys after canonicalizing the keys.
197+
* @param obj - The record with adapter names as keys
198+
* @returns The record with canonicalized adapter names as keys
199+
*/
200+
export const canonicalizeAdapterNameKeys = <T>(
201+
obj: StringKeys<T> | undefined,
202+
): StringKeys<T> | undefined => {
203+
if (obj === undefined) {
204+
return undefined
205+
}
206+
return Object.fromEntries(
207+
Object.entries(obj).map(([key, value]) => [getCanonicalAdapterName(key), value]),
208+
)
209+
}

test/overrides.test.ts

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class OverrideTestTransport implements Transport<TestTransportGenerics> {
6868

6969
test.beforeEach(async (t) => {
7070
const adapter = new Adapter({
71-
name: 'TEST',
71+
name: 'TEST_ADAPTER',
7272
defaultEndpoint: 'test-endpoint',
7373
endpoints: [
7474
new AdapterEndpoint({
@@ -103,7 +103,7 @@ test('request overrides are respected', async (t) => {
103103
base: 'OVER2',
104104
quote: 'USD',
105105
overrides: {
106-
test: {
106+
test_adapter: {
107107
OVER2: 'qweqwe',
108108
},
109109
},
@@ -137,7 +137,7 @@ test('request overrides take precedence over adapter hardcoded ones', async (t)
137137
base: 'OVER1',
138138
quote: 'USD',
139139
overrides: {
140-
test: {
140+
test_adapter: {
141141
OVER1: 'priority',
142142
},
143143
},
@@ -154,7 +154,7 @@ test('request overrides that resolve field to overridable symbol are not overrid
154154
base: 'OVER2',
155155
quote: 'USD',
156156
overrides: {
157-
test: {
157+
test_adapter: {
158158
OVER2: 'OVER1',
159159
},
160160
},
@@ -204,13 +204,34 @@ test('adapter with overrideAdapterName uses overrideAdapterName', async (t) => {
204204
})
205205
})
206206

207+
test('adapterNameOverride can use hyphen', async (t) => {
208+
const response = await t.context.testAdapter.request({
209+
base: 'OVER2',
210+
quote: 'USD',
211+
adapterNameOverride: 'override-test',
212+
overrides: {
213+
override_test: {
214+
OVER2: 'valid',
215+
},
216+
test: {
217+
OVER2: 'invalid',
218+
},
219+
},
220+
})
221+
222+
t.deepEqual(response.json().data, {
223+
base: 'valid',
224+
quote: 'USD',
225+
})
226+
})
227+
207228
test('adapter with overrideAdapterName uses original name if no override specified for overrideAdapterName', async (t) => {
208229
const response = await t.context.testAdapter.request({
209230
base: 'OVER2',
210231
quote: 'USD',
211232
adapterNameOverride: 'overridetest',
212233
overrides: {
213-
test: {
234+
test_adapter: {
214235
OVER2: 'overridden',
215236
},
216237
},
@@ -221,3 +242,37 @@ test('adapter with overrideAdapterName uses original name if no override specifi
221242
quote: 'USD',
222243
})
223244
})
245+
246+
test('adapter name in overrides can have hyphen', async (t) => {
247+
const response = await t.context.testAdapter.request({
248+
base: 'OVER2',
249+
quote: 'USD',
250+
overrides: {
251+
'test-adapter': {
252+
OVER2: 'qweqwe',
253+
},
254+
},
255+
})
256+
257+
t.deepEqual(response.json().data, {
258+
base: 'qweqwe',
259+
quote: 'USD',
260+
})
261+
})
262+
263+
test('adapter name in overrides can use upper case', async (t) => {
264+
const response = await t.context.testAdapter.request({
265+
base: 'OVER2',
266+
quote: 'USD',
267+
overrides: {
268+
TEST_ADAPTER: {
269+
OVER2: 'qweqwe',
270+
},
271+
},
272+
})
273+
274+
t.deepEqual(response.json().data, {
275+
base: 'qweqwe',
276+
quote: 'USD',
277+
})
278+
})

test/transports/routing.test.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ test.beforeEach(async (t) => {
291291
})
292292

293293
const sampleAdapter = new Adapter({
294-
name: 'TEST',
294+
name: 'TEST_ADAPTER',
295295
defaultEndpoint: 'price',
296296
config: customConfig,
297297
endpoints: [sampleEndpoint],
@@ -704,7 +704,42 @@ test.serial('transport override routes to correct Transport', async (t) => {
704704
to,
705705
transport: 'websocket',
706706
overrides: {
707-
test: {
707+
test_adapter: {
708+
transport: 'batch',
709+
},
710+
},
711+
})
712+
713+
t.is(error.statusCode, 504)
714+
const internalTransport = transports.get('batch') as unknown as MockHttpTransport
715+
t.assert(internalTransport.registerRequestCalls > 0)
716+
})
717+
718+
test.serial('transport override adapter name can use hyphen', async (t) => {
719+
axiosMock
720+
.onPost(`${restUrl}/price`, {
721+
pairs: [
722+
{
723+
base: from,
724+
quote: to,
725+
},
726+
],
727+
})
728+
.reply(200, {
729+
prices: [
730+
{
731+
pair: `${from}/${to}`,
732+
price,
733+
},
734+
],
735+
})
736+
737+
const error = await t.context.testAdapter.request({
738+
from,
739+
to,
740+
transport: 'websocket',
741+
overrides: {
742+
'test-adapter': {
708743
transport: 'batch',
709744
},
710745
},

test/utils.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { groupArrayByKey, splitArrayIntoChunks } from '../src/util'
1+
import {
2+
groupArrayByKey,
3+
splitArrayIntoChunks,
4+
getCanonicalAdapterName,
5+
canonicalizeAdapterNameKeys,
6+
} from '../src/util'
27
import test from 'ava'
38

49
test('Test splitArrayIntoChunks function', async (t) => {
@@ -37,3 +42,32 @@ test('Test groupArrayByKey function', async (t) => {
3742
DASH: [{ base: 'LTC', quote: 'DASH' }],
3843
})
3944
})
45+
46+
test('Test getCanonicalAdapterName', async (t) => {
47+
t.is(getCanonicalAdapterName(undefined), undefined)
48+
t.is(getCanonicalAdapterName('test'), 'test')
49+
t.is(getCanonicalAdapterName('TEST'), 'test')
50+
t.is(getCanonicalAdapterName('TEST-adapter'), 'test_adapter')
51+
t.is(getCanonicalAdapterName('TEST_ADAPTER'), 'test_adapter')
52+
t.is(getCanonicalAdapterName('A-B-C-D-e-f'), 'a_b_c_d_e_f')
53+
})
54+
55+
test('Test canonicalizeAdapterNameKeys', async (t) => {
56+
t.deepEqual(canonicalizeAdapterNameKeys(undefined), undefined)
57+
t.deepEqual(
58+
canonicalizeAdapterNameKeys({
59+
test: 1,
60+
TEST2: 2,
61+
'TEST-adapter': 3,
62+
TEST_ADAPTER2: 4,
63+
'A-B-C-D-e-f': 5,
64+
}),
65+
{
66+
test: 1,
67+
test2: 2,
68+
test_adapter: 3,
69+
test_adapter2: 4,
70+
a_b_c_d_e_f: 5,
71+
},
72+
)
73+
})

0 commit comments

Comments
 (0)