openclient
Version:
An opinionated client for RESTful APIs (particularly OpenStack's).
204 lines (157 loc) • 6.14 kB
JavaScript
var http = require('http'),
https = require('https');
var base = require("../../client/base"),
error = require("../../client/error");
var ObjectManager = base.Manager.extend({
namespace: "objects",
init: function (client) {
this._super(client);
this.method_map.create = "put";
},
_safe_id: function (id) {
return encodeURIComponent(id);
},
// Method to handle binary file transfers since the XMLHttpRequest
// library currently tries to transfer everything as utf8.
_doBinaryRequest: function (params, token, callback) {
var client = this.client;
if (!params.pipe) {
client.log("error", "_doBinaryRequest called without a response pipe.");
callback("Unable to download object.", null, {status: 500});
}
var matches = params.url.match(/^(https?)\:\/\/([^\/?#]+)(?:[\/?#]|$)/i),
host_and_port = matches[2].split(':'),
request_module, request_default_port;
if (matches[1] === 'https') {
request_module = https;
request_default_port = 443;
}
else {
request_module = http;
request_default_port = 80;
}
var options = {
hostname: host_and_port[0],
port: host_and_port[1] || request_default_port,
path: '/' + params.url.substring(matches[0].length),
method: params.method,
headers: {
'X-Auth-Token': token
}
};
var response_data = "";
var request = request_module.request(options, function (response) {
if (response.statusCode === 0 || response.statusCode >= 400) callback(response, null, {status: response.statusCode});
if (params.pipe.setHeader) {
var response_headers = ["Content-Length", "Content-Type", "Content-Encoding", "Last-Modified", "ETag"];
response_headers.forEach(function (header) {
if (response.headers[header.toLowerCase()]) {
params.pipe.setHeader(header, response.headers[header.toLowerCase()]);
}
});
params.pipe.setHeader("Content-Disposition", 'attachment; filename="' + params.id + '"');
}
response.pipe(params.pipe);
// TODO: Use standard response logging method here.
client.log("info",
"\nRES:", params.method, params.url,
"\nstatus:", response.statusCode);
Object.keys(response.headers).forEach(function (key) {
client.log("info", key + ":", response.headers[key]);
});
response.on('end', function () {
client.log("info", "body:", "<binary data>");
callback(null, response_data, {status: 200});
});
});
request.on('error', function (err) {
callback(err, null, {status: 500});
});
client.log("info", "\nREQ:", params.method, params.url);
Object.keys(options.headers).forEach(function (key) {
client.log("info", key + ":", options.headers[key]);
});
request.end();
},
prepare_params: function (params, url, plural_or_singular) {
params.result_key = false;
return this._super(params, url, plural_or_singular);
},
prepare_namespace: function (params) {
return params.manager_method === "create" ? params.container + "/" + params.id : params.container;
},
del: function (params, callback) {
params.id = params.id || params.data.id || this._safe_id(params.data.name);
params.container = params.data.container;
delete params.data;
this._super(params, callback);
},
get: function (params, callback) {
if (!params.http_method) params.http_method = "head";
params.id = params.id || params.data.id || this._safe_id(params.data.name);
params.container = params.data.container;
params.parseHeaders = function (headers) {
var result = {};
result.id = params.id;
result.name = decodeURIComponent(params.id);
result.hash = headers.etag;
result.last_modified = headers['last-modified'];
result.content_type = headers['content-type'];
result.bytes = headers['content-length'];
return result;
};
delete params.data;
this._super(params, callback);
},
download: function (params, callback) {
var manager = this;
params.id = params.id || params.data.id || this._safe_id(params.data.name);
params.container = params.data.container;
params.url = this.get_base_url(params) + "/" + params.id;
params.method = "GET";
delete params.data;
this._doBinaryRequest(params, this.client.scoped_token.id, function (err, result, xhr) {
if (err) return manager.safe_complete(err, null, xhr, params, callback);
manager.safe_complete(err, result, xhr, params, callback);
});
},
create: function (params, callback) {
var manager = this,
success = params.success,
container = params.data.container;
params.method = "PUT";
if (!params.headers) params.headers = {};
params.headers['Content-Type'] = 'application/octet-stream';
params.id = params.id || params.data.id || this._safe_id(params.data.name);
params.container = params.data.container;
params.url = this.get_base_url(params) + "/" + params.id;
delete params.data;
var uploader = this._openBinaryStream(params, params.headers, this.client.scoped_token.id, function (err, result) {
if (err) return manager.safe_complete(err, null, null, params, callback);
manager.safe_complete(err, result, {status: 100}, params, callback);
});
uploader.success = function (response_data, success_callback) {
manager.get({
id: params.id,
data: {
container: container
}
}, success_callback);
};
return uploader;
},
all: function (params, callback) {
var manager = this;
params.query = params.query || {};
if (!params.query.format) params.query.format = "json";
params.container = params.data.container;
params.parseResult = function (results) {
results.forEach(function (result) {
result.id = manager._safe_id(result.name);
});
return results;
};
this._super(params, callback);
}
});
module.exports = ObjectManager;