(function() {
'use strict';
var app = angular.module('topcat');
app.service('tcIcat', function($sessionStorage, $rootScope, $q, helpers, tcIcatEntity, tcIcatQueryBuilder, tcCache){
this.create = function(facility){
return new Icat(facility);
};
/**
* @interface Icat
**/
function Icat(facility){
var that = this;
var cache;
var tc = facility.tc();
// Set up a dontCache filter function for zero-sized investigations, if required
var dontCache = null
if(tc.config().dontCacheZeroSizedInvestigations){
dontCache = function(key,value){
return ( value == 0 && key.startsWith("getSize:investigation") );
}
}
this.cache = function(){
if(!cache) cache = tcCache.create('icat:' + facility.config().name,dontCache);
return cache;
};
/**
* Returns the Facility that this particular Icat belongs to.
*
* @method
* @name Icat#facility
* @return {Facility}
*/
this.facility = function(){
return facility;
};
/**
* Returns the current version of this Icat.
*
* @method
* @name Icat#version
* @return {Promise<string>}
*/
this.version = function(){
var out = $q.defer();
this.get('version').then(function(data){
out.resolve(data.version);
}, function(){ out.reject(); });
return out.promise;
};
/**
* Returns and object containing the data stored within this session.
*
* @method
* @name Icat#session
* @return {object}
*/
this.session = function(){
var facilityName = facility.config().name;
if($sessionStorage.sessions && $sessionStorage.sessions[facilityName]){
return $sessionStorage.sessions[facilityName];
}
return {};
};
/**
* Refreshes the session's time to live.
*
* @method
* @name Icat#refreshSession
* @return {Promise}
*/
this.refreshSession = function(){
return this.put('session/' + this.session().sessionId);
};
this.login = helpers.overload({
/**
* Logs a user into this Icat with a username and password.
*
* @method
* @name Icat#login
* @param {string} plugin The type of authentication mechanism
* @param {object} credentials
* @return {Promise}
*/
'string, object': function(plugin, credentials){
var params = {
plugin: plugin,
credentials: []
};
_.each(credentials, function(value, name){
var credential = {};
credential[name] = value;
params.credentials.push(credential);
});
params = {json: JSON.stringify(params)};
$rootScope.$broadcast('session:changing');
return this.post('session', params).then(function(response){
if(!$sessionStorage.sessions) $sessionStorage.sessions = {};
var facilityName = facility.config().name;
var sessionId = response.sessionId;
$sessionStorage.sessions[facilityName] = { sessionId: sessionId }
return that.get('session/' + response.sessionId).then(function(response){
var username = response.userName;
$sessionStorage.sessions[facilityName].username = username;
$sessionStorage.sessions[facilityName].plugin = plugin;
var promises = [];
var name = facility.config().name;
promises.push(that.query([
"SELECT facility FROM Facility facility WHERE facility.name = ?", name
]).then(function(results){
var facility = results[0];
if(facility){
$sessionStorage.sessions[facilityName].facilityId = facility.id;
} else {
console.error("Could not find facility by name '" + name + "'");
}
}));
var idsUploadDatasetType = facility.config().idsUploadDatasetType;
if(idsUploadDatasetType){
promises.push(that.query([
"SELECT datasetType FROM DatasetType datasetType, datasetType.facility as facility",
"WHERE facility.name = ?", name,
"AND datasetType.name = ?", idsUploadDatasetType
]).then(function(results){
var datasetType = results[0];
if(datasetType){
$sessionStorage.sessions[facilityName].idsUploadDatasetTypeId = datasetType.id;
} else {
console.error("Could not find IDS upload dataset type with name '" + idsUploadDatasetType + "'");
}
}));
}
var idsUploadDatafileFormat = facility.config().idsUploadDatafileFormat;
if(idsUploadDatafileFormat){
promises.push(that.query([
"SELECT datasetType FROM DatafileFormat datasetType, datasetType.facility as facility",
"WHERE facility.name = ?", name,
"AND datasetType.name = ?", idsUploadDatafileFormat
]).then(function(results){
var datasetType = results[0];
if(datasetType){
$sessionStorage.sessions[facilityName].idsUploadDatafileFormatId = datasetType.id;
} else {
console.error("Could not find IDS upload datafile format with name '" + idsUploadDatafileFormat + "'");
}
}));
}
promises.push(facility.admin().isValidSession(sessionId).then(function(isAdmin){
$sessionStorage.sessions[facilityName].isAdmin = isAdmin;
if(isAdmin) document.cookie = "isAdmin=true";
}));
promises.push(that.query(["select user from User user where user.name = ?", username]).then(function(users){
if(users[0]){
$sessionStorage.sessions[facilityName].fullName = users[0].fullName;
} else {
$sessionStorage.sessions[facilityName].fullName = username;
}
}));
var completedDownloads = {};
_.each(tc.userFacilities(), function(facility){
promises.push(facility.user().downloads(["where download.isDeleted = false"]).then(function(downloads){
_.each(downloads, function(download){
var key = facility.config().name + ":" + download.id;
completedDownloads[key] = true;
});
}));
});
return $q.all(promises).then(function(){
$rootScope.$broadcast('session:change');
$rootScope.$broadcast('session:changed', completedDownloads);
$rootScope.$broadcast('session:add', that);
});
});
}, function(response){
// Initial POST /session failed; if response is empty, ICAT may be down
var reason = response?response.message:'ICAT may be down';
console.error('Failed to log into ICAT for ' + facility.config().name + ': ' + reason);
return $q.reject({message: 'Unable to log into ' + facility.config().name + ': ' + reason});
});
},
/**
* Logs a user into this Icat with a username and password.
*
* @method
* @name Icat#login
* @param {string} plugin The type of authentication mechanism
* @param {string} username
* @param {string} password
* @return {Promise}
*/
'string, string, string': function(plugin, username, password){
return this.login(plugin, {
username: username,
password: password
});
}
});
this.logout = helpers.overload({
/**
* Destroys a users session
*
* @method
* @name Icat#logout
* @param {boolean} isSoft if true the session will only be destroyed in the browser but not on the server
* @return Promise
*/
'boolean': function(isSoft){
var promises = [];
if(!isSoft && this.session().sessionId){
promises.push(this.delete('session/' + this.session().sessionId, {
server: facility.config().icatUrl
}));
}
$rootScope.$broadcast('session:remove', this);
delete $sessionStorage.sessions[facility.config().name];
$sessionStorage.$apply();
if(tc.adminFacilities().length == 0){
document.cookie = 'isAdmin=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
}
// Clear the facility user's cartCache so it can't leak to the next user
facility.user().clearCartCache();
return $q.all(promises).then(function(){
$rootScope.$broadcast('session:change');
}, function(){
$rootScope.$broadcast('session:change');
});
},
/**
* Destroys a users session
*
* @method
* @name Icat#logout
* @return Promise
*/
'': function(){
return this.logout(false);
}
});
this.query = helpers.overload({
/**
* Performs Icat JPQL queries and returns the results.
*
* @method
* @name Icat#query
* @param {array} query the jqpl expression
* @param {object} options {@link https://docs.angularjs.org/api/ng/service/$http#usage|as specified in the Angular documentation}
* @return {Promise<IcatEntity[]>} a deferred array of entities
* @example
*
* var investigationId = 3456;
* var defered = $q.defer();
* var page = 2;
* var pageSize = 10;
*
* tc.icat('LILS').query([
* "select dataset from Dataset dataset, dataset.investigation as investigation",
* "where id = ?", investigationId,
* "limit ?, ?", page, pageSize
* ], {
* cache: false,
* timeout: defered.promise
* }).then(function(datasets){
* console.log(datasets);
* });
*/
'array, object': function(query, options){
var defered = $q.defer();
var query = helpers.buildQuery(query);
var key = "query:" + query;
this.cache().getPromise(key, 10 * 60 * 60, function(){
return that.get('entityManager', {
sessionId: that.session().sessionId,
query: query,
server: facility.config().icatUrl
}, options);
}).then(function(results){
defered.resolve(_.map(results, function(result){
var type = _.keys(result)[0];
if(helpers.typeOf(result) != 'object' || !type) return result;
var out = result[type];
/**
* The type of entity e.g. 'investigation', 'facility' or 'facilityCycle' etc...
*
* @name IcatEntity#entityType
* @type {string}
*/
out.entityType = helpers.uncapitalize(type);
out = tcIcatEntity.create(out, facility);
return out;
}));
}, function(response){
defered.reject(response);
});
return defered.promise;
},
/**
* Performs Icat JPQL queries and returns the results.
*
* @method
* @name Icat#query
* @param {Promise} timeout if resolved will cancel the request
* @param {array} query the jqpl expression
* @return {Promise<IcatEntity[]>} a deferred array of entities
* @example
*
* var investigationId = 3456;
* var defered = $q.defer();
* var page = 2;
* var pageSize = 10;
*
* tc.icat('LILS').query(defered.promise, [
* "select dataset from Dataset dataset, dataset.investigation as investigation",
* "where id = ?", investigationId,
* "limit ?, ?", page, pageSize
* ]).then(function(datasets){
* console.log(datasets);
* });
*/
'promise, array': function(timeout, query){
return this.query(query, {timeout: timeout});
},
/**
* Performs Icat JPQL queries and returns the results.
*
* @method
* @name Icat#query
* @param {Promise} timeout if resolved will cancel the request
* @param {string} query the jqpl expression
* @return {Promise<IcatEntity[]>} a deferred array of entities
* @example
*
* var defered = $q.defer();
* var query = "select dataset from Dataset dataset, dataset.investigation as investigation where investigation.id = 2342";
*
* tc.icat('LILS').query(defered.promise, query).then(function(datasets){
* console.log(datasets);
* });
*/
'promise, string': function(timeout, query){
return this.query([query], {timeout: timeout});
},
/**
* Performs Icat JPQL queries and returns the results.
*
* @method
* @name Icat#query
* @param {array} query the jqpl expression
* @return {Promise<IcatEntity[]>} a deferred array of entities
* @example
*
* var investigationId = 3456;
* var page = 2;
* var pageSize = 10;
*
* tc.icat('LILS').query([
* "select dataset from Dataset dataset, dataset.investigation as investigation",
* "where id = ?", investigationId,
* "limit ?, ?", page, pageSize
* ]).then(function(datasets){
* console.log(datasets);
* });
*/
'array': function(query){
return this.query(query, {});
},
/**
* Performs Icat JPQL queries and returns the results.
*
* @method
* @name Icat#query
* @param {string} query the jqpl expression
* @return {Promise<IcatEntity[]>} a deferred array of entities
* @example
*
* var query = "select dataset from Dataset dataset, dataset.investigation as investigation where investigation.id = 2342";
*
* tc.icat('LILS').query(query).then(function(datasets){
* console.log(datasets);
* });
*/
'string': function(query){
return this.query([query], {});
}
});
this.write = helpers.overload({
/**
* Creates or updates entities for this Icat.
*
* @method
* @name Icat#write
* @param {object[]} entities an array of entities to be written to this Icat
* @param {object} options {@link https://docs.angularjs.org/api/ng/service/$http#usage|as specified in the Angular documentation}
* @return {Promise<number[]>} a deferred array of entity ids that have been created
*/
'array, object': function(entities, options){
return this.post('entityManager', {
sessionId: this.session().sessionId,
entities: JSON.stringify(entities)
}, options);
},
/**
* Creates or updates entities for this Icat.
*
* @method
* @name Icat#write
* @param {Promise} timeout if resolved will cancel the request
* @param {object[]} entities an array of entities to be written to this Icat
* @return {Promise<number[]>} a deferred array of entity ids that have been created
*/
'promise, array': function(timeout, entities){
return this.write(entities, {timeout: timeout});
},
/**
* Creates or updates entities for this Icat.
*
* @method
* @name Icat#write
* @param {object[]} entities an array of entities to be written to this Icat
* @return {Promise<number[]>} a deferred array of entity ids that have been created
*/
'array': function(entities){
return this.write(entities, {});
}
});
/**
* Returns a new IcatQueryBuilder for this Icat.
*
* @method
* @name Icat#queryBuilder
* @param {string} entityType the type of entities you wish to return e.g. 'dataset' or 'investigation' etc
* @return {IcatQueryBuilder}
*/
this.queryBuilder = function(entityType){
return tcIcatQueryBuilder.create(this, entityType);
};
this.getSize = helpers.overload({
/**
* Gets the total file size of a particular entity.
*
* @method
* @name Icat#getSize
* @param {string} entityType the type of entity can be 'investigation', 'dataset' or 'datafile'
* @param {number} entityId the id of the entity
* @param {object} options {@link https://docs.angularjs.org/api/ng/service/$http#usage|as specified in the Angular documentation}
* @return {Promise<number>} the defered size in bytes
*/
'string, number, object': function(entityType, entityId, options){
var key = 'getSize:' + entityType + ":" + entityId;
// Allow config to set a lifetime for Investigation entries - issue #394
var lifetime = 0;
if (entityType == 'investigation'){
var investigationLifetime = tc.config().investigationSizeCacheLifetimeSeconds;
lifetime = investigationLifetime ? investigationLifetime : 0;
}
return this.cache().getPromise(key, lifetime, function(){
var params = {
facilityName: facility.config().name,
sessionId: that.session().sessionId,
entityType: entityType,
entityId: entityId
};
options.lowPriority = true;
return facility.tc().get('user/getSize', params, options).then(function(size){
return parseInt('' + size);
});
});
},
/**
* Gets the total file size of a particular entity.
*
* @method
* @name Icat#getSize
* @param {string} entityType the type of entity can be 'investigation', 'dataset' or 'datafile'
* @param {number} entityId the id of the entity
* @param {Promise} timeout if resolved will cancel the request
* @return {Promise<number>} the defered size in bytes
*/
'string, number, promise': function(entityType, entityId, timeout){
return this.getSize(entityType, entityId, {timeout: timeout});
},
/**
* Gets the total file size of a particular entity.
*
* @method
* @name Icat#getSize
* @param {string} entityType the type of entity can be 'investigation', 'dataset' or 'datafile'
* @param {number} entityId the id of the entity
* @return {Promise<number>} the defered size in bytes
*/
'string, number': function(entityType, entityId){
return this.getSize(entityType, entityId, {});
}
});
helpers.generateRestMethods(this, facility.config().icatUrl + '/icat/');
helpers.mixinPluginMethods('icat', this);
}
});
})();