0

I am trying to run my chainlink external adapter with a job in my node. However, every time I get an error at "fetch bridge" task: error making http request: Post "http://localhost:8080": dial tcp 127.0.0.1:8080: connect: connection refused

enter image description here

My external adapter is running on localhost:8080 and I can successfully perform curl commands like curl -X POST -H "content-type:application/json" "http://localhost:8080/" --data '{"id": 1, "data": { "playerId": 90026231 }}'

Here is my Bridge configuration:

enter image description here

And here my JobSpecs:

type = "directrequest"
schemaVersion = 1
name = "Soccer-Data-EA"
contractAddress = "0xF59646024204a733E1E4f66B303c9eF4f68324cC"
maxTaskDuration = "0s"
observationSource = """
    decode_log   [type=ethabidecodelog
                  abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"
                  data="$(jobRun.logData)"
                  topics="$(jobRun.logTopics)"]

    decode_cbor  [type=cborparse data="$(decode_log.data)"]
    fetch        [type=bridge name="sports-data" requestData="{\\"id\\": $(jobSpec.externalJobID), \\"data\\": { \\"playerId\\": $(decode_cbor.playerId)}}"]
    parse        [type=jsonparse path="data,0,Games" data="$(fetch)"]
    encode_data  [type=ethabiencode abi="(uint256 value)" data="{ \\"value\\": $(parse) }"]
    encode_tx    [type=ethabiencode
                  abi="fulfillOracleRequest(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data)"
                  data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}"
                 ]
    submit_tx    [type=ethtx to="0xF59646024204a733E1E4f66B303c9eF4f68324cC" data="$(encode_tx)"]

    decode_log -> decode_cbor -> fetch -> parse -> encode_data -> encode_tx -> submit_tx
"""
externalJobID = "d5a37166-2c83-461b-9912-97beebe4d213"

Here my index.js i.e. my external adapter:

const { Requester, Validator } = require('@chainlink/external-adapter')
require('dotenv').config()

// Define custom error scenarios for the API.
// Return true for the adapter to retry.
const customError = (data) => {
  if (data.Response === 'Error') return true
  return false
}

// Define custom parameters to be used by the adapter.
// Extra parameters can be stated in the extra object,
// with a Boolean value indicating whether or not they
// should be required.
const customParams = {
  playerId: ['playerId'],
  endpoint: false
}

const createRequest = (input, callback) => {
  // The Validator helps you validate the Chainlink request data
  const validator = new Validator(callback, input, customParams)
  const jobRunID = validator.validated.id

  const apiKey = process.env.API_KEY
  const pId = validator.validated.data.playerId


  const url = `https://api.sportsdata.io/v3/soccer/stats/json/PlayerSeasonStatsByPlayer/1/${pId}?key=${apiKey}`


  const params = {
    pId
  }

  // This is where you would add method and headers
  // you can add method like GET or POST and add it to the config
  // The default is GET requests
  // method = 'get' 
  // headers = 'headers.....'
  const config = {
    url,
    params
  }

  // The Requester allows API calls be retry in case of timeout
  // or connection failure
  Requester.request(config, customError)
    .then(response => {
      // It's common practice to store the desired value at the top-level
      // result key. This allows different adapters to be compatible with
      // one another.
      //response.data.result = Requester.validateResultNumber(response.data, [tsyms])

      
      callback(response.status, Requester.success(jobRunID, response))
    })
    .catch(error => {
      callback(500, Requester.errored(jobRunID, error))
    })
}

// This is a wrapper to allow the function to work with
// GCP Functions
exports.gcpservice = (req, res) => {
  createRequest(req.body, (statusCode, data) => {
    res.status(statusCode).send(data)
  })
}

// This is a wrapper to allow the function to work with
// AWS Lambda
exports.handler = (event, context, callback) => {
  createRequest(event, (statusCode, data) => {
    callback(null, data)
  })
}

// This is a wrapper to allow the function to work with
// newer AWS Lambda implementations
exports.handlerv2 = (event, context, callback) => {
  createRequest(JSON.parse(event.body), (statusCode, data) => {
    callback(null, {
      statusCode: statusCode,
      body: JSON.stringify(data),
      isBase64Encoded: false
    })
  })
}

// This allows the function to be exported for testing
// or for running in express
module.exports.createRequest = createRequest

The index.js from above gets rendered in app.js see code below (same directory as index.js):

const createRequest = require('./index').createRequest

const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const port = process.env.EA_PORT || 8080

app.use(bodyParser.json())

app.post('/', (req, res) => {
  console.log('POST Data: ', req.body)
  createRequest(req.body, (status, result) => {
    console.log('Result: ', result)
    res.status(status).json(result)
  })
})

app.listen(port, () => console.log(`Listening on port ${port}!`))

Does somebody see what's wrong here with my code or configuration?

REMARK:

I saw that in app.js the directory in const createRequest = require('./index').createRequest the "./index" is wierd, because index.js and app.js are in the same directory. However, I can still perform curl like mentioned before and also when I put index.js in a index folder it doesn't change nothing.

1

1 Answer 1

0

There's nothing wrong with your configuration except for the Bridge URL you set. Since chainlink-node and external-adapter are running in different docker containers, the API call made from chainlink-node to localhost, is executed internally to the Docker container, so the connection is refused.

You can simulate the same behaviour doing the steps below:

  1. run docker container ls to copy the container id of the chainlink-node
  2. run docker exec -it {container_id} sh
  3. run curl http://localhost:8000

You will see the error curl: (7) Failed to connect to localhost port 8000: Connection refused.

To fix your problem, without creating a superstructure, you can get the internal IP address of your external-adapter and use it as Bridge URL. Follow these steps to get the internal IP address correctly:

  1. run docker container ls to copy the container id of the external-adapter
  2. run docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {container_id}

You will get the IP address to be used as Bridge URL.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.