1 // FIXME: Microsoft Edge has issues returning errors for responses
2 // with a 401 status. We should add browser detection to only
3 // use the ponyfill for unsupported browsers.
4 const { fetch } = require('fetch-ponyfill')()
5 const errors = require('./errors')
6 const btoa = require('btoa')
8 const blacklistAttributes = [
18 const snakeize = (object) => {
19 for(let key in object) {
20 let value = object[key]
24 if (/^[A-Z]+$/.test(key)) {
28 if (/[A-Z]/.test(key)) {
29 newKey = key.replace(/([A-Z])/g, v => `_${v.toLowerCase()}`)
33 if (typeof value == 'object' && blacklistAttributes.indexOf(newKey) == -1) {
34 value = snakeize(value)
37 object[newKey] = value
43 const camelize = (object) => {
44 for (let key in object) {
45 let value = object[key]
49 newKey = key.replace(/([_][a-z])/g, v => v[1].toUpperCase())
53 if (typeof value == 'object' && blacklistAttributes.indexOf(key) == -1) {
54 value = camelize(value)
57 object[newKey] = value
65 * constructor - create a new Chain client object capable of interacting with
66 * the specified Chain Core.
68 * @param {String} baseUrl Chain Core URL.
69 * @param {String} token Chain Core client token for API access.
70 * @param {String} agent https.Agent used to provide TLS config.
73 constructor(baseUrl, token = '', agent) {
74 this.baseUrl = baseUrl
75 this.token = token || ''
80 * Submit a request to the specified Chain Core.
82 * @param {String} path
83 * @param {object} [body={}]
86 request(path, body = {}, skipSnakeize = false) {
91 // Convert camelcased request body field names to use snakecase for API
93 const snakeBody = skipSnakeize ? body : snakeize(body) // Ssssssssssss
98 'Accept': 'application/json',
99 // 'Content-Type': 'application/json',
101 // TODO(jeffomatic): The Fetch API has inconsistent behavior between
102 // browser implementations and polyfills.
104 // - For Edge: we can't use the browser's fetch API because it doesn't
105 // always returns a WWW-Authenticate challenge to 401s.
106 // - For Safari/Chrome: using fetch-ponyfill (the polyfill) causes
107 // console warnings if the user agent string is provided.
109 // For now, let's not send the UA string.
110 //'User-Agent': 'chain-sdk-js/0.0'
112 body: JSON.stringify(snakeBody)
116 req.headers['Authorization'] = `Basic ${btoa(this.token)}`
120 req.agent = this.agent
123 return fetch(this.baseUrl + path, req).catch((err) => {
126 'Fetch error: ' + err.toString(),
130 if (resp.status == 204) {
131 return { status: 204 }
134 return resp.json().catch(() => {
137 'Could not parse JSON response',
138 {response: resp, status: resp.status}
141 if (resp.status / 100 == 2) {
145 // Everything else is a status error.
147 if (resp.status == 401) {
148 errType = errors.types.UNAUTHORIZED
149 } else if (resp.status == 404) {
150 errType = errors.types.NOT_FOUND
151 } else if (resp.status / 100 == 4) {
152 errType = errors.types.BAD_REQUEST
154 errType = errors.types.SERVER
159 errors.formatErrMsg(body, null),
168 if(body.status === 'fail'){
171 // After processing the response, convert snakecased field names to
172 // camelcase to match language conventions.
173 return skipSnakeize? body : camelize(body)
179 Connection.snakeize = snakeize
180 Connection.camelize = camelize
182 module.exports = Connection