UNPKG

cttv.api

Version:

Javascript interface for the Open Targets rest api

378 lines (325 loc) 12.4 kB
var apijs = require("tnt.api"); var http = require("httpplease"); var promises = require('httpplease-promises'); var withcredentials = require('./withcredentials.js'); var Promise = require('es6-promise').Promise; var json = require("httpplease/plugins/json"); jsonHttp = http.use(json).use(promises(Promise)); var jsonreq = require("httpplease/plugins/jsonrequest"); jsonReqHttp = http.use(jsonreq).use(promises(Promise)); // http = http.use(promises(Promise)); var structure = require("./structure.js"); var cttvApi = function () { var req; // can be json or not var therapeuticareas; // placeholder for caching therapeutic areas as per API response var credentials = { token : undefined, // is a promise or a string appname : "", secret : "", expiry : undefined }; var config = { verbose: false, prefix: "https://www.targetvalidation.org", version: "v3", postfix: "platform", format: "json" }; var sendCredentialsCookie = function() { jsonHttp = jsonHttp.use(withcredentials); jsonReqHttp = jsonReqHttp.use(withcredentials); }; var getToken = function () { var tokenUrl = _.url.requestToken(credentials); credentials.token = jsonHttp.get({ "url": tokenUrl }); return credentials.token; }; var _ = {}; _.call = function (myurl, data, format) { // Response format json or not if (!format) { format = "json"; } if (format === "json") { req = jsonHttp; } else { req = jsonReqHttp; } // No auth if ((!credentials.token) && (!credentials.appname) && (!credentials.secret)) { if (config.verbose) { console.log(" CttvApi running in non-authentication mode"); } if (data){ // post return req.post({ "url": myurl, "body": data, "headers": { "Accept": "*/*" } }); } return req.get({ "url" : myurl, "headers": { "Accept": "*/*" } }); } // Auth - but not token if (!credentials.token) { if (config.verbose) { console.log("No credential token, requesting one..."); } return getToken() .then(callApi) .catch(catchErr); // Auth & token } else { if (config.verbose) { console.log("Current token is: " + credentials.token); } var tokenPromise = new Promise (function (resolve) { resolve ({ body: { token: credentials.token } }); }); if (typeof credentials.token !== "string") { // The token is still a string tokenPromise = credentials.token; } return tokenPromise // .then (function () { // return callApi // // return jsonHttp.get({ // // "url" : myurl, // // "headers": { // // "Auth-token": credentials.token // // } // // }) // .catch(catchErr); .then (callApi) .catch(catchErr); // }); } function callApi (resp) { if (config.verbose) { console.log(" ======> Got a new token: " + resp.body.token); } credentials.token = resp.body.token; var headers = { "Auth-token": resp.body.token }; var myPromise; if (data) { // post myPromise = req.post ({ "url": myurl, "headers": headers, "body": data }); } else { // get myPromise = req.get ({ "url": myurl, "headers": headers }); } return myPromise; } function catchErr (err) { // Logic to deal with expired tokens switch (err.status) { case (401) : // Probably wrong credentials if (err.body.message === "authentication credentials not valid") { if (config.verbose) { console.log(" --- Received an api error -- Possibly the credentials are wrong (" + err.status + "), so I'll make the call without credentials"); } console.warn('wrong authentication appname (' + credentials.appname + ') or secret (' + credentials.secret + ') -- I will remove the credentials and try the same call again'); // Remove credentials and run in non-authentication mode credentials.appname = undefined; credentials.secret = undefined; credentials.token = undefined; return _.call(myurl, data); } break; case (419) : // Token expired if (config.verbose) { console.log(" --- Received an api error -- Possibly the token has expired (" + err.status + "), so I'll request a new one"); } if (typeof credentials.token === "string" ) { credentials.token = undefined; } return _.call(myurl, data); case (429) : // Too many calls if (config.verbose) { console.log(" --- Received an api error -- Probably too many calls made to the api (" + err.status + "), so I'll wait some time to fetch the data"); } var delayedPromise = new Promise (function (resolve) { setTimeout(function (){ if (config.verbose) { console.log(" *** trying again after receiving a too many requests error"); } resolve(_.call(myurl, data)); }, 10000); }); return delayedPromise; default: throw err; } } }; apijs(_) .getset(credentials) .getset(config) .method('sendCredentialsCookie', sendCredentialsCookie); // Utils var flat2tree = function(data){ var getPromise = function() { return new Promise (function (resolve) { resolve ({ body: { data: structure.flat2tree(data, therapeuticareas) } }); }); } // if class therapeuticareas is already popuplated, get that, otherwise fetch from API if(therapeuticareas){ return getPromise(); } else { return _.call(_.url.therapeuticAreas()) .then( function(resp){ therapeuticareas = resp.body.therapeuticareas; return getPromise(); } ) } } _.utils = {}; _.utils.flat2tree = flat2tree; // URL object _.url = {}; // prefixes var prefixFilterby = "public/evidence/filter?"; var prefixAssociations = "public/association/filter?"; var prefixSearch = "public/search?"; var prefixGene = "private/target/"; var prefixDisease = "private/disease/"; // updated from "efo" to "disease" var prefixToken = "public/auth/request_token?"; var prefixAutocomplete = "private/autocomplete?"; var prefixQuickSearch = "private/quicksearch?"; var prefixExpression = "private/target/expression?"; var prefixProxy = "proxy/generic/"; var prefixTarget = "private/target/"; // this replaces prefixGene var prefixTargetRelation = "private/relation/target/"; var prefixDiseaseRelation = "private/relation/disease/"; var prefixLogSession = "private/utils/logevent"; var prefixMultiSearch = "private/besthitsearch?"; var prefixStats = "public/utils/stats"; var prefixTargetsEnrichment = "private/enrichment/targets?"; var prefixTherapeuticAreas = "public/utils/therapeuticareas"; var prefixDrug = "private/drug/"; /* * Returns the "origin" (inspired by window.location.origin) of the url, which is * prefix/version/postfix/ */ var getOrigin = function () { return trim(config.prefix) + '/' + trim(config.version) + '/' + trim(config.postfix) + '/'; } /* * Trim / from string start and end */ var trim = function(s){ return s.replace(/^[\/]+|[\/]+$/g, ''); } _.url.gene = function (obj) { return getOrigin() + prefixGene + obj.gene_id; }; _.url.target = function (obj) { if (obj && obj.target_id) { // One target return getOrigin() + prefixTarget + obj.target_id; } // Multiple targets (optionally we can specify specific fields) return getOrigin() + prefixTarget + parseUrlParams(obj); }; _.url.disease = function (obj) { return getOrigin() + prefixDisease + obj.code; }; _.url.search = function (obj) { return getOrigin() + prefixSearch + parseUrlParams(obj); }; _.url.associations = function (obj) { return getOrigin() + prefixAssociations + parseUrlParams(obj); }; _.url.filterby = function (obj) { return getOrigin() + prefixFilterby + parseUrlParams(obj); }; _.url.requestToken = function (obj) { return getOrigin() + prefixToken + "app_name=" + obj.appname + "&secret=" + obj.secret + (credentials.expiry ? ("&expiry=" + credentials.expiry) : "" ); }; _.url.autocomplete = function (obj) { return getOrigin() + prefixAutocomplete + parseUrlParams(obj); }; _.url.quickSearch = function (obj) { return getOrigin() + prefixQuickSearch + parseUrlParams(obj); }; _.url.expression = function (obj) { return getOrigin() + prefixExpression + parseUrlParams(obj); }; _.url.bestHitSearch = function (obj) { return getOrigin() + prefixMultiSearch + parseUrlParams(obj); }; _.url.proxy = function (obj) { return getOrigin() + prefixProxy + obj.url; }; _.url.targetRelation = function(obj){ return getOrigin() + prefixTargetRelation + obj.id + '?' + parseUrlParams(obj); }; _.url.diseaseRelation = function(obj){ var id = obj.id; delete(obj.id); return getOrigin() + prefixDiseaseRelation + id + "?" + parseUrlParams(obj); }; _.url.logSession = function (obj) { return getOrigin() + prefixLogSession + '?' + parseUrlParams(obj); }; _.url.stats = function () { return getOrigin() + prefixStats; }; _.url.targetsEnrichment = function (obj) { return getOrigin() + prefixTargetsEnrichment + parseUrlParams(obj); }; _.url.therapeuticAreas = function () { return getOrigin() + prefixTherapeuticAreas; }; _.url.drug = function (obj) { return getOrigin() + prefixDrug + obj.id; }; /** * This takes a params object and returns the params concatenated in a string. * If a parameter is an array, it adds each item, all with hte same key. * Example: * obj = {q:'braf',size:20,filters:['id','pvalue']}; * console.log( parseUrlParams(obj) ); * // prints "q=braf&size=20&filters=id&filters=pvalue" */ var parseUrlParams = function(obj){ var opts = []; for(var i in obj){ if( obj.hasOwnProperty(i)){ if(obj[i].constructor === Array){ opts.push(i+"="+(obj[i].join("&"+i+"="))); } else { opts.push(i+"="+obj[i]); } } } return opts.join("&"); }; return _; }; module.exports = cttvApi;