Skip to content

Commit 2a4a4eb

Browse files
authored
Refactor dxfeed (#4195)
* Copy ws * Refactor dxfeed * Comments
1 parent 1a70ddd commit 2a4a4eb

8 files changed

Lines changed: 216 additions & 202 deletions

File tree

.changeset/orange-ears-complain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@chainlink/dxfeed-adapter': patch
3+
---
4+
5+
Code refactor

packages/sources/dxfeed/src/config/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ export const config = new AdapterConfig({
1414
sensitive: true,
1515
},
1616
WS_API_ENDPOINT: {
17-
description: 'The websocket url for dxfeed',
17+
description: 'The websocket url for dxfeed including username and password',
1818
type: 'string',
19+
sensitive: true,
1920
},
2021
API_ENDPOINT: {
2122
description: 'The API url for dxfeed',
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1 @@
1-
export { buildDxFeedHttpTransport } from '../transport/price-http'
2-
export {
3-
BaseEndpointTypes,
4-
customInputValidation,
5-
inputParameters,
6-
endpoint as price,
7-
} from './price'
8-
export { buildDxFeedWsTransport } from '../transport/price-ws'
1+
export { endpoint as price } from './price'
Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,38 @@
11
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
22
import { TransportRoutes } from '@chainlink/external-adapter-framework/transports'
3-
import {
4-
AdapterRequest,
5-
SingleNumberResultResponse,
6-
} from '@chainlink/external-adapter-framework/util'
7-
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
3+
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
84
import {
95
AdapterError,
106
AdapterInputError,
117
} from '@chainlink/external-adapter-framework/validation/error'
128
import { config } from '../config'
139
import overrides from '../config/overrides.json'
1410
import { buildDxFeedHttpTransport } from '../transport/price-http'
15-
import { buildDxFeedWsTransport } from '../transport/price-ws'
16-
17-
export const inputParameters = new InputParameters(
18-
{
19-
base: {
20-
aliases: ['from', 'coin', 'market'],
21-
type: 'string',
22-
description: 'The symbol of the currency to query',
23-
required: true,
24-
},
25-
},
26-
[
27-
{
28-
base: 'TSLA',
29-
},
30-
],
31-
)
11+
import { transport as wsTransport } from '../transport/price-ws'
12+
import { inputParameters } from './utils'
3213

3314
export type BaseEndpointTypes = {
3415
Parameters: typeof inputParameters.definition
3516
Settings: typeof config.settings
3617
Response: SingleNumberResultResponse
3718
}
3819

39-
export function customInputValidation(
40-
req: AdapterRequest<typeof inputParameters.validated>,
41-
settings: typeof config.settings,
42-
): AdapterError | undefined {
43-
if (req.requestContext.transportName === 'ws' && !settings.WS_API_ENDPOINT) {
44-
return new AdapterInputError({
45-
statusCode: 400,
46-
message: 'WS_API_ENDPOINT is not set',
47-
})
48-
}
49-
return
50-
}
51-
5220
export const endpoint = new AdapterEndpoint({
5321
name: 'price',
5422
aliases: ['crypto', 'stock', 'forex', 'commodities'],
5523
transportRoutes: new TransportRoutes<BaseEndpointTypes>()
56-
.register('ws', buildDxFeedWsTransport())
24+
.register('ws', wsTransport)
5725
.register('rest', buildDxFeedHttpTransport()),
5826
defaultTransport: 'rest',
59-
inputParameters: inputParameters,
27+
inputParameters,
6028
overrides: overrides.dxfeed,
61-
customInputValidation,
29+
customInputValidation: (req, settings): AdapterError | undefined => {
30+
if (req.requestContext.transportName === 'ws' && !settings.WS_API_ENDPOINT) {
31+
return new AdapterInputError({
32+
statusCode: 400,
33+
message: 'WS_API_ENDPOINT is not set',
34+
})
35+
}
36+
return
37+
},
6238
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
2+
3+
export const inputParameters = new InputParameters(
4+
{
5+
base: {
6+
aliases: ['from', 'symbol', 'asset', 'coin', 'ticker', 'market'],
7+
type: 'string',
8+
description: 'The symbol of the currency to query',
9+
required: true,
10+
},
11+
},
12+
[
13+
{
14+
base: 'TSLA',
15+
},
16+
],
17+
)

packages/sources/dxfeed/src/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,3 @@ export const adapter = new Adapter({
1919
})
2020

2121
export const server = (): Promise<ServerInstance | undefined> => expose(adapter)
22-
23-
export default { config }
24-
export * from './endpoint'
Lines changed: 16 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,23 @@
1-
import { WebSocketTransport } from '@chainlink/external-adapter-framework/transports/websocket'
2-
import { makeLogger } from '@chainlink/external-adapter-framework/util'
31
import { BaseEndpointTypes } from '../endpoint/price'
4-
5-
const logger = makeLogger('DxFeed Websocket')
6-
7-
export type DXFeedMessage = {
8-
channel: string
9-
clientId?: string
10-
id: string
11-
data: [string, [string, number, number, number, number, string, number, string, number, number]]
12-
successful?: boolean
13-
advice?: {
14-
interval: number
15-
timeout: number
16-
reconnect: string
17-
}
18-
}[]
19-
20-
export type WsTransportTypes = BaseEndpointTypes & {
21-
Provider: {
22-
WsMessage: DXFeedMessage
23-
}
24-
}
25-
26-
const META_HANDSHAKE = '/meta/handshake'
27-
const META_CONNECT = '/meta/connect'
28-
const SERVICE_SUB = '/service/sub'
29-
const SERVICE_DATA = '/service/data'
2+
import { buildWsTransport } from './ws'
303

314
const tickerIndex = 0
325
const priceIndex = 6
336

34-
class DxFeedWebsocketTransport extends WebSocketTransport<WsTransportTypes> {
35-
private _connectionClientId = ''
36-
id = 0
37-
38-
get connectionClientId() {
39-
return this._connectionClientId
40-
}
41-
42-
set connectionClientId(id) {
43-
this._connectionClientId = id
44-
}
45-
46-
get handshakeMsg() {
47-
return [
48-
{
49-
id: ++this.id,
50-
version: '1.0',
51-
minimumVersion: '1.0',
52-
channel: META_HANDSHAKE,
53-
supportedConnectionTypes: ['websocket', 'long-polling', 'callback-polling'],
54-
advice: {
55-
timeout: 60000,
56-
interval: 0,
7+
export const transport = buildWsTransport<BaseEndpointTypes>(
8+
(params) => ({ Quote: [params.base.toUpperCase()] }),
9+
(message) => {
10+
const base = message[0].data[1][tickerIndex]
11+
const price = message[0].data[1][priceIndex]
12+
13+
return {
14+
params: { base },
15+
response: {
16+
result: price,
17+
data: {
18+
result: price,
5719
},
5820
},
59-
]
60-
}
61-
62-
get firstHeartbeatMsg() {
63-
return [
64-
{
65-
id: ++this.id,
66-
clientId: this.connectionClientId,
67-
channel: META_CONNECT,
68-
connectionType: 'websocket',
69-
advice: {
70-
timeout: 60000,
71-
},
72-
},
73-
]
74-
}
75-
76-
get heartbeatMsg() {
77-
return [
78-
{
79-
id: ++this.id,
80-
clientId: this.connectionClientId,
81-
channel: META_CONNECT,
82-
connectionType: 'websocket',
83-
},
84-
]
85-
}
86-
}
87-
88-
export function buildDxFeedWsTransport(): DxFeedWebsocketTransport {
89-
const wsTransport: DxFeedWebsocketTransport = new DxFeedWebsocketTransport({
90-
url: (context) => context.adapterSettings.WS_API_ENDPOINT || '',
91-
92-
handlers: {
93-
open(connection) {
94-
return new Promise((resolve) => {
95-
connection.addEventListener('message', (event: MessageEvent) => {
96-
const message: DXFeedMessage[0] = JSON.parse(event.data.toString())[0]
97-
if (message.clientId && message.channel === '/meta/handshake') {
98-
wsTransport.connectionClientId = message.clientId
99-
connection.send(JSON.stringify(wsTransport.firstHeartbeatMsg))
100-
}
101-
102-
if (message.channel === '/meta/connect') {
103-
connection.send(JSON.stringify(wsTransport.heartbeatMsg))
104-
resolve()
105-
}
106-
})
107-
connection.send(JSON.stringify(wsTransport.handshakeMsg))
108-
})
109-
},
110-
message(message) {
111-
// If dxfeed errors there is no information about failed feeds/params in the message, returning empty array. We need strict comparison because dxfeed sends info messages also that don't contain `successful` property.
112-
if (message[0].successful === false) {
113-
logger.warn('Dxfeed returned unsuccessful message')
114-
return []
115-
}
116-
117-
if (!Array.isArray(message) || message[0].channel !== SERVICE_DATA) {
118-
return []
119-
}
120-
121-
const base = message[0].data[1][tickerIndex]
122-
const price = message[0].data[1][priceIndex]
123-
return [
124-
{
125-
params: { base },
126-
response: {
127-
result: price,
128-
data: {
129-
result: price,
130-
},
131-
},
132-
},
133-
]
134-
},
135-
},
136-
137-
builders: {
138-
subscribeMessage: (params) => {
139-
return [
140-
{
141-
channel: SERVICE_SUB,
142-
data: { add: { Quote: [params.base.toUpperCase()] } },
143-
clientId: `${wsTransport.connectionClientId}`,
144-
},
145-
]
146-
},
147-
unsubscribeMessage: (params) => {
148-
return [
149-
{
150-
channel: SERVICE_SUB,
151-
data: { remove: { Quote: [params.base.toUpperCase()] } },
152-
clientId: `${wsTransport.connectionClientId}`,
153-
},
154-
]
155-
},
156-
},
157-
})
158-
return wsTransport
159-
}
21+
}
22+
},
23+
)

0 commit comments

Comments
 (0)