(function(window)
{
var Gitana = window.Gitana;
Gitana.Chainable = Base.extend(
/** @lends Gitana.Chainable.prototype */
{
/**
* @constructs
*
* @param {Gitana.Driver} driver
*
* @class Provides common chaining functions used by various interface methods
*/
constructor: function(driver)
{
var self = this;
this.base();
/**
* Override the Chain.__copyState method so that it utilizes a base method that we can override
* on a per-class basis.
*/
this.__copyState = function(other) {
Gitana.copyInto(this, other);
this.chainCopyState(other);
};
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// privileged methods
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
this.getDriver = function()
{
return driver;
};
this.getFactory = function()
{
return new Gitana.ObjectFactory();
};
this.httpError = function(httpError)
{
var err = new Gitana.Error();
err.name = "Http Error";
err.message = httpError.message;
err.status = httpError.status;
err.statusText = httpError.statusText;
if (httpError.errorType)
{
err.errorType = httpError.errorType;
}
// stack trace might be available
if (httpError.stacktrace)
{
err.stacktrace = httpError.stacktrace;
}
this.error(err);
return false;
};
this.missingNodeError = function(id)
{
var err = new Gitana.Error();
err.name = "Missing Node error";
err.message = "The node: " + id + " could not be found";
this.error(err);
return false;
};
/////////////////////////////////////////////////////////////////////////////////////////
//
// PRIVILEGED
// CHAIN HANDLERS
//
/////////////////////////////////////////////////////////////////////////////////////////
/**
* Performs a GET from the server and populates the chainable.
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param params
*/
this.chainGet = function(chainable, uri, params)
{
var self = this;
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
driver.gitanaGet(uri, params, {}, function(response) {
chain.handleResponse(response);
chain.next();
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Creates an object on the server (write + read).
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param object
* @param uri
* @param params
*/
this.chainCreate = function(chainable, object, uri, params)
{
var self = this;
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
// create
driver.gitanaPost(uri, params, object, function(status) {
driver.gitanaGet(uri + "/" + status.getId(), null, {}, function(response) {
chain.handleResponse(response);
chain.next();
}, function(http) {
self.httpError(http);
});
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
}, "chainCreate");
};
/**
* Creates an object on the server using one URL and then reads it back using another URL.
* This exists because the security responses don't include _doc fields like other responses.
*
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param object
* @param createUri
* @param readUri
*/
this.chainCreateEx = function(chainable, object, createUri, readUri)
{
var self = this;
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(createUri)) {
createUri = createUri.call(self);
}
// create
driver.gitanaPost(createUri, null, object, function(status) {
// allow for closures on uri for late resolution
if (Gitana.isFunction(readUri)) {
readUri = readUri.call(self, status);
}
driver.gitanaGet(readUri, null, {}, function(response) {
chain.handleResponse(response);
chain.next();
}, function(http) {
self.httpError(http);
});
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Performs a POST to the server and populates the chainable with results.
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param params
* @param payload
*/
this.chainPost = function(chainable, uri, params, payload)
{
var self = this;
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
// create
driver.gitanaPost(uri, params, payload, function(response) {
chain.handleResponse(response);
chain.next();
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Performs a POST to the server. The response is not handled.
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param params
* @param payload (optional)
* @param contentType (optional) - example "text/plain"
*/
this.chainPostEmpty = function(chainable, uri, params, payload, contentType)
{
var self = this;
// if no payload, set to empty
if (!payload)
{
payload = {};
}
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
// create
driver.gitanaPost(uri, params, payload, function(response) {
chain.next();
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Performs a POST to the server. The response is not handled.
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param params
* @param contentType (optional) - example "text/plain"
* @param payload (optional)
*/
this.chainUpload = function(chainable, uri, params, contentType, payload)
{
var self = this;
// if no payload, leave f
if (!payload)
{
payload = {};
}
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
// create
driver.gitanaUpload(uri, params, contentType, payload, function(response) {
chain.next();
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Performs a GET to the server and pushes the response into the chain.
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param params
*/
this.chainGetResponse = function(chainable, uri, params)
{
var self = this;
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
driver.gitanaGet(uri, params, {}, function(response) {
chain.next(response);
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Performs a GET to the server and pushes the text response to the callback.
*
* @param chainable
* @param uri
* @param params
*/
this.chainGetResponseText = function(chainable, uri, params)
{
var self = this;
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
driver.gitanaRequest("GET", uri, params, "text/plain", null, {}, function(response) {
chain.next(response);
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Performs a GET to the server and pushes the "rows" response attribute into the chain.
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param params
*/
this.chainGetResponseRows = function(chainable, uri, params)
{
return this.chainGetResponse(chainable, uri, params).then(function(response) {
return response["rows"];
});
};
/**
* Performs a GET to the server and checks whether the "rows" array attribute of the response
* has the given value.
*
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param value
*/
this.chainHasResponseRow = function(chainable, uri, value)
{
return this.chainGetResponse(chainable, uri).then(function(response) {
var authorized = false;
for (var i = 0; i < response.rows.length; i++)
{
if (response.rows[i].toLowerCase() == value.toLowerCase())
{
authorized = true;
}
}
return authorized;
});
};
/**
* Performs a POST to the server and pushes the response into the chain.
* Proceeds with the chain as bound to the chainable.
*
* @param chainable
* @param uri
* @param params
*/
this.chainPostResponse = function(chainable, uri, params, payload)
{
var self = this;
return this.subchain(chainable).then(function() {
var chain = this;
// allow for closures on uri for late resolution
if (Gitana.isFunction(uri)) {
uri = uri.call(self);
}
driver.gitanaPost(uri, params, payload, function(response) {
chain.next(response);
}, function(http) {
self.httpError(http);
});
// NOTE: we return false to tell the chain that we'll manually call next()
return false;
});
};
/**
* Helper to gets the principal id for a principal object, json structure or principal id itself.
* This returns something like "domainId/principalId"
*
* @param principal
*/
this.extractPrincipalDomainQualifiedId = function(principal, defaultDomainId)
{
var identifiers = this.extractPrincipalIdentifiers(principal, defaultDomainId);
return identifiers["domain"] + "/" + identifiers["principal"];
};
/**
* Helper to gets the principal id for a principal object, json structure or principal id itself.
* This returns something like "domainId/principalId"
*
* @param principal principal object or string (principal id or domain qualified principal id)
* @param defaultDomainId
*/
this.extractPrincipalIdentifiers = function(principal, defaultDomainId)
{
var identifiers = {};
if (!defaultDomainId)
{
defaultDomainId = "default";
}
if (Gitana.isString(principal))
{
var x = principal.indexOf("/");
if (x > -1)
{
identifiers["domain"] = principal.substring(0, x);
identifiers["principal"] = principal.substring(x + 1);
}
else
{
identifiers["domain"] = defaultDomainId;
identifiers["principal"] = principal;
}
}
else if (principal.objectType && principal.objectType() == "Gitana.DomainPrincipal")
{
identifiers["domain"] = principal.getDomainId();
identifiers["principal"] = principal.getId();
}
else if (principal.objectType && principal.objectType() == "Gitana.TeamMember")
{
identifiers["domain"] = principal["domainId"];
identifiers["principal"] = principal["_doc"];
}
else if (principal["_doc"])
{
identifiers["domain"] = defaultDomainId;
identifiers["principal"] = principal["_doc"];
}
else if (principal["name"])
{
identifiers["domain"] = defaultDomainId;
identifiers["principal"] = principal["name"];
}
return identifiers;
};
},
/**
* Used internally during chain copy going forward or backward through the chain.
*
* This is wired into the Chain.__copyState method and allows a convenient way to override
* chain copy behavior on a per-object-type basis.
*
* @param otherObject
*/
chainCopyState: function(otherObject)
{
}
});
})(window);