Commit 4a1655ef authored by Romain Courteaud's avatar Romain Courteaud

Update erp5storage.

Follow draft API provided by
http://git.erp5.org/gitweb/slapos.core.git/tree/HEAD:/master/bt5/slapos_jio

Drop compatibility with previous API.
parent 830a9671
...@@ -3,442 +3,232 @@ ...@@ -3,442 +3,232 @@
* Released under the LGPL license. * Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html * http://www.gnu.org/licenses/lgpl.html
*/ */
// JIO Erp5 Storage Description : // JIO ERP5 Storage Description :
// { // {
// type: "erp5" // type: "erp5"
// url: {string} // url: {string}
// mode: {string} (optional)
// - "generic" (default)
// - "erp5_only"
//
// with
//
// auth_type: {string} (optional)
// - "none" (default)
// - "basic" (not implemented)
// - "digest" (not implemented)
// username: {string}
// password: {string} (optional)
// - no password (default)
//
// or
//
// encoded_login: {string} (not implemented)
//
// or
//
// secured_login: {string} (not implemented)
// } // }
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint nomen: true, unparam: true */
/*global define, jIO, jQuery, complex_queries */ /*global jIO, complex_queries, console, UriTemplate, FormData, RSVP */
(function (dependencies, module) { (function (jIO, complex_queries) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
module(jIO, jQuery, complex_queries);
}(['jio', 'jquery', 'complex_queries'], function (jIO, $, complex_queries) {
"use strict"; "use strict";
function ERP5Storage(spec) { function ERP5Storage(spec) {
var priv = {}, that = this, erp5 = {}; if (typeof spec.url !== 'string' && !spec.url) {
throw new TypeError("ERP5 'url' must be a string " +
// ATTRIBUTES // "which contains more than one character.");
priv.url = null; }
priv.mode = "generic"; this._url = spec.url;
priv.auth_type = "none"; }
priv.encoded_login = null;
// CONSTRUCTOR // ERP5Storage.prototype._getSiteDocument = function () {
/** return jIO.util.ajax({
* Init the erp5 storage connector thanks to the description "type": "GET",
* @method __init__ "url": this._url,
* @param {object} description The description object "xhrFields": {
*/ withCredentials: true
priv.__init__ = function (description) {
priv.url = description.url || "";
priv.url = priv.removeSlashIfLast(priv.url);
if (description.mode === "erp5_only") {
priv.mode = "erp5_only";
} }
if (description.encoded_login) { }).then(function (response) {
priv.encoded_login = description.encoded_login; return JSON.parse(response.target.responseText);
} else { });
if (description.username) { };
priv.encoded_login =
"__ac_name=" + priv.convertToUrlParameter(description.username) + ERP5Storage.prototype._get = function (param, options) {
"&" + (typeof description.password === "string" ? return this._getSiteDocument()
"__ac_password=" + .then(function (site_hal) {
priv.convertToUrlParameter(description.password) + "&" : ""); return jIO.util.ajax({
} else { "type": "GET",
priv.encoded_login = ""; "url": UriTemplate.parse(site_hal._links.traverse.href)
.expand({
relative_url: param._id,
view: options._view
}),
"xhrFields": {
withCredentials: true
}
});
})
.then(function (response) {
var result = JSON.parse(response.target.responseText);
result._id = param._id;
return result;
});
};
ERP5Storage.prototype.get = function (command, param, options) {
this._get(param, options)
.then(function (response) {
command.success({"data": response});
})
.fail(function (error) {
console.error(error);
// XXX How to propagate the error
command.error(
"not_found",
"missing",
"Cannot find document"
);
});
};
ERP5Storage.prototype.post = function (command, metadata, options) {
return this._getSiteDocument()
.then(function (site_hal) {
var post_action = site_hal._actions.add,
data = new FormData(),
key;
for (key in metadata) {
if (metadata.hasOwnProperty(key)) {
// XXX Not a form dialog in this case but distant script
data.append(key, metadata[key]);
}
} }
} return jIO.util.ajax({
}; "type": post_action.method,
"url": post_action.href,
// TOOLS // "data": data,
/** "xhrFields": {
* Replace substrings to another strings withCredentials: true
* @method recursiveReplace }
* @param {string} string The string to do replacement });
* @param {array} list_of_replacement An array of couple }).then(function (doc) {
* ["substring to select", "selected substring replaced by this string"]. // XXX Really depend on server response...
* @return {string} The replaced string var new_document_url = doc.target.getResponseHeader("Location");
*/ return jIO.util.ajax({
priv.recursiveReplace = function (string, list_of_replacement) { "type": "GET",
var i, split_string = string.split(list_of_replacement[0][0]); "url": new_document_url,
if (list_of_replacement[1]) { "xhrFields": {
for (i = 0; i < split_string.length; i += 1) { withCredentials: true
split_string[i] = priv.recursiveReplace( }
split_string[i], });
list_of_replacement.slice(1) }).then(function (response) {
var doc_hal = JSON.parse(response.target.responseText);
if (doc_hal !== null) {
command.success({"id": doc_hal._relative_url});
} else {
command.error(
"not_found",
"missing",
"Cannot find document"
); );
} }
} }, function (error) {
return split_string.join(list_of_replacement[0][1]); console.error(error);
}; command.error(
500,
/** "Too bad...",
* Changes & to %26 "Unable to post doc"
* @method convertToUrlParameter );
* @param {string} parameter The parameter to convert });
* @return {string} The converted parameter };
*/
priv.convertToUrlParameter = function (parameter) { ERP5Storage.prototype.put = function (command, metadata, options) {
return priv.recursiveReplace(parameter, [[" ", "%20"], ["&", "%26"]]); return this._get(metadata, options)
}; .then(function (result) {
var put_action = result._embedded._view._actions.put,
/** renderer_form = result._embedded._view,
* Removes the last character if it is a "/". "/a/b/c/" become "/a/b/c" data = new FormData(),
* @method removeSlashIfLast key;
* @param {string} string The string to modify data.append(renderer_form.form_id.key,
* @return {string} The modified string renderer_form.form_id['default']);
*/ for (key in metadata) {
priv.removeSlashIfLast = function (string) { if (metadata.hasOwnProperty(key)) {
if (string[string.length - 1] === "/") { if (key !== "_id") {
return string.slice(0, -1); // Hardcoded my_ ERP5 behaviour
} if (renderer_form.hasOwnProperty("my_" + key)) {
return string; data.append(renderer_form["my_" + key].key, metadata[key]);
}; } else {
throw new Error("Can not save property " + key);
/** }
* Modify an ajax object to add default values
* @method makeAjaxObject
* @param {object} json The JSON object
* @param {object} option The option object
* @param {string} method The erp5 request method
* @param {object} ajax_object The ajax object to override
* @return {object} A new ajax object with default values
*/
priv.makeAjaxObject = function (json, option, method, ajax_object) {
ajax_object.type = "POST";
ajax_object.dataType = "json";
ajax_object.data = [
{"name": "doc", "value": JSON.stringify(json)},
{"name": "option", "value": JSON.stringify(option)},
{"name": "mode", "value": priv.mode}
];
ajax_object.url = priv.url + "/JIO_" + method +
"?" + priv.encoded_login + "_=" + Date.now();
ajax_object.async = ajax_object.async === false ? false : true;
ajax_object.crossdomain =
ajax_object.crossdomain === false ? false : true;
ajax_object.headers = ajax_object.headers || {};
return ajax_object;
};
/**
* Runs all ajax requests for erp5Storage
* @method ajax
* @param {object} json The JSON object
* @param {object} option The option object
* @param {string} method The erp5 request method
* @param {object} ajax_object The request parameters (optional)
*/
priv.ajax = function (json, option, method, ajax_object) {
return $.ajax(
priv.makeAjaxObject(json, option, method, ajax_object || {})
);
//.always(then || function () {});
};
/**
* Creates error objects for this storage
* @method createError
* @param {string} url url to clean up
* @return {object} error The error object
*/
priv.createError = function (status, message, reason) {
return {
"status": status,
"message": message,
"reason": reason
};
};
/**
* Converts ajax error object to a JIO error object
* @method ajaxErrorToJioError
* @param {object} ajax_error_object The ajax error object
* @param {string} message The error message
* @param {string} reason The error reason
* @return {object} The JIO error object
*/
priv.ajaxErrorToJioError = function (ajax_error_object, message, reason) {
var jio_error_object = {};
jio_error_object.status = ajax_error_object.status;
jio_error_object.statusText = ajax_error_object.statusText;
jio_error_object.error =
ajax_error_object.statusText.toLowerCase().split(" ").join("_");
jio_error_object.message = message;
jio_error_object.reason = reason;
return jio_error_object;
};
/**
* Function that create an object containing jQuery like callbacks
* @method makeJQLikeCallback
* @return {object} jQuery like callback methods
*/
priv.makeJQLikeCallback = function () {
var result = null, emptyFun = function () {
return;
}, jql = {
"respond": function () {
result = arguments;
},
"to_return": {
"always": function (func) {
if (result) {
func.apply(func, result);
jql.to_return.always = emptyFun;
} else {
jql.respond = func;
} }
return jql.to_return;
}
}
};
return jql;
};
/**
* Use option object and converts a query to a compatible ERP5 Query.
*
* @param {Object} option The command options
*/
priv.convertToErp5Query = function (option) {
option.query = complex_queries.QueryFactory.create(option.query || "");
if (option.wildcard_character === undefined ||
(option.wildcard_character !== null &&
typeof option.wildcard_character !== 'string')) {
option.wildcard_character = '%';
} else {
option.wildcard_character = option.wildcard_character || '';
}
option.query.onParseSimpleQuery = function (object) {
if (option.wildcard_character.length === 1 &&
object.parsed.operator === '=') {
object.parsed.operator = 'like';
if (option.wildcard_character === '%') {
object.parsed.value =
object.parsed.value.replace(/_/g, '\\_');
} else if (option.wildcard_character === '_') {
object.parsed.value =
object.parsed.value.replace(/%/g, '\\%').replace(/_/g, '%');
} else {
object.parsed.value =
object.parsed.value.replace(
/([%_])/g,
'\\$1'
).replace(
new RegExp(complex_queries.stringEscapeRegexpCharacters(
option.wildcard_character
), 'g'),
'%'
);
} }
} }
}; return jIO.util.ajax({
option.query = option.query.parse(); "type": put_action.method,
}; "url": put_action.href,
"data": data,
// ERP5 REQUESTS // "xhrFields": {
/** withCredentials: true
* Sends a request to ERP5
* @method erp5.genericRequest
* @param {object} doc The document object
* @param {object} option The option object
* @param {string} method The ERP5 request method
*/
erp5.genericRequest = function (method, json, option) {
var jql = priv.makeJQLikeCallback(), error = null;
priv.ajax(json, option, method).always(function (one, state) {
if (state === "parsererror") {
return jql.respond(priv.createError(
24,
"Cannot parse data",
"Corrupted data"
), undefined);
}
if (state !== "success") {
error = priv.ajaxErrorToJioError(
one,
"An error occured on " + method,
"Unknown"
);
if (one.status === 404) {
error.reason = "Not Found";
} }
return jql.respond(error, undefined); });
} })
if (one.err !== null) { .then(function (result) {
return jql.respond(one.err, undefined); command.success(result);
} })
if (one.response !== null) { .fail(function (error) {
return jql.respond(undefined, one.response); console.error(error);
} command.error(
return jql.respond(priv.createError( "error",
24, "did not work as expected",
"Cannot parse data", "Unable to call put"
"Corrupted data" );
), undefined);
}); });
return jql.to_return;
};
// JIO COMMANDS // };
/**
* The ERP5 storage generic command ERP5Storage.prototype.allDocs = function (command, param, options) {
* @method genericCommand return this._getSiteDocument()
* @param {object} command The JIO command object .then(function (site_hal) {
* @param {string} method The ERP5 request method return jIO.util.ajax({
*/ "type": "GET",
priv.genericCommand = function (method, command, param, option) { "url": UriTemplate.parse(site_hal._links.raw_search.href)
if (complex_queries !== undefined && .expand({
method === 'allDocs' && query: options.query,
option.query) { // XXX Force erp5 to return embedded document
priv.convertToErp5Query(option); select_list: options.select_list || ["title", "reference"],
} limit: options.limit
erp5.genericRequest( }),
method, "xhrFields": {
param, withCredentials: true
option }
).always(function (err, response) { });
if (err) { })
return command.error(err); .then(function (response) {
} return JSON.parse(response.target.responseText);
if (['get', 'getAttachment', 'allDocs'].indexOf(method) === -1) { })
return command.success(response); .then(function (catalog_json) {
var data = catalog_json._embedded.contents,
count = data.length,
i,
item,
result = [],
promise_list = [result];
for (i = 0; i < count; i += 1) {
item = data[i];
item._id = item._relative_url;
result.push({
id: item._relative_url,
key: item._relative_url,
doc: {},
value: item
});
// if (options.include_docs) {
// promise_list.push(RSVP.Queue().push(function () {
// return this._get({_id: item.name}, {_view: "View"});
// }).push
// }
} }
return command.success({"data": response}); return RSVP.all(promise_list);
})
.then(function (promise_list) {
var result = promise_list[0],
count = result.length;
command.success({"data": {"rows": result, "total_rows": count}});
})
.fail(function (error) {
console.error(error);
command.error(
"error",
"did not work as expected",
"Unable to call allDocs"
);
}); });
};
/**
* Creates a new document
* @method post
* @param {object} command The JIO command
*/
that.post = function (command, metadata, options) {
priv.genericCommand("post", command, metadata, options);
};
/** };
* Creates or updates a document
* @method put
* @param {object} command The JIO command
*/
that.put = function (command, metadata, options) {
priv.genericCommand("put", command, metadata, options);
};
/**
* Add an attachment to a document
* @method putAttachment
* @param {object} command The JIO command
*/
that.putAttachment = function (command, param, options) {
priv.genericCommand("putAttachment", command, param, options);
};
/**
* Get a document
* @method get
* @param {object} command The JIO command
*/
that.get = function (command, param, options) {
priv.genericCommand("get", command, param, options);
};
/**
* Get an attachment
* @method getAttachment
* @param {object} command The JIO command
*/
that.getAttachment = function (command, param, options) {
priv.genericCommand("getAttachment", command, param, options);
};
/**
* Remove a document
* @method remove
* @param {object} command The JIO command
*/
that.remove = function (command, param, options) {
priv.genericCommand("remove", command, param, options);
};
/**
* Remove an attachment
* @method removeAttachment
* @param {object} command The JIO command
*/
that.removeAttachment = function (command, param, options) {
priv.genericCommand("removeAttachment", command, param, options);
};
/**
* Gets a document list from a distant erp5 storage
* Options:
* - {boolean} include_docs Also retrieve the actual document content.
* @method allDocs
* @param {object} command The JIO command
*/
that.allDocs = function (command, param, options) {
priv.genericCommand("allDocs", command, param, options);
};
/**
* Checks a document state
* @method check
* @param {object} command The JIO command
*/
that.check = function (command, param, options) {
priv.genericCommand("check", command, param, options);
};
/**
* Restore a document state to a coherent state
* @method repair
* @param {object} command The JIO command
*/
that.repair = function (command, param, options) {
priv.genericCommand("repair", command, param, options);
};
priv.__init__(spec);
if (typeof priv.url !== "string" || priv.url === "") {
throw new TypeError("The erp5 server URL is not provided");
}
if (priv.encoded_login === null) {
throw new TypeError("Impossible to create the authorization");
}
}
jIO.addStorage("erp5", ERP5Storage); jIO.addStorage("erp5", ERP5Storage);
})); }(jIO, complex_queries));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment