/**
* Copyright© 2016, Oracle and/or its affiliates. All rights reserved.
*/
/**
* Represents a mobile backend in Oracle Mobile Cloud Service and provides access to all capabilities of the backend.
* Callers should use MobileBackendManager's [getMobileBackend()]{@link MobileBackendManager#getMobileBackend} method.
* @constructor
* @global
*/
function MobileBackend(manager, name, config, platform, utils, logger, persistence) {
var _this = this;
var AUTHENTICATION_TYPES = utils.AUTHENTICATION_TYPES;
var PLATFORM_PATH = 'mobile/platform';
var CUSTOM_CODE_PATH = 'mobile/custom';
var HEADERS = utils.HEADERS;
this._config = config;
this._baseUrl = utils.validateConfiguration(this._config.baseUrl);
var _authenticationType = null;
this._getCustomCodeUri = function(path){
var url = "/" + CUSTOM_CODE_PATH;
if (strEndsWith(path,"/")) {
path = path.slice(0, -1);
}
return url + '/' + path;
};
/**
* The name of the MobileBackend as read from the configuration.
* @type {String}
* @name MobileBackend#name
* @readonly
*/
this.name = name;
/**
* Get current authorization object.
* @returns {Authorization}
*/
this.authorization = null;
/**
* Current authorization object.
* @type {Authorization}
* @name MobileBackend#Authorization
* @readonly
* @deprecated Will be removed in next version. Use {@link MobileBackend#authorization} instead.
*/
Object.defineProperty(this, "Authorization", {
get: function() {
return _this.authorization;
}
});
/**
* Returns the Diagnostics object that enables end-end debugging across application and cloud.
* @returns {Diagnostics}
*/
this.diagnostics = new Diagnostics(platform, utils);
/**
* Returns the Diagnostics object that enables end-end debugging across application and cloud.
* @type {Diagnostics}
* @name MobileBackend#Diagnostics
* @deprecated Will be deleted in next version. Use {@link MobileBackend#diagnostics} instead.
*/
Object.defineProperty(this, "Diagnostics", {
get: function() {
return _this.diagnostics;
}
});
/**
* Returns the CustomCode object that enables calls to custom APIs.
* @returns {CustomCode}
*/
this.customCode = new CustomCode(this, utils, platform);
/**
* Returns the CustomCode object that enables calls to custom APIs.
* @type {CustomCode}
* @name MobileBackend#CustomCode
* @readonly
* @deprecated Will be deleted in next version. Use {@link MobileBackend#customCode} instead.
*/
Object.defineProperty(this, "CustomCode", {
get: function() {
return _this.customCode;
}
});
/**
* Returns the Analytics object that enables capture of mobile analytics events.
* @returns {Analytics}
*/
this.analytics = new Analytics(this, platform, utils, logger);
/**
* Returns the Analytics object that enables capture of mobile analytics events.
* @type {Analytics}
* @name MobileBackend#Analytics
* @readonly
* @deprecated Will be deleted in next version. Use {@link MobileBackend#analytics} instead.
*/
Object.defineProperty(this, "Analytics", {
get: function() {
return _this.analytics;
}
});
/**
* Returns the Storage object that provides cloud-based object storage capabilities.
* @returns {Storage}
*/
this.storage = new Storage(this, utils, platform, logger);
/**
* Returns the Storage object that provides cloud-based object storage capabilities.
* @type {Storage}
* @name MobileBackend#Storage
* @readonly
* @deprecated Will be deleted in next version. Use {@link MobileBackend#storage} instead.
*/
Object.defineProperty(this, "Storage", {
get: function() {
return _this.storage;
}
});
if(persistence) {
/**
* Returns the Synchronization object that provides caching and synchronization capabilities.
* @readonly
* @name MobileBackend#synchronization
* @type {Synchronization}
* @deprecated Will be deleted in next version. Use {@link MobileBackend#synchronization} instead.
*/
Object.defineProperty(this, "Synchronization", {
get: function () {
return _this.synchronization;
}
});
/**
* Returns the Synchronization object that provides caching and synchronization capabilities.
* @returns {Synchronization}
*/
this.synchronization = new Synchronization(manager, this, this._config.synchronization, utils, platform, persistence);
}
if(Notifications){
/**
* Returns the Notifications object that provides notification capabilities.
* @returns {Notifications}
*/
this.notifications = new Notifications(this, utils, platform, logger);
/**
* Returns the Notifications object that provides notification capabilities.
* @type {Notifications}
* @name MobileBackend#Notifications
* @readonly
* @deprecated Will be deleted in next version. Use {@link MobileBackend#notifications} instead.
*/
Object.defineProperty(this, "Notifications", {
get: function() {
return _this.notifications;
}
});
}
/**
* Returns an instance of the application configuration object.
* Callers can download the configuration from the service by invoking loadAppConfig().
* @returns {Object}
*/
this.appConfig = {};
/**
* Returns an instance of the application configuration object.
* Callers can download the configuration from the service by invoking loadAppConfig().
* @deprecated Will be deleted in next version. Use {@link MobileBackend#appConfig} instead.
* @name MobileBackend#AppConfig
* @readonly
* @type {Object}
*/
Object.defineProperty(this, "AppConfig", {
get: function() {
return _this.appConfig;
}
});
/**
* Constructs a full URL by prepending the prefix for platform API REST endpoints to the given endpoint path.
* @param path {String} The relative path of the endpoint following the platform prefix, i.e. {BaseUrl}/mobile/platform.
* @returns {String} The full URL.
*/
this.getPlatformUrl = function (path) {
var url = _this._config.baseUrl;
// dev instance hack, replace port ends with 1 with 7777
if(_authenticationType == "ssoAuth" && strEndsWith(_this._config.baseUrl,"1")){
url = url.substring(0, url.length - 4) + "7777";
}
url = utils.validateConfiguration(url) + "/" + PLATFORM_PATH;
if (!strEndsWith(url, "/")) {
url += "/";
}
return url + path;
};
/**
* Constructs a full URL by prepending the prefix for custom API REST endpoints to the given endpoint path.
* @param path {String} The relative path of the endpoint following the platform prefix, i.e. {BaseUrl}/mobile/custom.
* @returns {String} The full URL.
*/
this.getCustomCodeUrl = function (path) {
return utils.validateConfiguration(_this._config.baseUrl) + _this._getCustomCodeUri(path);
};
/**
* Constructs a full URL, including the prefix, for the OAuth token endpoint.
* @returns {String} The full URL for the OAuth token endpoint.
*/
this.getOAuthTokenUrl = function () {
var tokenUri = utils.validateConfiguration(this._config.authorization.oAuth.tokenEndpoint);
if(!strEndsWith(tokenUri,"/")) {
tokenUri += "/"
}
return tokenUri;
};
/**
* Constructs a full URL, including the prefix, for the SSO token endpoint.
* @returns {String} The full URL for the SSO token endpoint.
*/
this.getSSOAuthTokenUrl = function () {
var tokenUri = utils.validateConfiguration(_this._config.authorization.ssoAuth.tokenEndpoint);
if(!strEndsWith(tokenUri,"/")) {
tokenUri += "/"
}
return tokenUri;
};
/**
* Populates auth and diagnostics HTTP headers for making REST calls to a mobile backend.
* @param [headers] {Object} An optional object with which to populate with the headers.
* @returns {Object} The headers parameter that is passed in. If not provided, a new object with the populated
* headers as properties of that object is created.
*/
this.getHttpHeaders = function (module, headers) {
if (!headers) {
headers = {};
}
_this.diagnostics._getHttpHeaders(headers);
if(_this.authorization) {
if (_this.authorization._getIsAuthorized() && _this.authorization._getIsAnonymous()) {
_this.authorization._getAnonymousHttpHeaders(headers);
}
else {
_this.authorization._getHttpHeaders(headers);
}
}
headers[HEADERS.ORACLE_MOBILE_CLIENT_SDK_INFO] = this.getClientSDKInfoHeader(module);
return headers;
};
this.getClientSDKInfoHeader = function(module){
var infoHeader = manager.platform.isCordova ? utils.PLATFORM_NAMES.CORDOVA : utils.PLATFORM_NAMES.JAVASCRIPT;
infoHeader += ' ' + manager.mcsVersion;
infoHeader += module && module !== '' ? ' [' + module + ']' : '';
return infoHeader;
};
/**
* Returns the Authentication type.
* @return {String} Authentication type
* @deprecated Will be deleted in next version. Use {@link MobileBackend#getAuthenticationType} instead.
*/
this.getAuthenticationTypeVariable = function(){
return _authenticationType;
};
/**
* Sets Authentication variable for MobileBackend.
* @param type
* @deprecated Will be deleted in next version. Use {@link MobileBackend#setAuthenticationType} instead.
*/
this.setAuthenticationTypeVariable = function(type){
_authenticationType = type;
};
/**
* Returns the Authentication type.
* @return {String} Authentication type
*/
this.getAuthenticationType = function(){
return _authenticationType;
};
/**
* Returns the Authorization object that provides authorization capabilities and access to user properties.
* @param {string} type.
* For Basic Authentication, you would specify "basicAuth" to use the Basic Authentication security schema.
* For OAuth authentication, you would specify "oAuth" to use OAuth Authentication security schema.
* If you put any type other than those two, it will throw an Exception stating that the type of Authentication that you provided
* is not supported at this time.
* @type {Authorization}
* @example <caption>Example usage of mobileBackend.setAuthenticationType()</caption>
* @example var mobileBackend = mcs.mobileBackendManager.getMobileBackend('YOUR_BACKEND_NAME');
* @example mobileBackend.setAuthenicationType("basicAuth");
* //Basic Authorization schema
* @example mobileBackend.setAuthenicationType("oAuth");
* //OAuth Authorization schema
* @example mobileBackend.setAuthenicationType("facebookAuth");
* //Facebook Authorization schema
* @example mobileBackend.setAuthenicationType("ssoAuth");
* //Single Sign On Authorization schema
* @example mobileBackend.setAuthenicationType("tokenAuth");
* //Token Exchange Authorization schema
*/
this.setAuthenticationType = function(type) {
var authType = utils.validateConfiguration(type);
_this.authorization = null;
if (!_this._config.authorization.hasOwnProperty(authType)) {
throw logger.Exception("No Authentication Type called " + type +
" is defined in MobileBackendManager.config " + "\n" +
"check MobileBackendManager.config in authorization object for the following objects:" + "\n" +
AUTHENTICATION_TYPES.BASIC + "\n" +
AUTHENTICATION_TYPES.OAUTH + "\n"+
AUTHENTICATION_TYPES.FACEBOOK + "\n"+
AUTHENTICATION_TYPES.TOKEN + "\n"+
AUTHENTICATION_TYPES.SSO);
}
if (_this.authorization && _this.authorization._getIsAuthorized()) {
_this.authorization.logout();
}
if (authType === AUTHENTICATION_TYPES.BASIC) {
_this.authorization = new BasicAuthorization(_this._config.authorization.basicAuth, _this, _this._config.applicationKey, utils, platform, logger);
logger.info( "Your Authentication type: " + authType);
_authenticationType = authType;
}
else if (authType === AUTHENTICATION_TYPES.OAUTH) {
_this.authorization = new OAuthAuthorization(_this._config.authorization.oAuth, _this, _this._config.applicationKey, utils, platform, logger);
logger.info( "Your Authentication type: " + authType);
_authenticationType = authType;
}
else if(authType === AUTHENTICATION_TYPES.FACEBOOK){
_this.authorization = new FacebookAuthorization(_this._config.authorization.facebookAuth,_this, _this._config.applicationKey, utils, platform, logger);
logger.info( "Your Authentication type: " + authType);
_authenticationType = authType;
}
else if(authType === AUTHENTICATION_TYPES.SSO){
_this.authorization = new SSOAuthorization(_this._config.authorization.ssoAuth, _this, _this._config.applicationKey, utils, platform, logger);
logger.info( "Your Authentication type: " + authType);
_authenticationType = authType;
} else if(authType === AUTHENTICATION_TYPES.TOKEN){
_this.authorization = new ExternalTokenExchangeAuthorization(_this._config.authorization.tokenAuth, _this, _this._config.applicationKey, utils, platform, logger);
logger.info( "Your Authentication type: " + authType);
_authenticationType = authType;
}
return _this.authorization;
};
/**
* Callback invoked after downloading the application configuration.
* @callback MobileBackend~appConfigSuccessCallback
* @param statusCode {Number} Any HTTP status code returned from the server, if available.
* @param appConfig {Object} The downloaded application configuration object.
* @deprecated Use promises instead
*/
/**
* Callback invoked on an error while downloading the application configuration.
* @callback MobileBackend~errorCallback
* @param statusCode {Number} Any HTTP status code returned from the server, if available.
* @param message {String} The HTTP payload from the server, if available, or an error message.
* @deprecated Use promises instead
*/
/**
* Downloads the configuration from the service. The AppConfig property will contain the downloaded configuration.
* @param [successCallback] {MobileBackend~appConfigSuccessCallback} Optional callback invoked on success (deprecated use promises instead).
* @param [errorCallback] {MobileBackend~errorCallback} Optional callback invoked on failure (deprecated use promises instead).
* @return {Promise.<NetworkResponse|NetworkResponse>}
*/
this.loadAppConfig = function(successCallback, errorCallback) {
if (!_this.authorization._getIsAuthorized()) {
return _this.authorization.authenticateAnonymous()
.then(loadAppConfig)
.then(loadAppConfigSuccess, loadAppConfigFail);
} else {
return loadAppConfig()
.then(loadAppConfigSuccess, loadAppConfigFail);
}
function loadAppConfigSuccess(response){
if(successCallback){
successCallback(response.statusCode, response.data);
}
return response;
}
function loadAppConfigFail(response){
if(errorCallback != null) {
errorCallback(response.statusCode, response.data);
} else {
return Promise.reject(response);
}
}
function loadAppConfig() {
var headers = _this.getHttpHeaders(utils.MODULE_NAMES.APP_CONFIG);
headers["Content-Type"] = "application/json";
return platform.invokeService({
method: 'GET',
url: _this.getPlatformUrl("appconfig/client"),
headers: headers
}).catch(invokeServiceFail);
function invokeServiceFail(response){
logger.error("App config download failed! with status code: " + response.statusCode);
return Promise.reject(response);
}
}
};
// private methods
/*
* Checks to see if the string ends with a suffix.
* @return {boolean}
*/
function strEndsWith(str, suffix) {
return str.match(suffix + '$') == suffix;
}
}