diff --git a/.changeset/gorgeous-toys-taste.md b/.changeset/gorgeous-toys-taste.md new file mode 100644 index 0000000000..f65411c746 --- /dev/null +++ b/.changeset/gorgeous-toys-taste.md @@ -0,0 +1,5 @@ +--- +'@chainlink/tradingeconomics-adapter': patch +--- + +Added support for cache TTL refresh on heartbeat messages for Forex endpoint diff --git a/packages/sources/tradingeconomics/README.md b/packages/sources/tradingeconomics/README.md index 64a202afc9..d5ade9fc4c 100644 --- a/packages/sources/tradingeconomics/README.md +++ b/packages/sources/tradingeconomics/README.md @@ -4,6 +4,12 @@ This document was generated automatically. Please see [README Generator](../../scripts#readme-generator) for more info. +## Known Issues + +### CACHE_MAX_AGE interaction with Heartbeat messages + +If `CACHE_MAX_AGE` is set below a current heartbeat interval (45000ms), the extended cache TTL feature for out-of-market-hours that relies on heartbeats will not work. + ## Environment Variables | Required? | Name | Description | Type | Options | Default | diff --git a/packages/sources/tradingeconomics/docs/known-issues.md b/packages/sources/tradingeconomics/docs/known-issues.md new file mode 100644 index 0000000000..a573f278cc --- /dev/null +++ b/packages/sources/tradingeconomics/docs/known-issues.md @@ -0,0 +1,5 @@ +## Known Issues + +### CACHE_MAX_AGE interaction with Heartbeat messages + +If `CACHE_MAX_AGE` is set below a current heartbeat interval (45000ms), the extended cache TTL feature for out-of-market-hours that relies on heartbeats will not work. diff --git a/packages/sources/tradingeconomics/src/transport/price-ws.ts b/packages/sources/tradingeconomics/src/transport/price-ws.ts index db3dee313e..a42d5b879c 100644 --- a/packages/sources/tradingeconomics/src/transport/price-ws.ts +++ b/packages/sources/tradingeconomics/src/transport/price-ws.ts @@ -1,4 +1,7 @@ -import { WebsocketReverseMappingTransport } from '@chainlink/external-adapter-framework/transports' +import { + WebsocketReverseMappingTransport, + WebSocketTransport, +} from '@chainlink/external-adapter-framework/transports' import { makeLogger } from '@chainlink/external-adapter-framework/util' import { BaseEndpointTypes } from '../endpoint/price' @@ -27,6 +30,14 @@ type WsTransportTypes = BaseEndpointTypes & { WsMessage: Message } } +/* +Tradingeconomics EA currently does not receive asset prices during off-market hours. When a heartbeat message is received during these hours, +we update the TTL of cache entries that EA is requested to provide a price during off-market hours. + */ +const updateTTL = async (transport: WebSocketTransport, ttl: number) => { + const params = await transport.subscriptionSet.getAll() + transport.responseCache.writeTTL(transport.name, params, ttl) +} export const wsTransport: WebsocketReverseMappingTransport = new WebsocketReverseMappingTransport({ @@ -35,10 +46,17 @@ export const wsTransport: WebsocketReverseMappingTransport { - if (!message.topic || message.topic === 'keepalive') { + message: (message, context) => { + if (!message.topic) { + return [] + } + // Check for a heartbeat message, refresh the TTLs of all requested entries in the cache + if (message.topic === 'keepalive') { + wsTransport.lastMessageReceivedAt = Date.now() + updateTTL(wsTransport, context.adapterSettings.CACHE_MAX_AGE) return [] } + const pair = wsTransport.getReverseMapping(message.s) if (!pair) { logger.error(`Pair not found in websocket reverse map for message symbol - ${message.s}`) diff --git a/packages/sources/tradingeconomics/test/integration/__snapshots__/adapter-ws.test.ts.snap b/packages/sources/tradingeconomics/test/integration/__snapshots__/adapter-ws.test.ts.snap index 9bbfef67e5..5defa6e45d 100644 --- a/packages/sources/tradingeconomics/test/integration/__snapshots__/adapter-ws.test.ts.snap +++ b/packages/sources/tradingeconomics/test/integration/__snapshots__/adapter-ws.test.ts.snap @@ -15,6 +15,21 @@ exports[`websocket price endpoint should return success 1`] = ` } `; +exports[`websocket price endpoint should update the ttl after heartbeat is received 1`] = ` +{ + "data": { + "result": 0.776530152665828, + }, + "result": 0.776530152665828, + "statusCode": 200, + "timestamps": { + "providerDataReceivedUnixMs": 1018, + "providerDataStreamEstablishedUnixMs": 1010, + "providerIndicatedTimeUnixMs": 1659472542655, + }, +} +`; + exports[`websocket stock endpoint should return success 1`] = ` { "data": { diff --git a/packages/sources/tradingeconomics/test/integration/adapter-ws.test.ts b/packages/sources/tradingeconomics/test/integration/adapter-ws.test.ts index 02f9d9e983..31fa9505fa 100644 --- a/packages/sources/tradingeconomics/test/integration/adapter-ws.test.ts +++ b/packages/sources/tradingeconomics/test/integration/adapter-ws.test.ts @@ -3,6 +3,7 @@ import { setEnvVariables, mockWebSocketProvider, MockWebsocketServer, + runAllUntilTime, } from '@chainlink/external-adapter-framework/util/testing-utils' import FakeTimers from '@sinonjs/fake-timers' import { WebSocketClassProvider } from '@chainlink/external-adapter-framework/transports' @@ -52,16 +53,25 @@ describe('websocket', () => { await testAdapter.api.close() }) - describe('price endpoint', () => { + describe('stock endpoint', () => { it('should return success', async () => { - const response = await testAdapter.request(dataPrice) + const response = await testAdapter.request(dataStock) expect(response.json()).toMatchSnapshot() }) }) - describe('stock endpoint', () => { + describe('price endpoint', () => { it('should return success', async () => { - const response = await testAdapter.request(dataStock) + const response = await testAdapter.request(dataPrice) + expect(response.json()).toMatchSnapshot() + }) + + it('should update the ttl after heartbeat is received', async () => { + // The cache ttl is 90 seconds. Mocked heartbeat message is sent after 10s after connection which should + // update the ttl and therefore after 93 seconds (from the initial message) we can access the asset + await runAllUntilTime(testAdapter.clock, 93000) + const response = await testAdapter.request(dataPrice) + expect(response.statusCode).toBe(200) expect(response.json()).toMatchSnapshot() }) }) diff --git a/packages/sources/tradingeconomics/test/integration/fixtures.ts b/packages/sources/tradingeconomics/test/integration/fixtures.ts index ecc7483b28..5f28d09058 100644 --- a/packages/sources/tradingeconomics/test/integration/fixtures.ts +++ b/packages/sources/tradingeconomics/test/integration/fixtures.ts @@ -140,6 +140,9 @@ export const mockWebSocketServer = (url: string) => { topic: 'USDCAD', }), ) + setTimeout(() => { + socket.send(JSON.stringify({ topic: 'keepalive' })) + }, 10000) } else { // stock endpoint socket.send(