Skip to content

Commit

Permalink
Merge pull request #278 from smartcontractkit/feature/dxfeed-secondary
Browse files Browse the repository at this point in the history
dxfeed-secondary adapter
  • Loading branch information
thodges-gh authored Jan 28, 2021
2 parents 6c7edd1 + dadf568 commit 684f538
Show file tree
Hide file tree
Showing 26 changed files with 516 additions and 154 deletions.
1 change: 1 addition & 0 deletions .github/strategy/adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"deribit",
"dns-query",
"dxfeed",
"dxfeed-secondary",
"eodhistoricaldata",
"etherchain",
"ethgasstation",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- `bitcoin-json-rpc`: composite adapter for querying bitcoin blockchain stats(difficulty, height) according to the existing convention
- `iex-cloud` to get stock and crypto market data from IEX Cloud
- `cfbenchmarks` to get crypto benchmarks and indices
- `dxfeed-secondary` to handle secondary mappings for the TSLA symbol
- Added support for metadata in requests. This gives adapters access to the FM on-chain round state.
- Moves re-usable test behaviors & testing utils to a new package - `@chainlink/adapter-test-helpers`
- Added support for using query string parameters as input to adapters.
Expand Down
3 changes: 3 additions & 0 deletions dxfeed-secondary/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
...require('../.eslintrc.ts.js'),
}
50 changes: 50 additions & 0 deletions dxfeed-secondary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Chainlink External Adapter for dxFeed

An adapter to add secondary mapping for symbols:

```bash
TSLA ➡️ 'TSLA.US:TEI'
```

## Configuration

This adapter supports the following environment variables:

- `API_USERNAME`: Your API username
- `API_PASSWORD`: Your API password
- `API_ENDPOINT`: The endpoint for your dxFeed. Defaults to the demo endpoint (`https://tools.dxfeed.com/webservice/rest`)

## Input Params

- `base`, `from`, or `asset`: The symbol of the asset to query
- `endpoint`: Optional endpoint param

## Output

```json
{
"jobRunID": "1",
"data": {
"status": "OK",
"Trade": {
"UKX:FTSE": {
"eventSymbol": "UKX:FTSE",
"eventTime": 0,
"time": 1593001772000,
"timeNanoPart": 0,
"sequence": 115972,
"exchangeCode": "",
"price": 6194.63,
"size": 0,
"dayVolume": 0,
"dayTurnover": "NaN",
"tickDirection": "ZERO_UP",
"extendedTradingHours": false
}
},
"result": 6194.63
},
"result": 6194.63,
"statusCode": 200
}
```
48 changes: 48 additions & 0 deletions dxfeed-secondary/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@chainlink/dxfeed-secondary-adapter",
"version": "0.0.1",
"description": "Chainlink dxfeed secondary adapter.",
"keywords": [
"Chainlink",
"LINK",
"blockchain",
"oracle",
"dxfeed"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"repository": {
"url": "https://github.com/smartcontractkit/external-adapters-js",
"type": "git"
},
"license": "MIT",
"scripts": {
"prepublishOnly": "yarn build && yarn test:unit",
"setup": "yarn build",
"build": "tsc -b",
"lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix",
"test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'",
"test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'",
"test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'",
"server": "node -e 'require(\"./index.js\").server()'",
"server:dist": "node -e 'require(\"./dist/index.js\").server()'",
"start": "yarn server:dist"
},
"devDependencies": {
"@types/chai": "^4.2.11",
"@types/express": "^4.17.6",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.13",
"@typescript-eslint/eslint-plugin": "^3.9.0",
"@typescript-eslint/parser": "^3.9.0",
"ts-node": "^8.10.2",
"typescript": "^3.9.7"
},
"dependencies": {
"@chainlink/dxfeed-adapter": "0.0.2"
}
}
36 changes: 36 additions & 0 deletions dxfeed-secondary/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Requester, Validator, AdapterError } from '@chainlink/external-adapter'
import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types'
import { makeConfig, DEFAULT_ENDPOINT } from './config'
import { price } from './endpoint'

const inputParams = {
endpoint: false,
}

export const execute: ExecuteWithConfig<Config> = async (request, config) => {
const validator = new Validator(request, inputParams)
if (validator.error) throw validator.error

Requester.logConfig(config)

const jobRunID = validator.validated.id
const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT

switch (endpoint) {
case price.NAME: {
return await price.execute(request, config)
break
}
default: {
throw new AdapterError({
jobRunID,
message: `Endpoint ${endpoint} not supported.`,
statusCode: 400,
})
}
}
}

export const makeExecute: ExecuteFactory<Config> = (config) => {
return async (request) => execute(request, config || makeConfig())
}
6 changes: 6 additions & 0 deletions dxfeed-secondary/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Requester } from '@chainlink/external-adapter'
import { Config } from '@chainlink/types'

export const DEFAULT_ENDPOINT = 'price'

