|
1 | | -import { WebSocketTransport } from '@chainlink/external-adapter-framework/transports/websocket' |
2 | | -import { makeLogger } from '@chainlink/external-adapter-framework/util' |
3 | 1 | 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' |
30 | 3 |
|
31 | 4 | const tickerIndex = 0 |
32 | 5 | const priceIndex = 6 |
33 | 6 |
|
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, |
57 | 19 | }, |
58 | 20 | }, |
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