alfresco-js-api
Version:
JavaScript client library for the Alfresco REST API
341 lines (292 loc) • 14.3 kB
JavaScript
'use strict';
var Emitter = require('event-emitter');
var ApiClient = require('./alfresco-core-rest-api/src/ApiClient');
var superagent = require('superagent');
var Storage = require('./storage');
class AlfrescoApiClient extends ApiClient {
/**
* @param {String} host
* */
constructor(host) {
super();
this.storage = new Storage();
this.host = host;
Emitter.call(this);
}
/**
* Invokes the REST service using the supplied settings and parameters.
*
* @param {String} path The base URL to invoke.
* @param {String} httpMethod The HTTP method to use.
* @param {Object.<String, String>} pathParams A map of path parameters and their values.
* @param {Object.<String, Object>} queryParams A map of query parameters and their values.
* @param {Object.<String, Object>} headerParams A map of header parameters and their values.
* @param {Object.<String, Object>} formParams A map of form parameters and their values.
* @param {Object} bodyParam The value to pass as the request body.
* @param {String[]} authNames An array of authentication type names.
* @param {String[]} contentTypes An array of request MIME types.
* @param {String[]} accepts An array of acceptable response MIME types.
* @param {(String|Array|ObjectFunction)} returnType The required type to return; can be a string for simple types or the
* @param {(String)} contextRoot alternative contextRoot
* @param {(String)} responseType is an enumerated value that returns the type of the response.
* It also lets the author change the response type to one "arraybuffer", "blob", "document",
* "json", or "text".
* If an empty string is set as the value of responseType, it is assumed as type "text".
* constructor for a complex type. * @returns {Promise} A Promise object.
*/
callApi(path, httpMethod, pathParams, queryParams, headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, returnType, contextRoot, responseType) {
var url;
if (contextRoot) {
var basePath = this.host + '/' + contextRoot;
url = this.buildUrlCustomBasePath(basePath, path, pathParams);
} else {
url = this.buildUrl(path, pathParams);
}
return this.callHostApi(path, httpMethod, pathParams, queryParams, headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, returnType, contextRoot, responseType, url);
}
/**
* Invokes the REST service using the supplied settings and parameters but not the basepath.
*
* @param {String} path The base URL to invoke.
* @param {String} httpMethod The HTTP method to use.
* @param {Object.<String, String>} pathParams A map of path parameters and their values.
* @param {Object.<String, Object>} queryParams A map of query parameters and their values.
* @param {Object.<String, Object>} headerParams A map of header parameters and their values.
* @param {Object.<String, Object>} formParams A map of form parameters and their values.
* @param {Object} bodyParam The value to pass as the request body.
* @param {String[]} authNames An array of authentication type names.
* @param {String[]} contentTypes An array of request MIME types.
* @param {String[]} accepts An array of acceptable response MIME types.
* @param {(String|Array|ObjectFunction)} returnType The required type to return; can be a string for simple types or the
* @param {(String)} contextRoot alternative contextRoot
* @param {(String)} responseType is an enumerated value that returns the type of the response.
* It also lets the author change the response type to one "arraybuffer", "blob", "document",
* "json", or "text".
* If an empty string is set as the value of responseType, it is assumed as type "text".
* constructor for a complex type. * @returns {Promise} A Promise object.
*/
callCustomApi(path, httpMethod, pathParams, queryParams, headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, returnType, contextRoot, responseType) {
var url = this.buildUrlCustomBasePath(path, '', pathParams);
return this.callHostApi(path, httpMethod, pathParams, queryParams, headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, returnType, contextRoot, responseType, url);
}
/**
* Invokes the REST service using the supplied settings and parameters.
*
* @param {String} path The base URL to invoke.
* @param {String} httpMethod The HTTP method to use.
* @param {Object.<String, String>} pathParams A map of path parameters and their values.
* @param {Object.<String, Object>} queryParams A map of query parameters and their values.
* @param {Object.<String, Object>} headerParams A map of header parameters and their values.
* @param {Object.<String, Object>} formParams A map of form parameters and their values.
* @param {Object} bodyParam The value to pass as the request body.
* @param {String[]} authNames An array of authentication type names.
* @param {String[]} contentTypes An array of request MIME types.
* @param {String[]} accepts An array of acceptable response MIME types.
* @param {(String|Array|ObjectFunction)} returnType The required type to return; can be a string for simple types or the
* @param {(String)} contextRoot alternative contextRoot
* @param {(String)} responseType is an enumerated value that returns the type of the response.
* It also lets the author change the response type to one "arraybuffer", "blob", "document",
* "json", or "text".
* If an empty string is set as the value of responseType, it is assumed as type "text".
* constructor for a complex type. * @returns {Promise} A Promise object.
*/
callHostApi(path, httpMethod, pathParams, queryParams, headerParams, formParams, bodyParam, authNames,
contentTypes, accepts, returnType, contextRoot, responseType, url) {
var eventEmitter = {};
Emitter(eventEmitter); // jshint ignore:line
var request = this.buildRequest(httpMethod, url, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts, responseType, eventEmitter, returnType);
if (returnType === 'Binary') {
request = request.buffer(true).parse(superagent.parse['application/octet-stream']);
}
this.promise = new Promise((resolve, reject) => {
request.end((error, response) => {
if (error) {
this.emit('error', error);
eventEmitter.emit('error', error);
if (error.status === 401) {
this.emit('unauthorized');
eventEmitter.emit('unauthorized');
}
if (response && response.text) {
error = error || {};
reject(Object.assign(error, {message: response.text}));
} else {
reject({error: error});
}
} else {
if (this.isBpmRequest()) {
if (response.header && response.header.hasOwnProperty('set-cookie')) {
this.authentications.cookie = response.header['set-cookie'];
}
}
var data = {};
if (response.type === 'text/html') {
data = this.deserialize(response, 'String');
} else {
data = this.deserialize(response, returnType);
}
eventEmitter.emit('success', data);
resolve(data);
}
}).on('abort', () => {
eventEmitter.emit('abort');
});
});
this.promise.on = function () {
eventEmitter.on.apply(eventEmitter, arguments);
return this;
};
this.promise.once = function () {
eventEmitter.once.apply(eventEmitter, arguments);
return this;
};
this.promise.emit = function () {
eventEmitter.emit.apply(eventEmitter, arguments);
return this;
};
this.promise.off = function () {
eventEmitter.off.apply(eventEmitter, arguments);
return this;
};
this.promise.abort = function () {
request.abort();
return this;
};
return this.promise;
}
isBpmRequest() {
return this.className === 'BpmAuth' || this.className === 'BpmClient';
}
isCsrfEnabled() {
if (this.config) {
return !this.config.disableCsrf;
} else {
return true;
}
}
setCsrfToken(request) {
var token = this.token();
request.set('X-CSRF-TOKEN', token);
if (this.isNodeEnv()) {
request.set('Cookie', 'CSRF-TOKEN=' + token + ';path=/');
}
try {
document.cookie = 'CSRF-TOKEN=' + token + ';path=/';
} catch (err) {
}
}
isNodeEnv() {
return (typeof process !== 'undefined') && (process.release && process.release.name === 'node');
}
token(a) {
return a ? (a ^ Math.random() * 16 >> a / 4).toString(16) : ([1e16] + 1e16).replace(/[01]/g, this.token);
}
progress(event, eventEmitter) {
if (event.lengthComputable && this.promise) {
var percent = Math.round(event.loaded / event.total * 100);
eventEmitter.emit('progress', {
total: event.total,
loaded: event.loaded,
percent: percent
});
}
}
/**
* Builds full URL by appending the given path to the base URL and replacing path parameter place-holders
* with parameter values.
*
* @param {String} basePath the base path
* @param {String} path The path to append to the base URL.
* @param {Object} pathParams The parameter values to append.
* @returns {String} The encoded path with parameter values substituted.
*/
buildUrlCustomBasePath(basePath, path, pathParams) {
if (!path.match(/^\//)) {
path = '/' + path;
}
var url = basePath + path;
var _this = this;
url = url.replace(/\{([\w-]+)\}/g, function (fullMatch, key) {
var value;
if (pathParams.hasOwnProperty(key)) {
value = _this.paramToString(pathParams[key]);
} else {
value = fullMatch;
}
return encodeURIComponent(value);
});
return url;
}
buildRequest(httpMethod, url, queryParams, headerParams, formParams, bodyParam,
contentTypes, accepts, responseType, eventEmitter, returnType) {
var request = superagent(httpMethod, url);
// apply authentications
this.applyAuthToRequest(request, ['basicAuth']);
// set query parameters
request.query(this.normalizeParams(queryParams));
// set header parameters
request.set(this.defaultHeaders).set(this.normalizeParams(headerParams));
if (this.isBpmRequest() && this.isCsrfEnabled()) {
this.setCsrfToken(request);
}
// add cookie for activiti
if (this.isBpmRequest()) {
request._withCredentials = true;
if (this.authentications.cookie) {
if (this.isNodeEnv()) {
request.set('Cookie', this.authentications.cookie);
}
}
}
// set request timeout
request.timeout(this.timeout);
var contentType = this.jsonPreferredMime(contentTypes);
if (contentType && contentType !== 'multipart/form-data') {
request.type(contentType);
} else if (!request.header['Content-Type'] && contentType !== 'multipart/form-data') {
request.type('application/json');
}
if (contentType === 'application/x-www-form-urlencoded') {
request.send(this.normalizeParams(formParams)).on('progress', (event) => {
this.progress(event, eventEmitter);
});
} else if (contentType === 'multipart/form-data') {
var _formParams = this.normalizeParams(formParams);
for (var key in _formParams) {
if (_formParams.hasOwnProperty(key)) {
if (this.isFileParam(_formParams[key])) {
// file field
request.attach(key, _formParams[key]).on('progress', (event) => {// jshint ignore:line
this.progress(event, eventEmitter);
});
} else {
request.field(key, _formParams[key]).on('progress', (event) => {// jshint ignore:line
this.progress(event, eventEmitter);
});
}
}
}
} else if (bodyParam) {
request.send(bodyParam).on('progress', (event) => {
this.progress(event, eventEmitter);
});
}
var accept = this.jsonPreferredMime(accepts);
if (accept) {
request.accept(accept);
}
if (returnType === 'Blob' || responseType === 'blob' || responseType === 'Blob') {
request.responseType('blob');
} else if (returnType === 'String') {
request.responseType('string');
}
return request;
}
}
Emitter(AlfrescoApiClient.prototype); // jshint ignore:line
module.exports = AlfrescoApiClient;