• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperwallet / node-sdk / 201

pending completion
201

push

travis-ci

Willian Mews
merge conflicts

428 of 448 branches covered (95.54%)

631 of 635 relevant lines covered (99.37%)

13.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.55
/src/utils/ApiClient.js
1
import request from "superagent";
1✔
2
import packageJson from "../../package.json";
1✔
3
import Encryption from "./Encryption";
1✔
4

5
/**
6
 * The callback interface for api calls
7
 *
26!
8
 * @typedef {function} api-callback
9
 * @param {Object[]} [errors] - In case of an error an array with error objects otherwise undefined
10
 * @param {string} [errors[].fieldName] - The field name (if error is caused by a particular field)
11
 * @param {string} errors[].message - The error message
12
 * @param {string} errors[].code - The error code
13
 * @param {Object} data - The rest response body
14
 * @param {Object} res - The raw superagent response object
15
 */
16

17
/**
18
 * The Hyperwallet API Client
19
 */
20
export default class ApiClient {
21
    /**
9✔
22
     * Create a instance of the API client
23
     *
351!
24
     * @param {string} username - The API username
25
     * @param {string} password - The API password
26
     * @param {string} server - The API server to connect to
27
     * @param {string} encryptionData - The API encryption data
28
     */
29
    constructor(username, password, server, encryptionData) {
351✔
30
        /**
31
         * The API username
32
         *
33
         * @type {string}
34
         * @protected
35
         */
36
        this.username = username;
351✔
37

38
        /**
39
         * The API password
40
         *
41
         * @type {string}
42
         * @protected
43
         */
44
        this.password = password;
351✔
45

46
        /**
47
         * The API server to connect to
48
         * @type {string}
49
         * @protected
50
         */
51
        this.server = server;
351✔
52

53
        /**
54
         * The Node SDK Version number
55
         *
56
         * @type {string}
57
         * @protected
58
         */
59
        this.version = packageJson.version;
351✔
60

61
        /**
62
         * The flag shows if encryption is enabled
63
         *
64
         * @type {boolean}
65
         * @protected
66
         */
67
        this.isEncrypted = false;
351✔
68

69
        if (encryptionData && encryptionData.clientPrivateKeySetPath && encryptionData.hyperwalletKeySetPath) {
351✔
70
            this.isEncrypted = true;
12✔
71
            this.clientPrivateKeySetPath = encryptionData.clientPrivateKeySetPath;
12✔
72
            this.hyperwalletKeySetPath = encryptionData.hyperwalletKeySetPath;
12✔
73
            this.encryption = new Encryption(this.clientPrivateKeySetPath, this.hyperwalletKeySetPath);
12✔
74
        }
75
    }
76

77
    /**
78
     * Do a POST call to the Hyperwallet API server
79
     *
80
     * @param {string} partialUrl - The api endpoint to call (gets prefixed by `server` and `/rest/v3/`)
81
     * @param {Object} data - The data to send to the server
82
     * @param {Object} params - Query parameters to send in this call
83
     * @param {api-callback} callback - The callback for this call
84
     */
85
    doPost(partialUrl, data, params, callback) {
10✔
86
        let contentType = "application/json";
10✔
87
        let accept = "application/json";
10✔
88
        let requestDataPromise = new Promise((resolve) => resolve(data));
10✔
89
        if (this.isEncrypted) {
10✔
90
            contentType = "application/jose+json";
4✔
91
            accept = "application/jose+json";
4✔
92
            this.createJoseJsonParser();
4✔
93
            requestDataPromise = this.encryption.encrypt(data);
4✔
94
        }
95
        requestDataPromise.then((requestData) => {
10✔
96
            request
9✔
97
                .post(`${this.server}/rest/v3/${partialUrl}`)
98
                .auth(this.username, this.password)
99
                .set("User-Agent", `Hyperwallet Node SDK v${this.version}`)
100
                .type(contentType)
101
                .accept(accept)
102
                .query(params)
103
                .send(requestData)
104
                .end(this.wrapCallback("POST", callback));
105
        }).catch(() => callback("Failed to encrypt body for POST request", undefined, undefined));
1✔
106
    }
107

108
    /**
109
     * Do a PUT call to the Hyperwallet API server to upload documents
110
     *
111
     * @param {string} partialUrl - The api endpoint to call (gets prefixed by `server` and `/rest/v3/`)
112
     * @param {Object} data - The data to send to the server
113
     * @param {api-callback} callback - The callback for this call
114
     */
115
    doPutMultipart(partialUrl, data, callback) {
1✔
116
        let contentType = "multipart/form-data";
1✔
117
        let accept = "application/json";
1✔
118
        /* eslint-disable no-unused-vars */
119
        const keys = Object.keys(data);
1✔
120
        /* eslint-enable no-unused-vars */
121

122
        let requestDataPromise = new Promise((resolve) => resolve(data));
1✔
123
        if (this.isEncrypted) {
1✔
124
            contentType = "multipart/form-data";
×
125
            accept = "application/jose+json";
×
126
            this.createJoseJsonParser();
×
127
            requestDataPromise = this.encryption.encrypt(data);
×
128
        }
129
        requestDataPromise.then(() => {
1✔
130
            const req = request
1✔
131
                .put(`${this.server}/rest/v3/${partialUrl}`)
132
                .auth(this.username, this.password)
133
                .set("User-Agent", `Hyperwallet Node SDK v${this.version}`)
134
                .type(contentType)
135
                .accept(accept);
136
            keys.forEach(key => {
1✔
137
                if (key === "data") {
2✔
138
                    req.field(key, JSON.stringify(data[key]));
1✔
139
                } else {
140
                    req.attach(key, data[key]);
1✔
141
                }
142
            });
143
            req.end(this.wrapCallback("PUT", callback));
1✔
144
        }).catch((err) => callback(err, undefined, undefined)).then((err, body, res) => callback(err, body, res));
1✔
145
    }
146

147
    /**
148
     * Do a PUT call to the Hyperwallet API server
149
     *
150
     * @param {string} partialUrl - The api endpoint to call (gets prefixed by server and /rest/v3/)
151
     * @param {Object} data - The data to send to the server
152
     * @param {Object} params - Query parameters to send in this call
153
     * @param {api-callback} callback - The callback for this call
154
     */
1!
155
    doPut(partialUrl, data, params, callback) {
11✔
156
        let contentType = "application/json";
11✔
157
        let accept = "application/json";
11✔
158
        let requestDataPromise = new Promise((resolve) => resolve(data));
11✔
159
        if (this.isEncrypted) {
11✔
160
            contentType = "application/jose+json";
5✔
161
            accept = "application/jose+json";
5✔
162
            this.createJoseJsonParser();
5✔
163
            requestDataPromise = this.encryption.encrypt(data);
5✔
164
        }
165
        requestDataPromise.then((requestData) => {
11✔
166
            request
10✔
167
                .put(`${this.server}/rest/v3/${partialUrl}`)
168
                .auth(this.username, this.password)
169
                .set("User-Agent", `Hyperwallet Node SDK v${this.version}`)
170
                .type(contentType)
171
                .accept(accept)
172
                .query(params)
173
                .send(requestData)
174
                .end(this.wrapCallback("PUT", callback));
175
        }).catch(() => callback("Failed to encrypt body for PUT request", undefined, undefined));
1✔
176
    }
177

178
    /**
179
     * Do a GET call to the Hyperwallet API server
180
     *
181
     * @param {string} partialUrl - The api endpoint to call (gets prefixed by `server` and `/rest/v3/`)
182
     * @param {Object} params - Query parameters to send in this call
183
     * @param {api-callback} callback - The callback for this call
184
     */
185
    doGet(partialUrl, params, callback) {
186
        let contentType = "application/json";
9✔
187
        let accept = "application/json";
9✔
188
        if (this.isEncrypted) {
9✔
189
            contentType = "application/jose+json";
2✔
190
            accept = "application/jose+json";
2✔
191
            this.createJoseJsonParser();
2✔
192
        }
193
        request
9✔
194
            .get(`${this.server}/rest/v3/${partialUrl}`)
195
            .auth(this.username, this.password)
196
            .set("User-Agent", `Hyperwallet Node SDK v${this.version}`)
11✔
197
            .type(contentType)
198
            .accept(accept)
199
            .query(params)
200
            .end(this.wrapCallback("GET", callback));
201
    }
202

203
    /**
204
     * Wrap a callback to process possible API and network errors
205
     *
206
     * @param {string} httpMethod - The http method that is currently processing
207
     * @param {api-callback} callback - The final callback
208
     * @returns {function(err: Object, res: Object)} - The super agent callback
209
     *
210
     * @private
211
     */
212
    wrapCallback(httpMethod, callback = () => null) {
38✔
213
        return (err, res) => {
38✔
214
            const expectedContentType = (this.isEncrypted) ? "application/jose+json" : "application/json";
36✔
215
            const invalidContentType = res && res.header && res.status !== 204 && res.header["content-type"].indexOf(expectedContentType) === -1;
36✔
216
            if (invalidContentType) {
36✔
217
                callback([{
1✔
218
                    message: "Invalid Content-Type specified in Response Header",
219
                }], res ? res.body : undefined, res);
220
                return;
1✔
221
            }
222
            if (this.isEncrypted) {
35✔
223
                this.processEncryptedResponse(httpMethod, err, res.body, callback);
10✔
224
            } else {
225
                this.processNonEncryptedResponse(err, res, callback);
25✔
226
            }
227
        };
228
    }
229

230
    /**
231
     * Process non encrypted response from server
232
     *
233
     * @param {Object} err - Error object
234
     * @param {Object} res - Response object
235
     * @param {api-callback} callback - The final callback
236
     *
237
     * @private
238
     */
239
    processNonEncryptedResponse(err, res, callback) {
240
        if (!err) {
26✔
241
            callback(undefined, res.body, res);
14✔
242
            return;
14✔
243
        }
244

245
        let errors = [
12✔
246
            {
247
                message: `Could not communicate with ${this.server}`,
248
                code: "COMMUNICATION_ERROR",
249
            },
250
        ];
36✔
251
        if (res && res.body && res.body.errors) {
12✔
252
            errors = res.body.errors;
6✔
253
        }
254
        callback(errors, res ? res.body : undefined, res);
12✔
255
    }
1!
256

257
    /**
258
     * Process encrypted response from server
35✔
259
     *
260
     * @param {string} httpMethod - The http method that is currently processing
261
     * @param {Object} err - Error object
262
     * @param {Object} res - Response object
263
     * @param {api-callback} callback - The final callback
264
     *
265
     * @private
266
     */
267
    processEncryptedResponse(httpMethod, err, res, callback) {
10✔
268
        if (!res) {
10✔
269
            callback("Try to decrypt empty response body", undefined, undefined);
1✔
270
        }
271
        this.encryption.decrypt(res)
10✔
272
            .then((decryptedData) => {
273
                const responseBody = JSON.parse(decryptedData.payload.toString());
8✔
274
                if (responseBody.errors) {
8✔
275
                    const responseWithErrors = {};
1✔
276
                    responseWithErrors.body = responseBody;
1✔
277
                    this.processNonEncryptedResponse(responseBody, responseWithErrors, callback);
1✔
278
                } else {
279
                    callback(undefined, responseBody, decryptedData);
7✔
280
                }
281
            })
282
            .catch(() => callback(`Failed to decrypt response for ${httpMethod} request`, res, res));
2✔
283
    }
284

285
    /**
286
     * Creates response body parser for application/jose+json content-type
287
     *
288
     * @private
38✔
289
     */
290
    createJoseJsonParser() {
291
        request.parse["application/jose+json"] = (res, callback) => {
11✔
292
            let data = "";
9✔
293
            res.on("data", (chunk) => {
9✔
294
                data += chunk;
8✔
295
            });
296
            res.on("end", () => {
9✔
297
                callback(null, data);
9✔
298
            });
299
        };
300
    }
301
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc