-
Notifications
You must be signed in to change notification settings - Fork 34
/
JsonClient.js
148 lines (118 loc) · 4.42 KB
/
JsonClient.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2012 Mark Cavage, Inc. All rights reserved.
'use strict';
var util = require('util');
var assert = require('assert-plus');
var restifyErrors = require('restify-errors');
var safeStringify = require('fast-safe-stringify');
var makeErrFromCode = restifyErrors.makeErrFromCode;
var RestError = restifyErrors.RestError;
var StringClient = require('./StringClient');
// --- API
function JsonClient(options) {
assert.object(options, 'options');
assert.optionalBool(options.safeStringify, 'options.safeStringify');
options.accept = options.accept || 'application/json';
options.name = options.name || 'JsonClient';
options.contentType = options.contentType || 'application/json';
StringClient.call(this, options);
this._super = StringClient.prototype;
this._safeStringify = options.safeStringify || false;
}
util.inherits(JsonClient, StringClient);
module.exports = JsonClient;
JsonClient.prototype.write = function write(options, body, callback) {
var self = this;
var bodyOrDefault = (body !== null ? body : {});
assert.ok(
typeof bodyOrDefault === 'string' || typeof bodyOrDefault === 'object',
'body'
);
var resBody;
// safely stringify body if client was configured thusly
if (self._safeStringify) {
resBody = safeStringify(bodyOrDefault);
} else {
resBody = JSON.stringify(bodyOrDefault);
}
return (this._super.write.call(this, options, resBody, callback));
};
/**
* parse body out of the response object.
* @private
* @method parse
* @param {Error} [err] low level http error if applicable
* @param {Object} req the request object
* @param {Object} [res] the response object
* @param {Function} callback a callback fn
* @returns {Object} parsed JSON
*/
JsonClient.prototype.parse = function parse(err, req, res, callback) {
assert.optionalObject(err, 'err');
assert.object(req, 'req');
assert.optionalObject(res, 'res');
assert.func(callback, 'callback');
var self = this;
var log = self.log;
function parseResponse(superErr, data) {
var obj;
var resErr = superErr;
var parseErr;
try {
if (data) {
obj = JSON.parse(data);
}
} catch (e) {
// bad data being returned that cannot be parsed should be surfaced
// to the caller. http errors should take precedence, but it's
// possible to receive malformed data regardless of a status code.
parseErr = e;
log.trace(parseErr, 'Invalid JSON in response');
}
// empty string and undefined are not valid JSON, fall back on the
// original response body (string) or empty POJO in worst case.
if (obj === '' || typeof obj === 'undefined') {
obj = (res && res.body) || {};
}
// http errors take precedence over JSON.parse errors
if (res && res.statusCode >= 400) {
// Upcast error to a RestError (if we can)
// Be nice and handle errors like
// { error: { code: '', message: '' } }
// in addition to { code: '', message: '' }.
if (obj.code || (obj.error && obj.error.code)) {
var _c = obj.code ||
(obj.error ? obj.error.code : '') ||
'';
var _m = obj.message ||
(obj.error ? obj.error.message : '') ||
'';
resErr = new RestError({
restCode: _c,
statusCode: res.statusCode
}, '%s', _m);
resErr.name = resErr.restCode;
if (!/Error$/.test(resErr.name)) {
resErr.name += 'Error';
}
} else if (!resErr) {
resErr = makeErrFromCode(res.statusCode,
obj.message || '', data);
}
}
// if no http error but we had a json parse error, return the json
// parse err as the top level error
if (!resErr && parseErr) {
resErr = new RestError({
cause: parseErr,
info: {
body: data
}
}, 'Invalid JSON in response');
}
if (resErr) {
resErr.body = obj;
}
return callback((resErr || null), obj);
}
return (self._super.parse.call(self, err, req, res, parseResponse));
};