Skip to content

Commit

Permalink
Added WS transport for coinpaprika (#3063)
Browse files Browse the repository at this point in the history
* add ws transport for crypto endpoints

* refactor ws transport

* Marketcap & Volume soak test payloads

---------

Co-authored-by: Alec Gard <[email protected]>
Co-authored-by: cl-ea <[email protected]>
  • Loading branch information
3 people authored Nov 20, 2023
1 parent 82f3591 commit 0a5e11e
Show file tree
Hide file tree
Showing 20 changed files with 1,188 additions and 657 deletions.
5 changes: 5 additions & 0 deletions .changeset/loud-needles-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/coinpaprika-adapter': patch
---

Added websocket transport for crypto(price), volume and marketcap endpoints. Refactored crypto endpoint/transport
2 changes: 2 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

127 changes: 113 additions & 14 deletions packages/sources/coinpaprika/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ This document was generated automatically. Please see [README Generator](../../s

## Environment Variables

| Required? | Name | Description | Type | Options | Default |
| :-------: | :----------: | :--------------------------------: | :----: | :-----: | :-----: |
| | API_ENDPOINT | The HTTP URL to retrieve data from | string | | |
| | API_KEY | An API key for Coinpaprika | string | | |
| Required? | Name | Description | Type | Options | Default |
| :-------: | :-------------: | :---------------------------------: | :----: | :-----: | :-------------------------------------: |
| | API_ENDPOINT | The HTTP URL to retrieve data from | string | | |
| | API_KEY | An API key for Coinpaprika | string | | |
| | WS_API_ENDPOINT | The WS API endpoint for Coinpaprika | string | | `wss://streaming.coinpaprika.com/ticks` |

---

Expand All @@ -26,22 +27,22 @@ This document was generated automatically. Please see [README Generator](../../s

## Input Parameters

| Required? | Name | Description | Type | Options | Default |
| :-------: | :------: | :-----------------: | :----: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: |
| | endpoint | The endpoint to use | string | [coins](#coins-endpoint), [crypto-vwap](#vwap-endpoint), [crypto](#crypto-endpoint), [dominance](#globalmarketcap-endpoint), [globalmarketcap](#globalmarketcap-endpoint), [marketcap](#crypto-endpoint), [price](#crypto-endpoint), [volume](#crypto-endpoint), [vwap](#vwap-endpoint) | `crypto` |
| Required? | Name | Description | Type | Options | Default |
| :-------: | :------: | :-----------------: | :----: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: |
| | endpoint | The endpoint to use | string | [coins](#coins-endpoint), [crypto-vwap](#vwap-endpoint), [crypto](#crypto-endpoint), [dominance](#globalmarketcap-endpoint), [globalmarketcap](#globalmarketcap-endpoint), [marketcap](#marketcap-endpoint), [price](#crypto-endpoint), [volume](#volume-endpoint), [vwap](#vwap-endpoint) | `crypto` |

## Crypto Endpoint

Supported names for this endpoint are: `crypto`, `marketcap`, `price`, `volume`.
Supported names for this endpoint are: `crypto`, `price`.

### Input Params

| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :--------: | :------------: | :--------------------------------------------------------------------: | :----: | :---------------------------------: | :-----: | :--------: | :------------: |
|| base | `coin`, `from` | The symbol of symbols of the currency to query | string | | | | |
|| quote | `market`, `to` | The symbol of the currency to convert to | string | | | | |
| | coinid | | The coin ID (optional to use in place of `base`) | string | | | | |
| | resultPath | | The path to the result within the asset quote in the provider response | string | `market_cap`, `price`, `volume_24h` | | | |
| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :--------: | :------------: | :------------------------------------------------------------------------------------: | :----: | :---------------------------------: | :-----: | :--------: | :------------: |
|| base | `coin`, `from` | The symbol of symbols of the currency to query | string | | | | |
|| quote | `market`, `to` | The symbol of the currency to convert to | string | | | | |
| | coinid | | The coin ID (optional to use in place of `base`) | string | | | | |
| | resultPath | | The path to the result within the asset quote in the provider response (only for REST) | string | `market_cap`, `price`, `volume_24h` | | | |

### Example

Expand Down Expand Up @@ -156,4 +157,102 @@ Request:

---

## Volume Endpoint

`volume` is the only supported name for this endpoint.

### Input Params

| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :--------: | :------------: | :------------------------------------------------------------------------------------: | :----: | :---------------------------------: | :-----: | :--------: | :------------: |
|| base | `coin`, `from` | The symbol of symbols of the currency to query | string | | | | |
|| quote | `market`, `to` | The symbol of the currency to convert to | string | | | | |
| | coinid | | The coin ID (optional to use in place of `base`) | string | | | | |
| | resultPath | | The path to the result within the asset quote in the provider response (only for REST) | string | `market_cap`, `price`, `volume_24h` | | | |

### Example

Request:

```json
{
"data": {
"endpoint": "volume",
"base": "AAAA",
"coinid": "eth-ethereum",
"quote": "USD",
"resultPath": "price"
}
}
```

<details>
<summary>Additional Examples</summary>

Request:

```json
{
"data": {
"endpoint": "volume",
"base": "ETH",
"quote": "USD",
"resultPath": "volume_24h"
}
}
```

</details>

---

## Marketcap Endpoint

`marketcap` is the only supported name for this endpoint.

### Input Params

| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :--------: | :------------: | :------------------------------------------------------------------------------------: | :----: | :---------------------------------: | :-----: | :--------: | :------------: |
|| base | `coin`, `from` | The symbol of symbols of the currency to query | string | | | | |
|| quote | `market`, `to` | The symbol of the currency to convert to | string | | | | |
| | coinid | | The coin ID (optional to use in place of `base`) | string | | | | |
| | resultPath | | The path to the result within the asset quote in the provider response (only for REST) | string | `market_cap`, `price`, `volume_24h` | | | |

### Example

Request:

```json
{
"data": {
"endpoint": "marketcap",
"base": "AAAA",
"coinid": "eth-ethereum",
"quote": "USD",
"resultPath": "price"
}
}
```

<details>
<summary>Additional Examples</summary>

Request:

```json
{
"data": {
"endpoint": "marketcap",
"base": "ETH",
"quote": "USD",
"resultPath": "volume_24h"
}
}
```

</details>

---

MIT License
2 changes: 2 additions & 0 deletions packages/sources/coinpaprika/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
"tslib": "^2.3.1"
},
"devDependencies": {
"@sinonjs/fake-timers": "9.1.2",
"@types/jest": "27.5.2",
"@types/node": "16.11.51",
"@types/sinonjs__fake-timers": "8.1.3",
"nock": "13.2.9",
"typescript": "5.0.4"
}
Expand Down
7 changes: 7 additions & 0 deletions packages/sources/coinpaprika/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export const config = new AdapterConfig({
required: false,
sensitive: true,
},
WS_API_ENDPOINT: {
description: 'The WS API endpoint for Coinpaprika',
default: 'wss://streaming.coinpaprika.com/ticks',
type: 'string',
},
})

export const getApiEndpoint = (settings: typeof config.settings): string =>
Expand All @@ -27,3 +32,5 @@ export const getApiHeaders = (settings: typeof config.settings): { Authorization
}
return headers
}

export const AVAILABLE_WS_QUOTES = ['USD', 'BTC', 'ETH', 'BNB', 'MATIC'] as const
81 changes: 10 additions & 71 deletions packages/sources/coinpaprika/src/endpoint/crypto.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,17 @@
import { CryptoPriceEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { config } from '../config'
import { transport } from '../transport/crypto'
import { TransportRoutes } from '@chainlink/external-adapter-framework/transports'
import { buildCryptoHttpTransport, buildWebsocketTransport } from '../transport/utils'
import { BaseEndpointTypes, cryptoInputParameters, customInputValidation } from './utils'
import overrides from '../config/overrides.json'
export const inputParameters = new InputParameters(
{
base: {
aliases: ['from', 'coin'],
description: 'The symbol of symbols of the currency to query',
required: true,
type: 'string',
},
quote: {
aliases: ['to', 'market'],
description: 'The symbol of the currency to convert to',
required: true,
type: 'string',
},
coinid: {
description: 'The coin ID (optional to use in place of `base`)',
required: false,
type: 'string',
},
resultPath: {
description: 'The path to the result within the asset quote in the provider response',
required: false,
type: 'string',
options: ['price', 'volume_24h', 'market_cap'],
},
},
[
{
base: 'AAAA',
coinid: 'eth-ethereum',
quote: 'USD',
resultPath: 'price',
},
{
base: 'ETH',
quote: 'USD',
resultPath: 'volume_24h',
},
],
)

export type BaseEndpointTypes = {
Parameters: typeof inputParameters.definition
Settings: typeof config.settings
Response: SingleNumberResultResponse
}

// Maps the input parameter value with the value that will be set in the requestContext.data object
const resultPathMap = {
price: 'price',
crypto: 'price',
volume: 'volume_24h',
marketcap: 'market_cap',
} as const

export const endpoint = new CryptoPriceEndpoint({
name: 'crypto',
aliases: ['price', 'marketcap', 'volume'],
requestTransforms: [
(request) => {
if (!request.requestContext.data.resultPath) {
const endpoint =
(request.body.data as { endpoint: keyof typeof resultPathMap }).endpoint ||
request.requestContext.endpointName
request.requestContext.data.resultPath = resultPathMap[endpoint]
}
},
],
transport,
inputParameters: inputParameters,
aliases: ['price'],
transportRoutes: new TransportRoutes<BaseEndpointTypes>()
.register('ws', buildWebsocketTransport('p'))
.register('rest', buildCryptoHttpTransport('price')),
defaultTransport: 'rest',
inputParameters: cryptoInputParameters,
overrides: overrides.coinpaprika,
customInputValidation,
})
2 changes: 2 additions & 0 deletions packages/sources/coinpaprika/src/endpoint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export { endpoint as coins } from './coins'
export { endpoint as crypto } from './crypto'
export { endpoint as global } from './global'
export { endpoint as vwap } from './vwap'
export { endpoint as volume } from './volume'
export { endpoint as marketcap } from './marketcap'
16 changes: 16 additions & 0 deletions packages/sources/coinpaprika/src/endpoint/marketcap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PriceEndpoint } from '@chainlink/external-adapter-framework/adapter'
import overrides from '../config/overrides.json'
import { TransportRoutes } from '@chainlink/external-adapter-framework/transports'
import { buildCryptoHttpTransport, buildWebsocketTransport } from '../transport/utils'
import { BaseEndpointTypes, cryptoInputParameters, customInputValidation } from './utils'

export const endpoint = new PriceEndpoint({
name: 'marketcap',
transportRoutes: new TransportRoutes<BaseEndpointTypes>()
.register('ws', buildWebsocketTransport('m'))
.register('rest', buildCryptoHttpTransport('market_cap')),
defaultTransport: 'rest',
inputParameters: cryptoInputParameters,
overrides: overrides.coinpaprika,
customInputValidation,
})
83 changes: 83 additions & 0 deletions packages/sources/coinpaprika/src/endpoint/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import {
AdapterRequest,
SingleNumberResultResponse,
} from '@chainlink/external-adapter-framework/util'

import { AVAILABLE_WS_QUOTES, config } from '../config'
import {
AdapterError,
AdapterInputError,
} from '@chainlink/external-adapter-framework/validation/error'

export const cryptoInputParameters = new InputParameters(
{
base: {
aliases: ['from', 'coin'],
description: 'The symbol of symbols of the currency to query',
required: true,
type: 'string',
},
quote: {
aliases: ['to', 'market'],
description: 'The symbol of the currency to convert to',
required: true,
type: 'string',
},
coinid: {
description: 'The coin ID (optional to use in place of `base`)',
required: false,
type: 'string',
},
resultPath: {
description:
'The path to the result within the asset quote in the provider response (only for REST)',
required: false,
type: 'string',
options: ['price', 'volume_24h', 'market_cap'],
},
},
[
{
base: 'AAAA',
coinid: 'eth-ethereum',
quote: 'USD',
resultPath: 'price',
},
{
base: 'ETH',
quote: 'USD',
resultPath: 'volume_24h',
},
],
)

export type BaseEndpointTypes = {
Parameters: typeof cryptoInputParameters.definition
Settings: typeof config.settings
Response: SingleNumberResultResponse
}

export function customInputValidation(
req: AdapterRequest<typeof cryptoInputParameters.validated>,
settings: typeof config.settings,
): AdapterError | undefined {
if (req.requestContext.transportName === 'ws' && !settings.WS_API_ENDPOINT) {
return new AdapterInputError({
statusCode: 400,
message: 'WS_API_ENDPOINT is required for streaming data',
})
}
if (
req.requestContext.transportName === 'ws' &&
!AVAILABLE_WS_QUOTES.includes(
req.requestContext.data.quote as (typeof AVAILABLE_WS_QUOTES)[number],
)
) {
return new AdapterInputError({
statusCode: 400,
message: `Invalid quote. Available quotes for websocket are - ${AVAILABLE_WS_QUOTES}`,
})
}
return
}
Loading

0 comments on commit 0a5e11e

Please sign in to comment.