blob: a10ca396b90de4ff54d79790ba055470cb1b0be4 [file] [log] [blame]
"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;