export const makeConfig = (prefix?: string): Config => Requester.getDefaultConfig(prefix)
1 change: 1 addition & 0 deletions dxfeed-secondary/src/endpoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as price from './price'
29 changes: 29 additions & 0 deletions dxfeed-secondary/src/endpoint/price.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Validator } from '@chainlink/external-adapter'
import { ExecuteWithConfig, Config } from '@chainlink/types'
import * as dxfeed from '@chainlink/dxfeed-adapter'

export const NAME = 'price'

const customParams = {
base: ['base', 'from', 'coin'],
}

const commonSymbols: { [key: string]: string } = {
N225: 'NKY.IND:TEI',
FTSE: 'UKX.IND:TEI',
TSLA: 'TSLA.US:TEI',
}

export const execute: ExecuteWithConfig<Config> = async (request, config) => {
const validator = new Validator(request, customParams)
if (validator.error) throw validator.error

let symbol = validator.validated.data.base.toUpperCase()
if (symbol in commonSymbols) {
symbol = commonSymbols[symbol]
}
request.data.base = symbol

const exec = dxfeed.makeExecute(config)
return exec(request)
}
7 changes: 7 additions & 0 deletions dxfeed-secondary/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { expose, util } from '@chainlink/ea-bootstrap'
import { makeExecute } from './adapter'
import { makeConfig } from './config'

const NAME = 'DXFEED_SECONDARY'

export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) }
66 changes: 66 additions & 0 deletions dxfeed-secondary/test/adapter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { assert } from 'chai'
import { Requester } from '@chainlink/external-adapter'
import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers'
import { AdapterRequest } from '@chainlink/types'
import { makeExecute } from '../src/adapter'

describe('execute', () => {
const jobID = '1'
const execute = makeExecute()

context('successful calls @integration', () => {
const requests = [
{ name: 'id not supplied', testData: { data: { base: 'FTSE' } } },
{ name: 'base', testData: { id: jobID, data: { base: 'FTSE' } } },
{ name: 'from', testData: { id: jobID, data: { from: 'FTSE' } } },
]

requests.forEach((req) => {
it(`${req.name}`, async () => {
const data = await execute(req.testData as AdapterRequest)
assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID)
assert.isAbove(data.result, 0)
assert.isAbove(data.data.result, 0)
})
})
})

context('validation error', () => {
const requests = [
{ name: 'empty body', testData: {} },
{ name: 'empty data', testData: { data: {} } },
{ name: 'base not supplied', testData: { id: jobID, data: {} } },
]

requests.forEach((req) => {
it(`${req.name}`, async () => {
try {
await execute(req.testData as AdapterRequest)
} catch (error) {
const errorResp = Requester.errored(jobID, error)
assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID)
}
})
})
})

context('error calls @integration', () => {
const requests = [
{
name: 'unknown base',
testData: { id: jobID, data: { base: 'not_real' } },
},
]

requests.forEach((req) => {
it(`${req.name}`, async () => {
try {
await execute(req.testData as AdapterRequest)
} catch (error) {
const errorResp = Requester.errored(jobID, error)
assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID)
}
})
})
})
})
10 changes: 10 additions & 0 deletions dxfeed-secondary/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"typeRoots": ["../node_modules/@types", "../typings", "./typings"]
},
"include": ["src/**/*"],
"exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"]
}
4 changes: 3 additions & 1 deletion dxfeed/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
module.exports = require('../.eslintrc.js')
module.exports = {
...require('../.eslintrc.ts.js'),
}
53 changes: 31 additions & 22 deletions dxfeed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,41 @@ This adapter supports the following environment variables:
- `base`, `from`, or `asset`: The symbol of the asset to query
- `endpoint`: Optional endpoint param

The `base` param handles the following symbol conversions:

```bash
N225 ➡️ 'NKY.IND:TEI'
FTSE ➡️ 'UKX.IND:TEI'
TSLA ➡️ 'TSLA:BFX'
TSLAX ➡️ 'TSLA.US:TEI'
```

## Output

```json
{
"jobRunID":"1",
"data":{
"status":"OK",
"Trade":{
"UKX:FTSE":{
"eventSymbol":"UKX:FTSE",
"eventTime":0,
"time":1593001772000,
"timeNanoPart":0,
"sequence":115972,
"exchangeCode":"",
"price":6194.63,
"size":0,
"dayVolume":0,
"dayTurnover":"NaN",
"tickDirection":"ZERO_UP",
"extendedTradingHours":false
}
},
"result":6194.63
"jobRunID": "1",
"data": {
"status": "OK",
"Trade": {
"UKX:FTSE": {
"eventSymbol": "UKX:FTSE",
"eventTime": 0,
"time": 1593001772000,
"timeNanoPart": 0,
"sequence": 115972,
"exchangeCode": "",
"price": 6194.63,
"size": 0,
"dayVolume": 0,
"dayTurnover": "NaN",
"tickDirection": "ZERO_UP",
"extendedTradingHours": false
}
},
"result":6194.63,
"statusCode":200
"result": 6194.63
},
"result": 6194.63,
"statusCode": 200
}
```
Loading

0 comments on commit 684f538

Please sign in to comment.