/**
* Response handler for dealing with the parsing of responses from
* Barista (enveloping Minerva).
*
* It will detect if the incoming response is structured correctly and
* give safe access to fields and properties.
*
* It is not meant to be a model for the parts in the data section,
* see the graph sections for that.
*
* @module bbop-response-barista
*/
var bbop = require('bbop-core');
var us = require('underscore');
var bbop_rest_response = require('bbop-rest-response').base;
/**
* Contructor for a Minerva REST JSON response object.
*
* The constructor argument is an object or a string.
*
* @constructor
* @param {Object|String} raw - the JSON object as a string or object
* @returns {response} response object
*/
var response = function(raw){
bbop_rest_response.call(this);
this._is_a = 'bbop-response-barista';
// Required top-level strings in the response.
// message and message_type are defined in the superclass.
this._uid = null; // initiating user
this._packet_id = null; // identify the packet
this._intention = null; // what the user wanted to do ('query', 'action')
this._reasoner_p = null; // was the reasoner used?
this._groups = null; // did groups come over the wire?
this._signal = null; // 'merge', 'rebuild', 'meta', etc.
// Optional top-level strings in the response.
this._commentary = null;
// Optional top-level objects.
// Data contains model_id, inconsistency, etc.
this._data = null;
// Start with the assumption that the response is bad, try and
// prove otherwise.
this.okay(false);
// Raw will only be provided in that cases that it makes sense.
this._raw = null;
// If we have no data coming in, there is a problem...
if( ! raw ){
this.message('empty response in handler');
this.message_type('error');
}else{
// If we do have something coming in, And it looks like
// something we might be able to deal with, do our best to
// decode it.
var itsa = bbop.what_is(raw);
if( itsa !== 'string' && itsa !== 'object' ){
// No idea what this thing is...
this.message('bad argument type in handler');
this.message_type('error');
}else{
// Try to make the string an object.
if( itsa === 'string' ){
try {
this._raw = JSON.parse(raw);
}catch(e){
// Didn't make it--chuck it to create a signal.
this._raw = null;
this.message('handler could not parse string response: ' +
raw);
this.message_type('error');
}
}else{
// Looks like somebody else got here first.
this._raw = raw;
}
// If we managed to define some kind of raw incoming data
// that is (or has been parsed to) a model, start probing
// it out to see if it is structured correctly.
if( this._raw ){
// Check required fields.
var jresp = this._raw;
// These must always be defined.
if( ! jresp['message-type'] || ! jresp['message'] ){
// Core info.
this.message_type('error');
this.message('message and message_type must always exist');
}else{
// Take out the individual optional bits for
// examination.
var cdata = jresp['commentary'] || null;
var odata = jresp['data'] || null;
// If data, object.
if( odata && bbop.what_is(odata) !== 'object' ){
// if( odata && bbop.what_is(odata) != 'object' &&
// bbop.what_is(odata) != 'array' ){
this.message('data not object');
this.message_type('error');
}else{
// If commentary, string.
if( cdata && bbop.what_is(cdata) !== 'string' ){
this.message('commentary not string');
this.message_type('error');
}else{
// Looks fine then I guess.
this.okay(true);
// Super-class.
this.message_type(jresp['message-type']);
this.message(jresp['message']);
// Plug in the other required fields.
this._uid = jresp['uid'] || 'unknown';
this._intention = jresp['intention'] || 'unknown';
this._reasoner_p = false;
if( typeof(jresp['is-reasoned']) === 'boolean' ){
this._reasoner_p = jresp['is-reasoned'];
}
if( us.isArray(jresp['provided-by']) &&
! us.isEmpty(jresp['provided-by']) ){
this._groups= jresp['provided-by'];
}
this._signal = jresp['signal'] || 'unknown';
this._packet_id = jresp['packet-id'] || 'unknown';
// Add any additional fields.
if( cdata ){ this._commentary = cdata; }
if( odata ){ this._data = odata; }
}
}
}
}
}
}
};
bbop.extend(response, bbop_rest_response);
/**
* Returns the user id (uid) for a call if it was generated my a known
* user.
*
* @returns {String|null} string or null
*/
response.prototype.user_id = function(){
var ret = null;
if( this._uid ){ ret = this._uid; }
return ret;
};
/**
* Returns the user intention for a call.
*
* @returns {String|null} string or null
*/
response.prototype.intention = function(){
var ret = null;
if( this._intention ){ ret = this._intention; }
return ret;
};
/**
* Returns whether the reasoner was used or not.
*
* @returns {Boolean} if none, then false
*/
response.prototype.reasoner_p = function(){
var ret = this._reasoner_p;
return ret;
};
/**
* Returns whether groups were used (non-null) and what they are (list
* of strings).
*
* @returns {Array|null} group(?) and what they are
*/
response.prototype.groups = function(){
var ret = this._groups;
return ret;
};
/**
* Returns whether groups were used (non-null) and what they are (list
* of strings).
*
* Alias of groups.
*
* @returns {Array|null} group(?) and what they are
*/
response.prototype.provided_by = response.prototype.groups;
/**
* Returns the server's action signal, if there was one.
*
* @returns {String|null} string or null
*/
response.prototype.signal = function(){
var ret = null;
if( this._signal ){ ret = this._signal; }
return ret;
};
/**
* Returns the response's unique id. Usful to make sure you're not
* talking to yourself in some cases.
*
* @returns {String|null} string or null
*/
response.prototype.packet_id = function(){
var ret = null;
if( this._packet_id ){ ret = this._packet_id; }
return ret;
};
/**
* Returns the commentary object (whatever that might be in any given
* case).
*
* @returns {Object|null} copy of commentary object or null
*/
response.prototype.commentary = function(){
var ret = null;
if( this._commentary ){
ret = bbop.clone(this._commentary);
}
return ret;
};
/**
* Returns the data object (whatever that might be in any given
* case). This grossly returns all response data, if any.
*
* @returns {Object|null} copy of data object or null
*/
response.prototype.data = function(){
var ret = null;
if( this._data ){
ret = bbop.clone(this._data);
}
return ret;
};
///
/// From here on out, we're in non-generic barista territory.
/// Minerva from here.
/// Model.
///
/**
* Returns the model id of the response.
*
* @returns {String|null} string or null
*/
response.prototype.model_id = function(){
var ret = null;
if( this._data && this._data['id'] ){
ret = this._data['id'];
}
return ret;
};
/**
* Returns true or false on whether or not the returned model is
* thought to be inconsistent. Starting assumption is that it is not.
*
* @returns {Boolean} true or false
*/
response.prototype.inconsistent_p = function(){
var ret = false;
if( this._data &&
typeof(this._data['inconsistent-p']) !== 'undefined' &&
this._data['inconsistent-p'] === true ){
ret = true;
}
return ret;
};
/**
* Returns true or false on whether or not the returned model is
* thought to have been modified since it's last disk save. Starting
* assumption is that it has not.
*
* @returns {Boolean} true or false
*/
response.prototype.modified_p = function(){
var ret = false;
if( this._data &&
typeof(this._data['modified-p']) !== 'undefined' &&
this._data['modified-p'] === true ){
ret = true;
}
return ret;
};
/**
* Returns a true or false depending on the existence an undo list.
*
* @returns {Boolean} boolean
*/
response.prototype.has_undo_p = function(){
var ret = false;
if( this._data && this._data['undo'] &&
us.isArray(this._data['undo']) &&
this._data['undo'].length > 0 ){
ret = true;
}
return ret;
};
/**
* Returns a true or false depending on the existence a redo list.
*
* @returns {Boolean} boolean
*/
response.prototype.has_redo_p = function(){
var ret = false;
if( this._data && this._data['redo'] &&
us.isArray(this._data['redo']) &&
this._data['redo'].length > 0 ){
ret = true;
}
return ret;
};
/**
* Returns the undo list.
*
* @returns {Array} list of undo IDs.
*/
response.prototype.undo = function(){
var ret = [];
if( this._data && this._data['undo'] && us.isArray(this._data['undo']) ){
ret = this._data['undo'];
}
return ret;
};
/**
* Returns the redo list.
*
* @returns {Array} list of redo IDs.
*/
response.prototype.redo = function(){
var ret = [];
if( this._data && this._data['redo'] && us.isArray(this._data['redo']) ){
ret = this._data['redo'];
}
return ret;
};
/**
* Returns a list of the facts in the response. Empty list if none.
*
* @returns {Array} list
*/
response.prototype.facts = function(){
var ret = [];
if( this._data && this._data['facts'] &&
us.isArray(this._data['facts']) ){
ret = this._data['facts'];
}
return ret;
};
/**
* Returns a list of owl data properties, may be used in Monarch, for
* things like type restrictions. Empty list if none.
*
* @returns {Array} list
*/
response.prototype.data_properties = function(){
var ret = [];
if( this._data && this._data['data-properties'] &&
us.isArray(this._data['data-properties']) ){
ret = this._data['data-properties'];
}
return ret;
};
/**
* Returns a list of relationships (represented like:{ "type":
* "property", "id": "BFO:0000050", "label": "part of" }) found in the
* model in the response. Empty list if none.
*
* @returns {Array} list
*/
response.prototype.properties = function(){
var ret = [];
if( this._data && this._data['properties'] &&
us.isArray(this._data['properties']) ){
ret = this._data['properties'];
}
return ret;
};
/**
* Returns the validation object. TBD.
*
* @returns {Object|Null} validation - return validation object or null.
*/
response.prototype.validation = function(){
var ret = null;
if( this._data && this._data['validation-results'] &&
us.isObject(this._data['validation-results']) ){
ret = bbop.clone(this._data['validation-results']);
}
return ret;
};
/**
* Returns boolean on whether the returned model is /completely/
* valid; true if there is no logic to probe (this is a temporary
* measure until reasoner is "always on").
*
* @returns {Boolean} p - bool
*/
response.prototype.valid_p = function(){
var ret = true;
var vres = this.validation();
if( vres && us.isObject(vres) ){
// Dig in farther to get conformance.
if( us.isBoolean(vres['is-conformant']) ){
if( vres['is-conformant'] === true ){
ret = true;
}else if( vres['is-conformant'] === false ){
ret = false;
}
}
}
return ret;
};
/**
* Returns boolean on whether the returned model is valid according to
* the owl reasoner; true if there is no logic to probe (this is a temporary
* measure until reasoner is "always on").
*
* @returns {Boolean} p - bool
*/
response.prototype.valid_owl_p = function(){
var ret = true;
var vres = this.validation();
if( vres && us.isObject(vres) ){
// Dig in farther to get conformance.
if( vres['owl-validation'] && us.isObject(vres['owl-validation']) ){
if( vres['owl-validation']['is-conformant'] === true ){
ret = true;
}else if( vres['owl-validation']['is-conformant'] === false ){
ret = false;
}
}
}
return ret;
};
/**
* Returns boolean on whether the returned model is valid according to
* ShEx shapes in Minerva; true if there is no logic to probe (this is
* a temporary measure until reasoner is "always on").
*
* @returns {Boolean} p - bool
*/
response.prototype.valid_shex_p = function(){
var ret = true;
var vres = this.validation();
if( vres && us.isObject(vres) ){
// Dig in farther to get conformance.
if( vres['shex-validation'] && us.isObject(vres['shex-validation']) ){
if( vres['shex-validation']['is-conformant'] === true ){
ret = true;
}else if( vres['shex-validation']['is-conformant'] === false ){
ret = false;
}
}
}
return ret;
};
/**
* Returns an array of objects that describe the ShEx violations
* found in a model.
*
* @returns {Array} violations or an empty list if none
*/
response.prototype.shex_violations = function(){
var ret = [];
var vres = this.validation();
if( vres && us.isObject(vres) ){
// Dig in farther to get conformance.
if( vres['shex-validation'] && us.isObject(vres['shex-validation']) &&
us.isArray(vres['shex-validation']['violations']) ){
ret = bbop.clone(vres['shex-validation']['violations']);
}
}
return ret;
};
/**
* Returns a list of the individuals in the response. Empty list if none.
*
* @returns {Array} list
*/
response.prototype.individuals = function(){
var ret = [];
if( this._data && this._data['individuals'] &&
us.isArray(this._data['individuals']) ){
ret = this._data['individuals'];
}
return ret;
};
/**
* Returns a list of the inferred_individuals in the response. Empty
* list if none.
*
* @returns {Array} list
*/
response.prototype.inferred_individuals = function(){
var ret = [];
if( this._data && this._data['individuals-i'] &&
us.isArray(this._data['individuals-i']) ){
ret = this._data['individuals-i'];
}
return ret;
};
/**
* Returns a list of the (complex) annotations found in the
* response. Sometimes not there, so check the return.
*
* @returns {Array} list
*/
response.prototype.annotations = function(){
var ret = [];
if( this._data && this._data['annotations'] &&
us.isArray(this._data['annotations']) ){
ret = this._data['annotations'];
}
return ret;
};
/**
* Returns the string of the export found in the return.
*
* @returns {String} string
*/
response.prototype.export_model = function(){
var ret = '';
if( this._data && this._data['export-model'] ){
ret = this._data['export-model'];
}
return ret;
};
///
/// Meta.
///
/**
* Returns a list of the relations found in the response. Sometimes not
* there, so check the return.
*
* This is a function mostly for meta responses.
*
* @returns {Array} list
*/
response.prototype.relations = function(){
var ret = [];
if( this._data && this._data['meta'] && this._data['meta']['relations'] &&
us.isArray(this._data['meta']['relations']) ){
ret = this._data['meta']['relations'];
}
return ret;
};
/**
* Returns a list of the evidence found in the response. Sometimes not
* there, so check the return.
*
* This is a function mostly for meta responses.
*
* @returns {Array} list
*/
response.prototype.evidence = function(){
var ret = [];
if( this._data && this._data['meta'] && this._data['meta']['evidence'] &&
us.isArray(this._data['meta']['evidence']) ){
ret = this._data['meta']['evidence'];
}
return ret;
};
/**
* Returns a list the model ids found in the response. Sometimes not
* there, so check the return.
*
* This is a function mostly for meta responses.
*
* See Also: <models_meta>
*
* @returns {Array} list
*/
response.prototype.model_ids = function(){
var ret = [];
if( this._data && this._data['meta'] && this._data['meta']['models-meta'] &&
us.isObject(this._data['meta']['models-meta']) ){
ret = us.keys(this._data['meta']['models-meta']);
}
return ret;
};
/**
* Returns a hash of the model ids to models properties found in the
* response.
*
* Sometimes not there, so check the return.
*
* WARNNG: A work in progress, but this is intended as an eventual
* replacement to model_ids.
*
* This is a function mostly for meta responses.
*
* See Also: <model_ids>
*
* @returns {Object} model ids to arrays of serialized annotation objects
*/
response.prototype.models_meta = function(){
var ret = {};
if( this._data && this._data['meta'] && this._data['meta']['models-meta'] &&
us.isObject(this._data['meta']['models-meta']) ){
ret = this._data['meta']['models-meta'];
}
return ret;
};
/**
* Returns a hash of the model ids to read-only models properties
* found in the response.
*
* Sometimes not there, so check the return.
*
* This is a function mostly for meta responses.
*
* See Also: {models_meta}
*
* @returns {Object} hash
*/
response.prototype.models_meta_read_only = function(){
var ret = {};
if( this._data && this._data['meta'] &&
this._data['meta']['models-meta-read-only'] &&
us.isObject(this._data['meta']['models-meta-read-only']) ){
ret = this._data['meta']['models-meta-read-only'];
}
return ret;
};
///
/// Exportable body.
///
module.exports = response;