mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
Created refactored route-utils2.js library
This commit is contained in:
@@ -7,14 +7,16 @@
|
||||
const express = require("express")
|
||||
const router = express.Router()
|
||||
|
||||
const axios = require("axios")
|
||||
const wlogger = require("../../../util/winston-logging")
|
||||
|
||||
const RouteUtils = require("../route-utils2")
|
||||
const routeUtils = new RouteUtils()
|
||||
|
||||
// Used to convert error messages to strings, to safely pass to users.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const axios = require("axios")
|
||||
const routeUtils = require("../route-utils")
|
||||
const wlogger = require("../../../util/winston-logging")
|
||||
|
||||
const BCHJS = require("@chris.troutner/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
@@ -26,6 +28,7 @@ class Blockchain {
|
||||
|
||||
this.bchjs = bchjs
|
||||
this.axios = axios
|
||||
this.routeUtils = routeUtils
|
||||
|
||||
this.router = router
|
||||
this.router.get("/", this.root)
|
||||
@@ -40,7 +43,7 @@ class Blockchain {
|
||||
// DRY error handler.
|
||||
errorHandler(err, res) {
|
||||
// Attempt to decode the error message.
|
||||
const { msg, status } = routeUtils.decodeError(err)
|
||||
const { msg, status } = this.routeUtils.decodeError(err)
|
||||
if (msg) {
|
||||
res.status(status)
|
||||
return res.json({ error: msg })
|
||||
@@ -65,7 +68,7 @@ class Blockchain {
|
||||
async getBestBlockHash(req, res, next) {
|
||||
try {
|
||||
// Axios options
|
||||
const options = routeUtils.getAxiosOptions()
|
||||
const options = this.routeUtils.getAxiosOptions()
|
||||
options.data.id = "getbestblockhash"
|
||||
options.data.method = "getbestblockhash"
|
||||
options.data.params = []
|
||||
@@ -110,7 +113,7 @@ class Blockchain {
|
||||
async getBlockchainInfo(req, res, next) {
|
||||
try {
|
||||
// Axios options
|
||||
const options = routeUtils.getAxiosOptions()
|
||||
const options = this.routeUtils.getAxiosOptions()
|
||||
options.data.id = "getblockchaininfo"
|
||||
options.data.method = "getblockchaininfo"
|
||||
options.data.params = []
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
A private library of utility functions used by several different routes.
|
||||
*/
|
||||
|
||||
"use strict"
|
||||
|
||||
const axios = require("axios")
|
||||
const wlogger = require("../../util/winston-logging")
|
||||
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
const BCHJS = require("@chris.troutner/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
let _this
|
||||
|
||||
class RouteUtils {
|
||||
constructor() {
|
||||
_this = this
|
||||
|
||||
this.bchjs = bchjs
|
||||
this.axios = axios
|
||||
}
|
||||
|
||||
// This function expects the Request Express.js object and an array as input.
|
||||
// The array is then validated against freemium and pro-tier rate limiting
|
||||
// requirements. A boolean is returned to indicate if the array size if valid
|
||||
// or not.
|
||||
validateArraySize(req, array) {
|
||||
const FREEMIUM_INPUT_SIZE = 20
|
||||
const PRO_INPUT_SIZE = 20
|
||||
|
||||
if (req.locals && req.locals.proLimit) {
|
||||
if (array.length <= PRO_INPUT_SIZE) return true
|
||||
} else if (array.length <= FREEMIUM_INPUT_SIZE) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Axios options used when calling axios.post() to talk with a full node.
|
||||
getAxiosOptions() {
|
||||
return {
|
||||
method: "post",
|
||||
baseURL: process.env.RPC_BASEURL,
|
||||
timeout: 15000,
|
||||
auth: {
|
||||
username: process.env.RPC_USERNAME,
|
||||
password: process.env.RPC_PASSWORD
|
||||
},
|
||||
data: {
|
||||
jsonrpc: "1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if user-provided cash address matches the correct network,
|
||||
// mainnet or testnet. If NETWORK env var is not defined, it returns false.
|
||||
// This prevent a common user-error issue that is easy to make: passing a
|
||||
// testnet address into rest.bitcoin.com or passing a mainnet address into
|
||||
// trest.bitcoin.com.
|
||||
validateNetwork(addr) {
|
||||
try {
|
||||
const network = process.env.NETWORK
|
||||
|
||||
// Return false if NETWORK is not defined.
|
||||
if (!network || network === "") {
|
||||
console.log(`Warning: NETWORK environment variable is not defined!`)
|
||||
return false
|
||||
}
|
||||
|
||||
// Convert the user-provided address to a cashaddress, for easy detection
|
||||
// of the intended network.
|
||||
const cashAddr = this.bchjs.Address.toCashAddress(addr)
|
||||
|
||||
// Return true if the network and address both match testnet
|
||||
const addrIsTest = this.bchjs.Address.isTestnetAddress(cashAddr)
|
||||
if (network === "testnet" && addrIsTest) return true
|
||||
|
||||
// Return true if the network and address both match mainnet
|
||||
const addrIsMain = this.bchjs.Address.isMainnetAddress(cashAddr)
|
||||
if (network === "mainnet" && addrIsMain) return true
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
logger.error(`Error in validateNetwork()`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Error messages returned by a full node can be burried pretty deep inside the
|
||||
// error object returned by Axios. This function attempts to extract and interpret
|
||||
// error messages.
|
||||
// Returns an object. If successful, obj.msg is a string.
|
||||
// If there is a failure, obj.msg is false.
|
||||
decodeError(err) {
|
||||
try {
|
||||
// Attempt to extract the full node error message.
|
||||
if (
|
||||
err.response &&
|
||||
err.response.data &&
|
||||
err.response.data.error &&
|
||||
err.response.data.error.message
|
||||
)
|
||||
return { msg: err.response.data.error.message, status: 400 }
|
||||
|
||||
// Attempt to extract the Insight error message
|
||||
if (err.response && err.response.data)
|
||||
return { msg: err.response.data, status: err.response.status }
|
||||
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
// console.log(`err: `, err)
|
||||
|
||||
// Attempt to detect a network connection error.
|
||||
if (err.message && err.message.indexOf("ENOTFOUND") > -1) {
|
||||
return {
|
||||
msg:
|
||||
"Network error: Could not communicate with full node or other external service.",
|
||||
status: 503
|
||||
}
|
||||
}
|
||||
|
||||
// Different kind of network error
|
||||
if (err.message && err.message.indexOf("ENETUNREACH") > -1) {
|
||||
return {
|
||||
msg:
|
||||
"Network error: Could not communicate with full node or other external service.",
|
||||
status: 503
|
||||
}
|
||||
}
|
||||
|
||||
// Different kind of network error
|
||||
if (err.message && err.message.indexOf("EAI_AGAIN") > -1) {
|
||||
return {
|
||||
msg:
|
||||
"Network error: Could not communicate with full node or other external service.",
|
||||
status: 503
|
||||
}
|
||||
}
|
||||
|
||||
// Axios timeout (aborted) error, or service is down (connection refused).
|
||||
if (
|
||||
err.code &&
|
||||
(err.code === "ECONNABORTED" || err.code === "ECONNREFUSED")
|
||||
) {
|
||||
return {
|
||||
msg:
|
||||
"Network error: Could not communicate with full node or other external service.",
|
||||
status: 503
|
||||
}
|
||||
}
|
||||
|
||||
return { msg: false, status: 500 }
|
||||
} catch (err) {
|
||||
wlogger.error(`unhandled error in route-utils.js/decodeError(): `, err)
|
||||
return { msg: false, status: 500 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RouteUtils
|
||||
@@ -28,7 +28,7 @@ const mockData = require("./mocks/blockchain-mock")
|
||||
|
||||
let originalEnvVars // Used during transition from integration to unit tests.
|
||||
|
||||
describe("#BlockchainRouter", () => {
|
||||
describe("#BlockchainRouter2", () => {
|
||||
let req, res
|
||||
let sandbox
|
||||
|
||||
|
||||
Reference in New Issue
Block a user