(function(window)
{
Gitana = Base.extend(
/** @lends Gitana.prototype */
{
/**
* @constructs
*
* @class Gitana
*
* Configuration options should look like:
*
* {
* "clientKey": {String} the oauth2 client id,
* "clientSecret": [String] the oauth2 client secret,
* "baseURL": [String] the relative URI path of the base URL (assumed to be "/proxy"),
* "locale": [String] optional locale (assumed to be en_US),
* "storage": [String|Object] Gitana.OAuth2.Storage implementation or a string identifying where to store
* Gitana OAuth2 tokens ("local", "session", "memory") or empty for memory-only storage
* }
*/
constructor: function(settings)
{
var self = this;
if (!settings)
{
settings = {};
}
if (settings.host)
{
settings.baseURL = settings.host + "/proxy";
}
this.applicationInfo = {};
this.stackInfo = {};
// build config
var config = {
"clientKey": null,
"clientSecret": null,
"baseURL": "/proxy",
"locale": null,
"application": null,
"loadAppHelper": true,
"storage": null
};
if (Gitana.DEFAULT_CONFIG)
{
for (var k in Gitana.DEFAULT_CONFIG)
{
if (Gitana.DEFAULT_CONFIG.hasOwnProperty(k))
{
config[k] = Gitana.DEFAULT_CONFIG[k];
}
}
}
Gitana.copyKeepers(config, Gitana.loadDefaultConfig());
Gitana.copyKeepers(config, settings);
if (typeof(config.cacheBuster) === "undefined")
{
config.cacheBuster = true;
}
//////////////////////////////////////////////////////////////////////////
//
// APPLY CONFIGURATION SETTINGS
//
// baseURL
this.baseURL = config.baseURL;
// locale
this.locale = config.locale;
//////////////////////////////////////////////////////////////////////////
//
// APPLY OAUTH2 SETTINGS
//
// set up our oAuth2 connection
var options = {};
if (config.clientKey) {
options.clientKey = config.clientKey;
}
if (config.clientSecret) {
options.clientSecret = config.clientSecret;
}
if (this.baseURL)
{
options.baseURL = this.baseURL;
options.tokenURL = "/oauth/token";
}
// the driver requires the "api" scope to be granted
options.requestedScope = "api";
//////////////////////////////////////////////////////////////////////////
//
// PRIVILEGED METHODS
//
//
this.updateOptions = function(o)
{
if (o)
{
Gitana.copyInto(options, o);
}
};
this.resetHttp = function(config)
{
var o = {};
Gitana.copyInto(o, options);
if (config)
{
Gitana.copyInto(o, config);
}
if (!o.storage)
{
o.storage = this.getOriginalConfiguration().storage;
}
self.http = new Gitana.OAuth2Http(o, o.storage);
};
this.setAuthInfo = function(authInfo)
{
this.authInfo = authInfo;
};
this.setStackInfo = function(stackInfo)
{
this.stackInfo = stackInfo;
};
this.setApplicationInfo = function(applicationInfo)
{
this.applicationInfo = applicationInfo;
};
this.getOriginalConfiguration = function()
{
return config;
};
this.getHttpHeaders = function()
{
var self = this;
var headers = {};
if (self.http && self.http.getBearerAuthorizationHeader())
{
headers["Authorization"] = self.http.getBearerAuthorizationHeader();
}
return headers;
};
},
/**
* Sets the authentication info
*/
getAuthInfo: function()
{
return this.authInfo;
},
getStackInfo: function()
{
return this.stackInfo;
},
getApplicationInfo: function()
{
return this.applicationInfo;
},
/**
* Sets the default locale for interactions with the Gitana server by this driver.
*
* @public
*
* @param {String} locale locale string
*/
setLocale: function(locale)
{
this.locale = locale;
},
/**
* Retrieves the default locale being used by this driver.
*
* @returns {String} locale string
*/
getLocale: function()
{
return this.locale;
},
/**
* Default AJAX failure callback
*
* @public
*/
defaultFailureCallback: function(http)
{
// if we're in debug mode, log a bunch of good stuff out to console
if (this.debug)
{
if (typeof console != "undefined")
{
var message = "Received bad http state (" + http.status + ")";
var stacktrace = null;
var json = null;
var responseText = http.responseText;
if (responseText)
{
json = JSON.parse(responseText);
if (json && json.message)
{
message = message + ": " + json.message;
}
}
if (json && json["stacktrace"])
{
stacktrace = json["stacktrace"];
}
console.log(message);
if (stacktrace)
{
console.log(stacktrace);
}
}
}
},
/**
* Performs Ajax communication with the Gitana server.
*
* NOTE: For the most part, you shouldn't have to use this function since most of the things you'd want
* to do with the Gitana server are wrapped by helper functions.
*
* @see Gitana.Driver#gitanaGet
* @see Gitana.Driver#gitanaPost
* @see Gitana.Driver#gitanaPut
* @see Gitana.Driver#gitanaDel
* @see Gitana.Driver#gitanaRequest
*
* @public
*
* @param {String} method The kind of method to invoke - "get", "post", "put", or "del"
* @param {String} url The full URL to the resource being requested (i.e. "http://server:port/uri"}
* @param {String} [contentType] In the case of a payload carrying request (i.e. not GET), the content type being sent.
* @param {Object} [data] In the case of a payload carrying request (i.e. not GET), the data to plug into the payload.
* @param {Object} [headers] A key/value map of headers to place into the request.
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails. If none provided, the default driver callback is used.
*/
ajax: function(method, url, contentType, data, headers, successCallback, failureCallback)
{
var _this = this;
// ensure headers
if (!headers)
{
headers = {};
}
// treat the method
if (method == null) {
method = "GET";
}
method = method.toUpperCase();
// flags
var json = false;
if (contentType == "application/json")
{
json = true;
}
// error checking
if ( (method == "POST" || method == "PUT") )
{
headers["Content-Type"] = contentType;
if (!contentType)
{
Gitana.debug("Performing method: " + method + " but missing content type");
return;
}
}
var toSend = data;
// special handling for json
if (json)
{
// if empty payload for payload-bearing methods, populate with {}
if (method == "PUT" || method == "POST")
{
if (!data)
{
data = {};
}
}
if (!Gitana.isString(data))
{
// stringify
toSend = Gitana.stringify(data);
}
}
//
// if the URL is relative and we're running in a browser, then we can pad the URL
// based on the URL of the browser
//
// otherwise, we can't handle relative URLs
//
if (url.substring(0,1) == "/")
{
// if window.location exists, then we're running on a browser
if (!Gitana.isUndefined(window.location))
{
var u = window.location.protocol + "//" + window.location.host;
if (window.location.host.indexOf(":") == -1)
{
if (window.location.port) {
u += ":" + window.location.port;
}
}
url = u + url;
}
else
{
// relative urls are not supported outside of the browser
throw new Error("Relative URL not supported outside of the browser: " + url);
}
}
var config = {
"method": method,
"url": url,
"data": toSend,
"headers": headers,
"success": successCallback,
"failure": failureCallback
};
Gitana.requestCount++;
this.http.request(config);
return this;
},
/**
* Send an HTTP request via AJAX to the Gitana Server.
*
* This method will additionally make sure of the following:
*
* 1) That the Gitana Driver authentication ticket is plugged onto the request.
* 2) That the Gitana Driver locale is plugged onto the request.
* 3) That full object data is returned (including metadata).
*
* @public
*
* @param {String} method The kind of method to invoke - "get", "post", "put", or "del"
* @param {String} url Either a full URL (i.e. "http://server:port/uri") or a URI against the driver's server URL (i.e. /repositories/...)
* @param {Object} params parameter map
* @param [String] contentType If the case of a payload carrying request (i.e. not GET), the content type being sent.
* @param {Object} data In the case of a payload carrying request (i.e. not GET), the JSON to plug into the payload.
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails.
*/
gitanaRequest: function(method, url, params, contentType, data, headers, successCallback, failureCallback)
{
// make sure we compute the real url
if (Gitana.startsWith(url, "/")) {
url = this.baseURL + url;
}
if (!failureCallback)
{
failureCallback = this.defaultFailureCallback;
}
if (!headers)
{
headers = {};
}
/**
* Primary success callback handler for oAuth call to server.
*
* @param responseObject
* @param xhr
*/
var onSuccess = function(responseObject, xhr)
{
if (successCallback)
{
// call back with just the response text (or json)
var arg = responseObject.text;
if (contentType == "application/json")
{
try {
arg = new Gitana.Response(JSON.parse(arg));
} catch (ex) {
failureCallback(ex);
}
}
successCallback(arg);
}
};
/**
* Primary failure callback handler for oAuth call to server.
*
* @param responseObject
* @param xhr
*/
var onFailure = function(responseObject, xhr)
{
if (failureCallback)
{
var httpError = {};
if (responseObject.timeout)
{
// due to a timeout
httpError["statusText"] = "Connection timed out";
httpError["status"] = xhr.status;
httpError["errorType"] = "timeout";
httpError["message"] = "Connection timed out";
httpError["response"] = responseObject;
httpError["xhr"] = xhr;
}
else
{
// due to an HTTP error
httpError["statusText"] = xhr.statusText;
httpError["status"] = xhr.status;
httpError["errorType"] = "http";
httpError["response"] = responseObject;
httpError["xhr"] = xhr;
var message = null;
var stacktrace = null;
var arg = responseObject.text;
if (contentType == "application/json")
{
try
{
var obj = new Gitana.Response(JSON.parse(arg));
if (obj.message)
{
message = obj.message;
}
if (obj.stacktrace)
{
stacktrace = obj.stacktrace;
}
}
catch (e) { }
}
if (message)
{
httpError.message = message;
}
if (stacktrace)
{
httpError.stacktrace = stacktrace;
}
}
failureCallback(httpError);
}
};
// ensure we have some params
if (!params)
{
params = {};
}
// adjust url to include "full" as well as "metadata" if not included
if (Gitana.isEmpty(params["metadata"]))
{
params["metadata"] = true;
}
if (Gitana.isEmpty(params["full"]))
{
params["full"] = true;
}
if (this.locale) {
headers["accept-language"] = this.locale;
params["locale"] = this.locale;
}
// cache buster
var cacheBuster = null;
if (this.getOriginalConfiguration().cacheBuster === true)
{
cacheBuster = new Date().getTime();
}
else if (typeof(this.getOriginalConfiguration().cacheBuster) === "string")
{
cacheBuster = this.getOriginalConfiguration().cacheBuster;
}
else if (typeof(this.getOriginalConfiguration().cacheBuster) === "function")
{
cacheBuster = this.getOriginalConfiguration().cacheBuster();
}
if (cacheBuster)
{
params["cb"] = cacheBuster;
}
// update URL to include params
for (var paramKey in params)
{
var paramValue = params[paramKey];
if (Gitana.isFunction(paramValue))
{
paramValue = paramValue.call();
}
else if (Gitana.isString(paramValue))
{
// NOTHING TO DO
}
else if (Gitana.isNumber(paramValue))
{
// NOTHING TO DO
}
else
{
paramValue = escape(Gitana.stringify(paramValue, false));
}
// apply
if (url.indexOf("?") > -1)
{
url = url + "&" + paramKey + "=" + paramValue;
}
else
{
url = url + "?" + paramKey + "=" + paramValue;
}
}
return this.ajax(method, url, contentType, data, headers, onSuccess, onFailure);
},
/**
* Sends an HTTP GET request to the Gitana server.
*
* @public
*
* @param {String} url Either a full URL (i.e. "http://server:port/uri") or a URI against the driver's server URL (i.e. /repositories/...)
* @param {Object} params request parameters
* @param {Object} headers request headers
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails.
*/
gitanaGet: function(url, params, headers, successCallback, failureCallback)
{
return this.gitanaRequest("GET", url, params, "application/json", null, headers, successCallback, failureCallback);
},
/**
* Sends an HTTP GET request to the Gitana server.
*
* @public
*
* @param {String} url Either a full URL (i.e. "http://server:port/uri") or a URI against the driver's server URL (i.e. /repositories/...)
* @param {Object} params request parameters
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails.
*/
gitanaDownload: function(url, params, successCallback, failureCallback)
{
return this.gitanaRequest("GET", url, params, null, null, {}, successCallback, failureCallback);
},
/**
* Sends an HTTP POST request to the Gitana server.
*
* @public
*
* @param {String} url Either a full URL (i.e. "http://server:port/uri") or a URI against the driver's server URL (i.e. /repositories/...)
* @param {Object} params request parameters
* @param {Object} [jsonData] The JSON to plug into the payload.
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails.
*/
gitanaPost: function(url, params, jsonData, successCallback, failureCallback)
{
return this.gitanaRequest("POST", url, params, "application/json", jsonData, {}, successCallback, failureCallback);
},
/**
* Sends an HTTP POST request to the Gitana server.
*
* @public
*
* @param {String} url Either a full URL (i.e. "http://server:port/uri") or a URI against the driver's server URL (i.e. /repositories/...)
* @param {Object} params request parameters
* @param {String} contentType content type being sent
* @param {Object} [jsonData] The JSON to plug into the payload.
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails.
*/
gitanaUpload: function(url, params, contentType, data, successCallback, failureCallback)
{
return this.gitanaRequest("POST", url, params, contentType, data, {}, successCallback, failureCallback);
},
/**
* Sends an HTTP PUT request to the Gitana server.
*
* @public
*
* @param {String} url Either a full URL (i.e. "http://server:port/uri") or a URI against the driver's server URL (i.e. /repositories/...)
* @param {Object} params request parameters
* @param {Object} [jsonData] The JSON to plug into the payload.
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails.
*/
gitanaPut: function(url, params, jsonData, successCallback, failureCallback)
{
return this.gitanaRequest("PUT", url, params, "application/json", jsonData, {}, successCallback, failureCallback);
},
/**
* Sends an HTTP DELETE request to the Gitana server.
*
* @public
*
* @param {String} url Either a full URL (i.e. "http://server:port/uri") or a URI against the driver's server URL (i.e. /repositories/...)
* @param {Object} params request parameters
* @param {Function} [successCallback] The function to call if the operation succeeds.
* @param {Function} [failureCallback] The function to call if the operation fails.
*/
gitanaDelete: function(url, params, successCallback, failureCallback)
{
return this.gitanaRequest("DELETE", url, params, "application/json", null, {}, successCallback, failureCallback);
},
getFactory: function()
{
return new Gitana.ObjectFactory();
},
//////////////////////////////////////////////////////////////////////////////////////////
//
// CHAINING METHODS
//
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Authenticates as the supplied user.
*
* A user can either be authenticated using username/password credentials or via an authentication code.
*
* Authorization Code flow:
*
* {
* "code": "<code>",
* "redirectUri": "<redirectUri>"
* }
* Username/password flow:
*
* {
* "username": "<username>",
* "password": "<password>"
* }
*
* Implicit flow:
*
* {
* "accessToken": "<accessToken>",
* "redirectUri": "<redirectUri>"
* }
*
* Using Gitana Ticket from a cookie:
*
* {
* "cookie": true
* }
*
* Using Gitana Ticket (explicitly provided):
*
* {
* "ticket": "<ticket>"
* }
*
* An authentication failure handler can be passed as the final argument
*
* @chained platform
*
* @param {Object} settings
* @param [Function] authentication failure handler
*/
authenticate: function(settings, authFailureHandler)
{
var driver = this;
// build config
var config = {
"code": null,
"redirectUri": null,
"username": null,
"password": null,
"accessToken": null,
"ticket": null,
"cookie": null,
"ticketMaxAge": null
};
Gitana.copyKeepers(config, Gitana.loadDefaultConfig());
Gitana.copyKeepers(config, settings);
// platform config (for cache key determination)
var platformConfig = {
"key": null,
"ticket": null,
"username": null,
"clientKey": null
};
Gitana.copyKeepers(platformConfig, this.getOriginalConfiguration());
Gitana.copyKeepers(platformConfig, settings);
var platformCacheKey = platformConfig.key;
if (!platformCacheKey)
{
platformCacheKey = Gitana.determinePlatformCacheKey(platformConfig, true);
}
if (platformCacheKey)
{
this.platformCacheKey = platformCacheKey;
}
// build a cluster instance
var cluster = new Gitana.Cluster(this, {});
var applyPlatformCache = function(driver, platform)
{
var platformCacheKey = driver.platformCacheKey;
if (platformCacheKey)
{
Gitana.PLATFORM_CACHE(platformCacheKey, platform);
}
// always cache on ticket as well
var ticket = driver.getAuthInfo().getTicket();
if (ticket) {
Gitana.PLATFORM_CACHE(ticket, platform);
}
};
// run with this = platform
var doAuthenticate = function()
{
var platform = this;
// we provide a fallback if no flow type is specified, using "password" flow with guest/guest
if (!config.code && !config.username && !config.accessToken && !config.cookie && !config.ticket)
{
config.username = "guest";
config.password = "guest";
}
//
// authenticate via the authentication flow
//
if (config.code)
{
// clear existing cookie and ticket
config.authorizationFlow = Gitana.OAuth2Http.AUTHORIZATION_CODE;
driver.resetHttp(config);
Gitana.deleteCookie("GITANA_TICKET", "/");
// fetch the auth info
driver.gitanaGet("/auth/info", {}, {}, function(response) {
var authInfo = new Gitana.AuthInfo(response);
driver.setAuthInfo(authInfo);
// TODO: fix this
// kill the JSESSIONID cookie which comes back from the proxy and ties us to a session
// on the Gitana server
Gitana.deleteCookie("JSESSIONID", "/");
// apply platform cache
applyPlatformCache(driver, platform);
// now continue the platform chain after we reload
platform.reload();
platform.next();
}, function(http) {
// if authentication fails, respond to custom auth failure handler
if (authFailureHandler)
{
authFailureHandler.call(platform, http);
}
});
}
//
// authenticate via password flow
//
else if (config.username)
{
// clear existing cookie and ticket
config.authorizationFlow = Gitana.OAuth2Http.PASSWORD;
driver.resetHttp(config);
Gitana.deleteCookie("GITANA_TICKET", "/");
// retrieve auth info and plug into the driver
driver.gitanaGet("/auth/info", {}, {}, function(response) {
var authInfo = new Gitana.AuthInfo(response);
driver.setAuthInfo(authInfo);
// TODO: fix this
// kill the JSESSIONID cookie which comes back from the proxy and ties us to a session
// on the Gitana server
Gitana.deleteCookie("JSESSIONID", "/");
// apply platform cache
applyPlatformCache(driver, platform);
// now continue the platform chain after we reload
platform.reload();
platform.next();
}, function(http) {
// if authentication fails, respond to custom auth failure handler
if (authFailureHandler)
{
authFailureHandler.call(platform, http);
}
});
}
//
// authenticate via implicit "token" flow
//
else if (config.accessToken)
{
// clear existing cookie and ticket
config.authorizationFlow = Gitana.OAuth2Http.TOKEN;
driver.resetHttp(config);
Gitana.deleteCookie("GITANA_TICKET", "/");
// fetch the auth info
driver.gitanaGet("/auth/info", {}, {}, function(response) {
var authInfo = new Gitana.AuthInfo(response);
driver.setAuthInfo(authInfo);
// TODO: fix this
// kill the JSESSIONID cookie which comes back from the proxy and ties us to a session
// on the Gitana server
Gitana.deleteCookie("JSESSIONID", "/");
// apply platform cache
applyPlatformCache(driver, platform);
// now continue the platform chain after we reload
platform.reload();
platform.next();
}, function(http) {
// if authentication fails, respond to custom auth failure handler
if (authFailureHandler)
{
authFailureHandler.call(platform, http);
}
});
}
//
// authenticate using an existing cookie
//
else if (config.cookie)
{
// reuse an existing cookie (token flow)
config.authorizationFlow = Gitana.OAuth2Http.COOKIE;
driver.resetHttp(config);
// fetch the auth info
driver.gitanaGet("/auth/info", {}, {}, function(response) {
var authInfo = new Gitana.AuthInfo(response);
driver.setAuthInfo(authInfo);
if (authInfo.accessToken)
{
driver.http.accessToken(authInfo.accessToken);
}
// TODO: fix this
// kill the JSESSIONID cookie which comes back from the proxy and ties us to a session
// on the Gitana server
Gitana.deleteCookie("JSESSIONID", "/");
// apply platform cache
applyPlatformCache(driver, platform);
// now continue the platform chain after we reload
platform.reload();
platform.next();
}, function(http) {
// if authentication fails, respond to custom auth failure handler
if (authFailureHandler)
{
authFailureHandler.call(platform, http);
}
});
}
//
// authenticate using an explicit gitana ticket
//
else if (config.ticket)
{
// reuse an existing cookie (token flow)
config.authorizationFlow = Gitana.OAuth2Http.TICKET;
driver.resetHttp(config);
var headers = {
"GITANA_TICKET": config.ticket
};
// fetch the auth info
driver.gitanaGet("/auth/info", {}, headers, function(response) {
var authInfo = new Gitana.AuthInfo(response);
driver.setAuthInfo(authInfo);
// TODO: fix this
// kill the JSESSIONID cookie which comes back from the proxy and ties us to a session
// on the Gitana server
Gitana.deleteCookie("JSESSIONID", "/");
// apply platform cache
applyPlatformCache(driver, platform);
// now continue the platform chain after we reload
platform.reload();
platform.next();
}, function(http) {
// if authentication fails, respond to custom auth failure handler
if (authFailureHandler)
{
authFailureHandler.call(platform, http);
}
});
}
else
{
var message = "Unsupported authentication flow - you must provide either a username, authorization code, access token or select cookie-based authentication";
if (authFailureHandler)
{
authFailureHandler.call(platform, {
"message": message
});
}
else
{
throw new Error(message);
}
}
};
var result = this.getFactory().platform(cluster);
return Chain(result).then(function() {
// NOTE: this = platform
doAuthenticate.call(this);
// tell the chain that we'll manually handle calling next()
return false;
});
},
reloadAuthInfo: function(callback)
{
var driver = this;
driver.gitanaGet("/auth/info", {}, {}, function(response) {
var authInfo = new Gitana.AuthInfo(response);
driver.setAuthInfo(authInfo);
callback();
}, function(http) {
callback(null, http);
});
},
/**
* Clears any authentication for the driver.
*/
clearAuthentication: function()
{
if (this.http.clearStorage)
{
this.http.clearStorage();
}
this.resetHttp();
Gitana.deleteCookie("GITANA_TICKET", "/");
},
/**
* Refreshes the authentication access token.
*
* @param callback
*/
refreshAuthentication: function(callback)
{
this.http.refresh(function(err) {
callback(err);
});
},
/**
* Destructor function, called at the end of the driver instance's lifecycle
*/
destroy: function()
{
this.clearAuthentication();
}
});
//
// STATICS
// Special Groups
Gitana.EVERYONE = {
"name": "everyone",
"type": "GROUP"
};
// temporary location for this code
Gitana.toCopyDependencyChain = function(typedID)
{
var array = [];
if (typedID.getType() === "node")
{
array = array.concat(Gitana.toCopyDependencyChain(typedID.getBranch()));
array = array.concat({
"typeId": "changeset",
"id": typedID.getSystemMetadata().getChangesetId()
});
}
else if (typedID.getType() === "association")
{
array = array.concat(Gitana.toCopyDependencyChain(typedID.getBranch()));
array = array.concat({
"typeId": "changeset",
"id": typedID.getSystemMetadata().getChangesetId()
});
}
else if (typedID.getType() === "branch")
{
array = array.concat(Gitana.toCopyDependencyChain(typedID.getRepository()));
}
else if (typedID.getType() === "platform")
{
// nothing to do here
}
else if (typedID.getType() === "stack")
{
array = array.concat(Gitana.toCopyDependencyChain(typedID.getPlatform()));
}
else if (typedID.getType() === "project")
{
array = array.concat(Gitana.toCopyDependencyChain(typedID.getPlatform()));
}
else
{
array = array.concat(Gitana.toCopyDependencyChain(typedID.getPlatform()));
}
array.push(Gitana.toDependencyObject(typedID));
return array;
};
Gitana.toDependencyObject = function(typedID)
{
return {
"typeId": typedID.getType(),
"id": typedID.getId()
};
};
Gitana.TypedIDConstants = {};
Gitana.TypedIDConstants.TYPE_APPLICATION = "application";
Gitana.TypedIDConstants.TYPE_EMAIL = "email";
Gitana.TypedIDConstants.TYPE_EMAIL_PROVIDER = "emailprovider";
Gitana.TypedIDConstants.TYPE_REGISTRATION = "registration";
Gitana.TypedIDConstants.TYPE_PAGE_RENDITION = "pageRendition";
Gitana.TypedIDConstants.TYPE_SETTINGS = "settings";
// cluster
Gitana.TypedIDConstants.TYPE_CLUSTER = "cluster";
Gitana.TypedIDConstants.TYPE_JOB = "job";
Gitana.TypedIDConstants.TYPE_LOG_ENTRY = "logEntry";
// directory
Gitana.TypedIDConstants.TYPE_DIRECTORY = "directory";
Gitana.TypedIDConstants.TYPE_IDENTITY = "identity";
Gitana.TypedIDConstants.TYPE_CONNECTION = "connection";
// domain
Gitana.TypedIDConstants.TYPE_DOMAIN = "domain";
Gitana.TypedIDConstants.TYPE_DOMAIN_GROUP = "group";
Gitana.TypedIDConstants.TYPE_DOMAIN_USER = "user";
// platform
Gitana.TypedIDConstants.TYPE_PLATFORM = "platform";
Gitana.TypedIDConstants.TYPE_AUTHENTICATION_GRANT = "authenticationGrant";
Gitana.TypedIDConstants.TYPE_BILLING_PROVIDERS_CONFIGURATION = "billingProviderConfiguration";
Gitana.TypedIDConstants.TYPE_CLIENT = "client";
Gitana.TypedIDConstants.TYPE_DESCRIPTOR = "externalServiceDescriptor";
Gitana.TypedIDConstants.TYPE_STACK = "stack";
Gitana.TypedIDConstants.TYPE_PROJECT = "project";
Gitana.TypedIDConstants.TYPE_SCHEDULED_WORK = "scheduled-work";
Gitana.TypedIDConstants.TYPE_REPORT = "report";
Gitana.TypedIDConstants.TYPE_WORKFLOW_INSTANCE = "workflowInstance";
Gitana.TypedIDConstants.TYPE_WORKFLOW_MODEL = "workflowModel";
Gitana.TypedIDConstants.TYPE_WORKFLOW_TASK = "workflowTask";
Gitana.TypedIDConstants.TYPE_WORKFLOW_COMMENT = "workflowComment";
// registrar
Gitana.TypedIDConstants.TYPE_REGISTRAR = "registrar";
Gitana.TypedIDConstants.TYPE_METER = "meter";
Gitana.TypedIDConstants.TYPE_PLAN = "plan";
Gitana.TypedIDConstants.TYPE_TENANT = "tenant";
// repository
Gitana.TypedIDConstants.TYPE_REPOSITORY = "repository";
Gitana.TypedIDConstants.TYPE_ASSOCIATION = "association";
Gitana.TypedIDConstants.TYPE_BRANCH = "branch";
Gitana.TypedIDConstants.TYPE_CHANGESET = "changeset";
Gitana.TypedIDConstants.TYPE_NODE = "node";
Gitana.TypedIDConstants.TYPE_RELEASE = "release";
// vault
Gitana.TypedIDConstants.TYPE_VAULT = "vault";
Gitana.TypedIDConstants.TYPE_ARCHIVE = "archive";
// warehouse
Gitana.TypedIDConstants.TYPE_WAREHOUSE = "warehouse";
Gitana.TypedIDConstants.TYPE_INTERACTION = "interaction";
Gitana.TypedIDConstants.TYPE_INTERACTION_APPLICATION = "interactionApplication";
Gitana.TypedIDConstants.TYPE_INTERACTION_NODE = "interactionNode";
Gitana.TypedIDConstants.TYPE_INTERACTION_PAGE = "interactionPage";
Gitana.TypedIDConstants.TYPE_INTERACTION_REPORT = "interactionReport";
Gitana.TypedIDConstants.TYPE_INTERACTION_REPORT_ENTRY = "interactionReportEntry";
Gitana.TypedIDConstants.TYPE_INTERACTION_SESSION = "interactionSession";
Gitana.TypedIDConstants.TYPE_INTERACTION_USER = "interactionUser";
Gitana.TypedIDConstants.TYPE_INTERACTION_CONTINENT = "interactionContinent";
Gitana.TypedIDConstants.TYPE_INTERACTION_COUNTRY = "interactionCountry";
Gitana.TypedIDConstants.TYPE_INTERACTION_CITY = "interactionCity";
Gitana.TypedIDConstants.TYPE_INTERACTION_REGION = "interactionRegion";
Gitana.TypedIDConstants.TYPE_INTERACTION_POSTALCODE = "interactionPostalCode";
Gitana.TypedIDConstants.TYPE_INTERACTION_USERAGENT = "interactionUserAgent";
Gitana.TypedIDConstants.TYPE_INTERACTION_OPERATINGSYSTEM = "interactionOperatingSystem";
Gitana.TypedIDConstants.TYPE_INTERACTION_DEVICE = "interactionDevice";
Gitana.TypedIDConstants.TYPE_CONVERSION_TRIGGER = "conversionTrigger";
// web host
Gitana.TypedIDConstants.TYPE_WEB_HOST = "webhost";
Gitana.TypedIDConstants.TYPE_AUTO_CLIENT_MAPPING = "autoClientMapping";
Gitana.TypedIDConstants.TYPE_TRUSTED_DOMAIN_MAPPING = "trustedDomainMapping";
Gitana.TypedIDConstants.TYPE_DEPLOYED_APPLICATION = "deployedApplication";
Gitana.handleJobCompletion = function(chain, cluster, jobId, synchronous, reportFn)
{
var jobFinalizer = function() {
return Chain(cluster).readJob(jobId).then(function() {
if (reportFn) {
reportFn(this);
}
if (!synchronous || (synchronous && (this.getState() == "FINISHED" || this.getState() == "ERROR")))
{
chain.loadFrom(this);
chain.next();
}
else
{
// reset timeout
window.setTimeout(jobFinalizer, 1000);
}
});
};
// set timeout
window.setTimeout(jobFinalizer, 1000);
};
/** Extension point for loading default config for server-side containers **/
Gitana.loadDefaultConfig = function()
{
};
/**
* Simple in-memory cache implementation for use by-default by the driver.
*
* @return {Function}
*/
Gitana.MemoryCache = function()
{
var cache = {};
return function(k, v)
{
if (!Gitana.isUndefined(v))
{
if (v) {
cache[k] = v;
}
else {
delete cache[k];
}
}
// support for "clear" method - removes everything from cache
if (k == "clear")
{
var za = [];
for (var z in cache)
{
za.push(z);
}
for (var i = 0; i < za.length; i++)
{
delete cache[za[i]];
}
}
return cache[k];
};
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// PLATFORM CACHE
//
//
// extension point - override with other implementations
Gitana.PLATFORM_CACHE = Gitana.MemoryCache();
Gitana.determinePlatformCacheKey = function(config, fallbackToDefault)
{
var cacheKey = null;
// "ticket" authentication - key = ticket
if (config.ticket) {
cacheKey = config.ticket;
}
else if (config.clientKey && config.username) {
cacheKey = config.clientKey + ":" + config.username;
}
else if (fallbackToDefault)
{
// if no config provided, use "default" key
cacheKey = "default";
}
return cacheKey;
};
/**
* Connects to a Gitana platform.
*
* @param config
* @param [callback] optional callback function that gets called once the server has been connected to. If no
* "application" config parameter is present, then the callback function is called with the this
* context set to the platform. If an "application" config parameter is present, then the stack
* for the application is loaded and references are resolved and the this context will be the
* app helper instance. This callback also acts as an error handler for any authentication issues.
* If an auth error happens, the err is passed to the callback as the first and only argument.
*
* @return {*}
*/
Gitana.connect = function(config, callback)
{
// allow for no config, callback-only
if (Gitana.isFunction(config)) {
callback = config;
config = null;
}
var missingConfig = false;
if (!config) {
config = {};
missingConfig = true;
}
if (Gitana.isString(config)) {
config = {"key": config};
}
// by default, set invalidatePlatformCache to false
if (typeof(config.invalidatePlatformCache) == "undefined")
{
config.invalidatePlatformCache = false;
}
// if no config key specified, we can generate one...
if (!config.key)
{
config.key = Gitana.determinePlatformCacheKey(config, missingConfig);
}
// default to load app helper if not defined
if (typeof(config.loadAppHelper) == "undefined")
{
config.loadAppHelper = true;
}
// this gets called once the platform is drawn from cache or created
// fires the callback and passes in the platform or the app helper
var setupContext = function(platformCacheKey)
{
// NOTE: this == platform
// if their configuration contains the "application" setting, then auto-load the app() context
// note that config.application could be undefined (we require explicit NULL here for copyKeepers)
if (config.loadAppHelper)
{
var appConfig = {
"application": (config.application ? config.application: null)
};
Gitana.copyKeepers(appConfig, Gitana.loadDefaultConfig());
Gitana.copyKeepers(appConfig, this.getDriver().getOriginalConfiguration());
if (appConfig.application) {
var appSettings = {
"application": appConfig.application
};
if (platformCacheKey) {
appSettings.appCacheKey = platformCacheKey + "_" + appConfig.application;
}
this.app(appSettings, function(err) {
if (callback) {
// NOTE: this == app helper
callback.call(this, err);
}
});
}
else {
if (callback) {
callback.call(this);
}
}
}
else
{
if (callback) {
callback.call(this);
}
}
};
// support for invalidatePlatformCache
if (config.key && config.invalidatePlatformCache)
{
Gitana.disconnect(config.key);
}
// either retrieve platform from cache or authenticate
var platform = null;
if (config.key) {
platform = Gitana.PLATFORM_CACHE(config.key);
}
if (platform)
{
// platform already loaded
// spawn off a new copy for thread safety
platform = Chain(new Gitana.Platform(platform.getCluster(), platform));
setupContext.call(platform, config.key);
return platform;
}
// if they didn't provide a config and made it this far, then lets assume a cookie based config?
if (missingConfig)
{
config["cookie"] = true;
}
// load it up
return new Gitana(config).authenticate(config, function(err) {
if (callback) {
callback.call(this, err);
}
}).then(function() {
// NOTE: this == platform
setupContext.call(this, config.key);
});
};
/**
* Disconnects a platform from the cache.
*
* @param key
* @param expireAccessToken
*/
Gitana.disconnect = function(key, expireAccessToken)
{
if (!key) {
key = "default";
}
var platform = Gitana.PLATFORM_CACHE(key);
if (platform)
{
// if we are meant to expire the server-side access token,
// fire off a signal to the Cloud CMS server to do so
// we ignore whether this succeeds or fails
if (expireAccessToken)
{
platform.getDriver().gitanaPost("/auth/expire", {}, {}, function() {
// success
}, function(err) {
// error
});
}
var badKeys = [];
for (var k in Gitana.APPS)
{
if (k.indexOf(key + "_") == 0)
{
badKeys.push(k);
}
}
for (var i = 0; i < badKeys.length; i++)
{
delete Gitana.APPS[badKeys[i]];
}
var ticket = platform.getDriver().getAuthInfo().getTicket();
if (ticket)
{
Gitana.PLATFORM_CACHE(ticket, null);
}
Gitana.PLATFORM_CACHE(key, null);
platform.getDriver().destroy();
}
};
// holds a total count of Ajax requests originated from the driver
Gitana.requestCount = 0;
// version of the driver
Gitana.VERSION = "__VERSION__";
// allow for optional global assignment
// TODO: until we clean up the "window" variable reliance, we have to always set onto window again
// TODO: to support loading within NodeJS
//if (window && !window.Gitana) {
if (window) {
window.Gitana = Gitana;
}
/**
* Resets the driver (used for test purposes).
*/
Gitana.reset = function()
{
Gitana.HTTP_TIMEOUT = 120000;
Gitana.PLATFORM_CACHE("clear");
Gitana.deleteCookie("GITANA_TICKET");
};
// insertion point for on-load adjustments (cloudcms-net server)
Gitana.__INSERT_MARKER = null;
// toggles use of GET method when possible (rather than POST)
// useful for branch.queryNodes()
Gitana.PREFER_GET_OVER_POST = false;
// method to call when a refresh token fails to acquire the access token
Gitana.REFRESH_TOKEN_FAILURE_FN = function(http) {
http.clearStorage();
Gitana.deleteCookie("GITANA_TICKET");
};
////////////////////////////////////////////////////////////////////////////////////////////////
//
// support for CSRF / XSRF
//
////////////////////////////////////////////////////////////////////////////////////////////////
// the CSRF token can be explicitly stored here if you want to forgo cookies as a storage mechanism
Gitana.CSRF_TOKEN = null;
// these cookies can be consulted by the driver to acquire the csrf token
// override this with different cookie names if your framework requires it
Gitana.CSRF_COOKIE_NAMES = ["CSRF-TOKEN", "XSRF-TOKEN"];
// the csrf token is sent over the wire using XHR and this header name
Gitana.CSRF_HEADER_NAME = "X-CSRF-TOKEN";
})(window);