/**
* Manager for handling communication and callbacks with a Minerva
* instances (mediated by Barista).
*
* See also: {module:bbop-response-barista}
*
* @modules bbop-manager-minerva
*/
var bbop = require('bbop-core');
var registry = require('bbop-registry');
var us = require('underscore');
var class_expression = require('class-expression');
var requests = require('minerva-requests');
//var rest_manager = require('bbop-rest-manager');
var barista_response = require('bbop-response-barista');
// Aliasing.
var each = us.each;
var request = requests.request;
var request_set = requests.request_set;
/**
* A manager for handling the AJAX and registry. Initial take from
* {module:bbop-rest-manager}.
*
* @constructor
* @param {String} barista_location - string for invariant part of API
* @param {String} namespace - string for namespace of API to use
* @param {String} user_token - identifying string for the user of the manager (Barista token)
* @param {Object} engine - Remote resource manager client to use (must be an instantiated {module:bbop-rest-manager} engine)
* @param {String} mode - whether or not to have utility methods (most besides fetch_with and start_with be in); options are "sync" and "async", which correspond to internally using fetch and start respectively
* @returns {manager} a classic manager
*/
var manager = function(barista_location, namespace, user_token, engine, mode){
registry.call(this, ['prerun', // internal; anchor only
'postrun', // internal
'manager_error', // internal/external...odd
//'success', // uninformative
'merge',
'rebuild',
'meta',
'warning', // trump
'error' //trump
]);
this._is_a = 'bbop-manager-minerva';
var anchor = this;
//var url = barista_location + '/api/' + namespace + '/m3Batch';
anchor._batch_url = null;
anchor._seed_url = null;
anchor._user_token = user_token;
//
anchor._engine = engine;
anchor._mode = mode;
anchor._runner = function(resource, payload){
var ret = null;
if( anchor._mode === 'sync' ){
ret = anchor._engine.fetch(resource, payload);
}else if( anchor._mode === 'async' ){
ret = anchor._engine.start(resource, payload);
}else{
throw new Error('"mode" not set in new bbop-manager-minerva');
}
return ret;
};
// Will use this one other spot, where the user can change the
// token.
function _set_url_from_token(in_token){
var batch_url =
barista_location + '/api/' + namespace + '/m3Batch';
var seed_url =
barista_location + '/api/' + namespace + '/seed/fromProcess';
if( in_token ){
batch_url = batch_url + 'Privileged';
seed_url = seed_url + 'Privileged';
}
anchor._batch_url = batch_url;
anchor._seed_url = seed_url;
}
_set_url_from_token(user_token);
// How to deal with failure.
function _on_fail(resp, man){
var retval = null;
// See if we got any traction.
if( ! resp || ! resp.message_type() || ! resp.message() ){
// Something dark has happened, try to put something
// together.
// console.log('bad resp!?: ', resp);
var resp_seed = {
'message_type': 'error',
'message': 'deep manager error'
};
resp = new barista_response(resp_seed);
retval = resp;
}
anchor.apply_callbacks('manager_error', [resp, anchor]);
return retval;
}
anchor._engine.register('error', _on_fail);
// When we have nominal success, we still need to do some kind of
// dispatch to the proper functionality.
function _on_nominal_success(resp, man){
var retval = resp;
// Switch on message type when there isn't a complete failure.
var m = resp.message_type();
if( m === 'error' ){
// Errors trump everything.
anchor.apply_callbacks('error', [resp, anchor]);
}else if( m === 'warning' ){
// Don't really have anything for warning yet...remove?
anchor.apply_callbacks('warning', [resp, anchor]);
}else if( m === 'success' ){
var sig = resp.signal();
if( sig === 'merge' || sig === 'rebuild' || sig === 'meta' ){
//console.log('run on signal: ' + sig);
anchor.apply_callbacks(sig, [resp, anchor]);
}else{
alert('unknown signal: very bad');
}
}else{
alert('unimplemented message_type');
}
// Postrun goes no matter what.
anchor.apply_callbacks('postrun', [resp, anchor]);
return retval;
}
anchor._engine.register('success', _on_nominal_success);
///
/// Control our identity.
///
/**
* Get/set the user token.
*
* @param {String} [user_token] - string to set user token to
* @returns {String} current user token
*/
anchor.user_token = function(user_token){
// Adjust the internal token.
if( user_token ){
anchor._user_token = user_token;
}
// Make sure we're using the right URL considering how we're
// identified.
_set_url_from_token(anchor._user_token);
return anchor._user_token;
};
///
/// Actual mechanism.
///
/**
* Trigger a rebuild {module:bbop-response-barista} with a model.
*
* Intent: "query".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.get_model = function(model_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.get_model();
return anchor.request_with(reqs);
};
// /*
// * Method: get_model_ids
// *
// * Trigger meta {module:bbop-response-barista} with a list of all model
// * ids.
// *
// * Intent: "query".
// * Expect: "success" and "meta".
// *
// * @param {} // * n/a
// *
// * @returns {} // * n/a
// */
// anchor.get_model_ids = function(){
// //
// var reqs = new request_set(anchor.user_token());
// var req = new request('model', 'all-model-ids');
// reqs.add(req);
// var args = reqs.callable();
// anchor.apply_callbacks('prerun', [anchor]);
// jqm.action(anchor._batch_url, args, 'GET');
// };
/**
* Trigger meta {module:bbop-response-barista} with a list of all model
* meta-information.
*
* Intent: "query".
* Expect: "success" and "meta".
*
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.get_meta = function(){
var reqs = new request_set(anchor.user_token());
reqs.get_meta();
return anchor.request_with(reqs);
};
/**
* Trigger meta {module:bbop-response-barista} of requested
* model's undo/redo information.
*
* This will make the request whether or not the user has an okay
* token defined.
*
* Intent: "query".
* Expect: "success" and "meta".
*
* @param {String} model_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.get_model_undo_redo = function(model_id){
//
var reqs = new request_set(anchor.user_token(), model_id);
reqs.get_undo_redo();
return anchor.request_with(reqs);
};
/**
* Trigger rebuild {module:bbop-response-barista} after an attempt
* to roll back the model to "last" state.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.perform_undo = function(model_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.undo_last_model_batch();
return anchor.request_with(reqs);
};
/**
* Trigger rebuild {module:bbop-response-barista} after an attempt
* to roll forward the model to "next" state.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.perform_redo = function(model_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.redo_last_model_batch();
return anchor.request_with(reqs);
};
/**
* Trigger merge (or possibly a rebuild)
* {module:bbop-response-barista} on attempt to add a single fact
* to a model.
*
* Intent: "action".
* Expect: "success" and "merge".
*
* @param {String} model_id - string
* @param {String} source_id - string
* @param {String} target_id - string
* @param {String} rel_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_fact = function(model_id, source_id, target_id, rel_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.add_fact([source_id, target_id, rel_id]);
return anchor.request_with(reqs);
};
/**
* Trigger merge (or possibly a rebuild)
* {module:bbop-response-barista} on attempt to remove a single
* fact to a model.
*
* Intent: "action".
* Expect: "success" and "merge".
*
* @param {String} model_id - string
* @param {String} source_id - string
* @param {String} target_id - string
* @param {String} rel_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.remove_fact = function(model_id, source_id, target_id, rel_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.remove_fact([source_id, target_id, rel_id]);
return anchor.request_with(reqs);
};
/**
* Trigger merge (or possibly a rebuild)
* {module:bbop-response-barista.response} on attempt to add a
* simple composite unit (class, enabled_by, and occurs_in) to a
* model.
*
* Intent: "action".
* Expect: "success" and "merge".
*
* @param {String} model_id - string
* @param {String} cls_exp - anything taken by {module:class-expression}
* @param {String} [enabled_by_expr] - anything taken by {module:class-expression}
* @param {String} [occurs_in_expr] - anything taken by {module:class-expression}
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_simple_composite = function(model_id, cls_expr,
enabled_by_expr, occurs_in_expr){
// Minimal requirements.
var reqs = new request_set(anchor.user_token(), model_id);
var ind = reqs.add_individual(cls_expr);
// Optional set expressions.
if( enabled_by_expr ){
reqs.add_type_to_individual(
class_expression.svf(enabled_by_expr, 'RO:0002333'), ind);
}
if( occurs_in_expr ){
reqs.add_type_to_individual(
class_expression.svf(occurs_in_expr, 'occurs_in'), ind);
}
return anchor.request_with(reqs);
};
/**
* Trigger merge (or possibly a rebuild)
* {module:bbop-response-barista.response} on attempt to add a
* complex class expression to an individual in a model.
*
* Intent: "action".
* Expect: "success" and "merge".
*
* @param {String} model_id - string
* @param {String} individual_id - string
* @param {String} cls_expr - anything acceptible to {module:class-expression}
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_class_expression = function(model_id, individual_id, cls_expr){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.add_type_to_individual(cls_expr, individual_id);
return anchor.request_with(reqs);
};
/**
* Trigger merge (or possibly a rebuild) {module:bbop-response-barista}
* on attempt to remove a complex class expression from an
* individual in a model.
*
* Intent: "action".
* Expect: "success" and "merge".
*
* @param {String} model_id - string
* @param {String} individual_id - string
* @param {String} cls_expr - or anything acceptible to {module:class-expression}
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.remove_class_expression = function(model_id, individual_id, cls_expr){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.remove_type_from_individual(cls_expr, individual_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild {module:bbop-response-barista} on attempt to remove
* an individual from a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} individual_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.remove_individual = function(model_id, indv_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.remove_individual(indv_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on
* attempting to create a new model...from nothing. Or something!
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} taxon_id - *[DEPRECATED]* *[optional]* string (full ncbi)
* @param {String} class_id - *[DEPRECATED]* *[optional]* string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_model = function(taxon_id, class_id){
// Conditions taken care of by request_set.
var reqs = new request_set(anchor.user_token());
reqs.add_model({'class-id': class_id, 'taxon_id': taxon_id});
return anchor.request_with(reqs);
};
/**
* *[DEPRECATED]*
*
* Trigger a meta {module:bbop-response-barista} containing model
* export text.
*
* Intent: "action".
* Expect: "success" and "meta".
*
* @deprecated
* @param {String} model_id - string
* @param {String} [format] - string (for legacy, "gaf" or "gpad")
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.export_model = function(model_id, format){
if( typeof(format) === 'undefined' ){ format = 'default'; }
var reqs = new request_set(anchor.user_token());
var req = null;
if( format === 'gaf' ){
req = new request('model', 'export-legacy');
req.special('format', 'gaf');
}else if( format === 'gpad' ){
req = new request('model', 'export-legacy');
req.special('format', 'gpad');
}else{
// Default (non-legacy) case is simpler.
req = new request('model', 'export');
}
// Add the model to the request.
req.model(model_id);
reqs.add(req);
return anchor.request_with(reqs);
};
/**
* *[DEPRECATED]*
*
* Trigger a rebuild response {module:bbop-response-barista} for a
* new model seeded/created from the argument string.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @deprecated
* @param {String} model_string - string representation of a model
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.import_model = function(model_string){
//
var reqs = new request_set(anchor.user_token());
var req = new request('model', 'import');
req.special('importModel', model_string);
reqs.add(req);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on a
* "permanent" store operation on a model.
*
* What?! A "rebuild" and not "meta"? Yes. This allows a workflow
* where a model is created, edited, and stored all in one pass.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.store_model = function(model_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.store_model();
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* evidence addition referencing an individual in a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} indv_id - string
* @param {String} evidence_id - string
* @param {Array|String} source_ids - string or list of strings
* @param {Array|String|null} with_strs - string or list of strings or null
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_individual_evidence = function(model_id, indv_id, evidence_id,
source_ids, with_strs){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.add_evidence(evidence_id, source_ids, with_strs, indv_id, model_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* evidence addition referencing a fact in a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} source_id - string
* @param {String} target_id - string
* @param {String} rel_id - string
* @param {String} evidence_id - string
* @param {Array|String} source_ids - string or list of strings
* @param {Array|String|null} with_strs - string or list of strings or null
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_fact_evidence = function(model_id,
source_id, target_id, rel_id,
evidence_id, source_ids, with_strs){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.add_evidence(evidence_id, source_ids, with_strs,
[source_id, target_id, rel_id], model_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* evidence addition referencing an individual in a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} evidence_individual_id - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.remove_evidence = function(model_id, evidence_individual_id){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.remove_evidence(evidence_individual_id, model_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on
* updating an entities annotations to a new set.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {Object} entity - string
* @param {String} key - string
* @param {Array|String} values - string
* @param {String|null} [value_type] - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.update_annotations = function(model_id, entity,
key, values, value_type){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.update_annotations(entity, key, values, value_type, model_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* annotation addition to an individual in a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} indv_id - string
* @param {String} key - string
* @param {String} value - string
* @param {String|null} [value_type] - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_individual_annotation = function(model_id, indv_id,
key, value, value_type){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.add_annotation_to_individual(key, value, value_type, indv_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* annotation addition to a referenced fact (edge) in a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} source_id - string
* @param {String} target_id - string
* @param {String} rel_id - string
* @param {String} key - string
* @param {String} value - string
* @param {String|null} [value_type] - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_fact_annotation = function(model_id,
source_id, target_id, rel_id,
key, value, value_type){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.add_annotation_to_fact(key, value, value_type,
[source_id, target_id, rel_id]);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* annotation addition to a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} key - string
* @param {String} value - string
* @param {String|null} [value_type] - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.add_model_annotation = function(model_id, key, value, value_type){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.add_annotation_to_model(key, value, value_type);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* annotation removeal from an individual in a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} indv_id - string
* @param {String} key - string
* @param {String} value - string
* @param {String|null} [value_type] - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.remove_individual_annotation = function(model_id, indv_id,
key, value, value_type){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.remove_annotation_from_individual(key, value, value_type, indv_id);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* annotation removeal from a referenced fact (edge) in a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} source_id - string
* @param {String} target_id - string
* @param {String} rel_id - string
* @param {String} key - string
* @param {String} value - string
* @param {String|null} [value_type] - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.remove_fact_annotation = function(model_id,
source_id, target_id, rel_id,
key, value, value_type){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.remove_annotation_from_fact(key, value, value_type,
[source_id, target_id, rel_id]);
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on an
* annotation removal from a model.
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} model_id - string
* @param {String} key - string
* @param {String} value - string
* @param {String|null} [value_type] - string
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.remove_model_annotation = function(model_id, key, value, value_type){
var reqs = new request_set(anchor.user_token(), model_id);
reqs.remove_annotation_from_model(key, value, value_type);
return anchor.request_with(reqs);
};
/**
* WARNING: This is currently very very old code and is mostly
* here as a bookmark on where to restart.
*
* Trigger a rebuild response {module:bbop-response-barista} on
* attempting to create a new model with information provided by
* Capella.
*
* If you're attempting to use this, you probably want to revisit
* everything and everbody first...
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {Object} bootstrap_obj - JSON object ???
* @param {String} term2aspect - ???
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.capella_bootstrap_model = function(bootstrap_obj, term2aspect){
var reqs = new request_set(anchor.user_token());
// Just get a new model going.
var req = new request('model', 'generate-blank');
//req.special('db', db_id); // unecessary
reqs.add(req);
each(bootstrap_obj, function(ob){
// Now, for each of these, we are going to be adding
// stuff to MF instances. If there is no MF coming
// in, we are just going to use GO:0003674.
var mfs = [];
var bps = [];
var ccs = [];
each(ob['terms'], function(tid){
if( term2aspect[tid] === 'molecular_function' ){
mfs.push(tid);
}else if( term2aspect[tid] === 'biological_process' ){
bps.push(tid);
}else if( term2aspect[tid] === 'cellular_component' ){
ccs.push(tid);
}
});
// There must be this no matter what.
if( us.isEmpty(mfs) ){
mfs.push('GO:0003674');
}
// We are going to be creating instances off of the
// MFs.
each(mfs, function(mf){
var req = new request('individual', 'add');
// Add in the occurs_in from CC.
each(ccs, function(cc){
req.add_svf_expression(cc, 'occurs_in');
});
// Add in the enabled_by from entities.
each(ob['entities'], function(ent){
req.add_svf_expression(ent, 'RO:0002333');
});
});
});
// Final send-off.
return anchor.request_with(reqs);
};
/**
* Trigger a rebuild response {module:bbop-response-barista} on
* attempting to create a new model with information provided by
* a seed service.
*
* This code will
*
* Intent: "action".
* Expect: "success" and "rebuild".
*
* @param {String} process_id - the GOlr resolvable process identifier
* @param {String} taxon_id - the GOlr resolvable taxon identifier
* @returns {module:bbop-barista-response#response} barista response
*/
anchor.seed_from_process = function(process_id, taxon_id){
var reqs = new request_set(anchor.user_token());
// Just get a new model going.
var req = new request('model', 'seed-from-process');
req.special('process', process_id);
req.special('taxon', taxon_id);
reqs.add(req);
// Final send-off.
return anchor.request_with(reqs);
};
/**
* Make a custom request with your own request set.
*
* Depending on the mode that you set, in addition to running the
* callbacks as usual, it will either return a response object
* ("sync") or a deferred promise for the response object ("async").
*
* Intent: ??? - whatever you set
* Expect: "success" and ??? (depends on your request)
*
* @param {module:minerva-requests#request_set} request_set
* @param {String} [model_id] - string
* @returns {Object} the deferred Q promise for the eventual response
*/
anchor.request_with = function(request_set, model_id){
// Assembly. Using callable() here seems to cause double
// encoding, so we're doing it a little more manually.
//var args = request_set.callable();
var args = request_set.structure();
//console.log('_args', args);
var reqs = args['requests'];
var str = JSON.stringify(reqs);
//var enc = encodeURIComponent(str);
args['requests'] = str;
//console.log('_batch_url', anchor._batch_url);
//console.log('_request_set', request_set);
//console.log('_args', args);
// Take care of prerun now (postrun handled elsewhere).
anchor.apply_callbacks('prerun', [anchor]);
// Get what ever output and move on. We'll need to switch on
// whatever our target URL is.
var thing = null;
// Check if known seeding, then direct to known seeding service.
var rs = request_set.structure();
if( rs && rs['requests'] && rs['requests'][0] &&
rs['requests'][0]['operation'] &&
rs['requests'][0]['operation'] === 'seed-from-process' ){
// seed
//console.log('running to seeder');
thing = anchor._runner(anchor._seed_url, args);
}else{
// batch
thing = anchor._runner(anchor._batch_url, args);
}
return thing;
};
};
bbop.extend(manager, registry);
///
/// Exportable body.
///
module.exports = manager;