| "use strict"; |
| |
| var request = require('request'); |
| var errors = require('./errors'); |
| var utils = require("../utils"); |
| |
| function ajax(options, adapterCallback) { |
| |
| var requestCompleted = false; |
| var callback = utils.getArguments(function (args) { |
| if (requestCompleted) { |
| return; |
| } |
| adapterCallback.apply(this, args); |
| requestCompleted = true; |
| }); |
| |
| if (typeof options === "function") { |
| callback = options; |
| options = {}; |
| } |
| |
| options = utils.clone(options); |
| |
| var defaultOptions = { |
| method : "GET", |
| headers: {}, |
| json: true, |
| processData: true, |
| timeout: 10000, |
| cache: false |
| }; |
| |
| options = utils.extend(true, defaultOptions, options); |
| |
| |
| function onSuccess(obj, resp, cb) { |
| if (!options.binary && !options.json && options.processData && |
| typeof obj !== 'string') { |
| obj = JSON.stringify(obj); |
| } else if (!options.binary && options.json && typeof obj === 'string') { |
| try { |
| obj = JSON.parse(obj); |
| } catch (e) { |
| // Probably a malformed JSON from server |
| return cb(e); |
| } |
| } |
| if (Array.isArray(obj)) { |
| obj = obj.map(function (v) { |
| var obj; |
| if (v.ok) { |
| return v; |
| } else if (v.error && v.error === 'conflict') { |
| obj = errors.REV_CONFLICT; |
| obj.id = v.id; |
| return obj; |
| } else if (v.error && v.error === 'forbidden') { |
| obj = errors.FORBIDDEN; |
| obj.id = v.id; |
| obj.reason = v.reason; |
| return obj; |
| } else if (v.missing) { |
| obj = errors.MISSING_DOC; |
| obj.missing = v.missing; |
| return obj; |
| } else { |
| return v; |
| } |
| }); |
| } |
| cb(null, obj, resp); |
| } |
| |
| function onError(err, cb) { |
| var errParsed, errObj, errType, key; |
| if (err.code && err.status) { |
| var err2 = new Error(err.message || err.code); |
| err2.status = err.status; |
| return cb(err2); |
| } |
| try { |
| errParsed = JSON.parse(err.responseText); |
| //would prefer not to have a try/catch clause |
| for (key in errors) { |
| if (errors.hasOwnProperty(key) && |
| errors[key].name === errParsed.error) { |
| errType = errors[key]; |
| break; |
| } |
| } |
| if (!errType) { |
| errType = errors.UNKNOWN_ERROR; |
| if (err.status) { |
| errType.status = err.status; |
| } |
| if (err.statusText) { |
| err.name = err.statusText; |
| } |
| } |
| errObj = errors.error(errType, errParsed.reason); |
| } catch (e) { |
| for (var key in errors) { |
| if (errors.hasOwnProperty(key) && errors[key].status === err.status) { |
| errType = errors[key]; |
| break; |
| } |
| } |
| if (!errType) { |
| errType = errors.UNKNOWN_ERROR; |
| if (err.status) { |
| errType.status = err.status; |
| } |
| if (err.statusText) { |
| err.name = err.statusText; |
| } |
| } |
| errObj = errors.error(errType); |
| } |
| cb(errObj); |
| } |
| |
| |
| if (options.json) { |
| if (!options.binary) { |
| options.headers.Accept = 'application/json'; |
| } |
| options.headers['Content-Type'] = options.headers['Content-Type'] || |
| 'application/json'; |
| } |
| |
| if (options.binary) { |
| options.encoding = null; |
| options.json = false; |
| } |
| |
| if (!options.processData) { |
| options.json = false; |
| } |
| |
| return request(options, function (err, response, body) { |
| if (err) { |
| err.status = response ? response.statusCode : 400; |
| return onError(err, callback); |
| } |
| var error; |
| var content_type = response.headers['content-type']; |
| var data = (body || new Buffer('')); |
| |
| // CouchDB doesn't always return the right content-type for JSON data, so |
| // we check for ^{ and }$ (ignoring leading/trailing whitespace) |
| if (!options.binary && (options.json || !options.processData) && |
| typeof data !== 'object' && |
| (/json/.test(content_type) || |
| (/^[\s]*\{/.test(data) && /\}[\s]*$/.test(data)))) { |
| data = JSON.parse(data); |
| } |
| |
| if (response.statusCode >= 200 && response.statusCode < 300) { |
| onSuccess(data, response, callback); |
| } |
| else { |
| if (options.binary) { |
| data = JSON.parse(data.toString()); |
| } |
| if (data.reason === 'missing') { |
| error = errors.MISSING_DOC; |
| } else if (data.reason === 'no_db_file') { |
| error = errors.error(errors.DB_MISSING, data.reason); |
| } else if (data.error === 'conflict') { |
| error = errors.REV_CONFLICT; |
| } else { |
| error = errors.error(errors.UNKNOWN_ERROR, data.reason, data.error); |
| } |
| error.status = response.statusCode; |
| callback(error); |
| } |
| }); |
| } |
| |
| module.exports = ajax; |