UNPKG

59.1 kBJavaScriptView Raw
1/// <reference path="../typings/index.d.ts" />
2// TODO use a cache, such as
3// https://github.com/levelgraph/levelgraph
4import "source-map-support/register";
5if (!global.hasOwnProperty("XMLHttpRequest")) {
6 global.XMLHttpRequest = require("xhr2");
7}
8import { curry, negate, uniq } from "lodash/fp";
9import { camelCase, defaultsDeep, fill, invert, isArray, isEmpty, isNaN, isNull, isString, isUndefined, omitBy, toPairs, zip } from "lodash";
10import { Observable } from "rxjs/Observable";
11import "rxjs/add/observable/dom/ajax";
12import "rxjs/add/observable/empty";
13import "rxjs/add/observable/forkJoin";
14import "rxjs/add/observable/from";
15import "rxjs/add/observable/throw";
16import "rxjs/add/observable/zip";
17import "rxjs/add/operator/buffer";
18import "rxjs/add/operator/bufferWhen";
19import "rxjs/add/operator/catch";
20import "rxjs/add/operator/concatAll";
21import "rxjs/add/operator/debounceTime";
22import "rxjs/add/operator/delay";
23import "rxjs/add/operator/distinctUntilChanged";
24import "rxjs/add/operator/do";
25import "rxjs/add/operator/filter";
26import "rxjs/add/operator/find";
27import "rxjs/add/operator/mergeMap";
28import "rxjs/add/operator/map";
29import "rxjs/add/operator/multicast";
30import "rxjs/add/operator/publishReplay";
31import "rxjs/add/operator/race";
32import "rxjs/add/operator/reduce";
33import "rxjs/add/operator/skip";
34import "rxjs/add/operator/toArray";
35import "rx-extra/add/operator/throughNodeStream";
36import { Subject } from "rxjs/Subject";
37import { TSVGetter } from "./spinoffs/TSVGetter";
38import { dataTypeParsers } from "./spinoffs/dataTypeParsers";
39import { arrayify } from "./spinoffs/jsonld-utils";
40const VError = require("verror");
41const BDB = "http://vocabularies.bridgedb.org/ops#";
42const BIOPAX = "http://www.biopax.org/release/biopax-level3.owl#";
43const IDENTIFIERS = "http://identifiers.org/";
44const OWL = "http://www.w3.org/2002/07/owl#";
45const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
46const CSV_OPTIONS = { objectMode: true, delimiter: "\t" };
47// time to wait for no new calls to xrefs() before we
48// batch up all calls in the queue and send to xrefsBatch()
49const XREF_REQUEST_DEBOUNCE_TIME = 10; // ms
50const XREF_REQUEST_CHUNK_SIZE = 100;
51const BRIDGE_DB_REPO_CDN = "https://raw.githubusercontent.com/bridgedb/BridgeDb/";
52const BRIDGE_DB_COMMIT_HASH = "465f9f944d09cefbb167eceb9c69499a764100a2";
53export const CONFIG_DEFAULT = {
54 baseIri: "https://webservice.bridgedb.org/",
55 context: [
56 BRIDGE_DB_REPO_CDN,
57 BRIDGE_DB_COMMIT_HASH,
58 "/org.bridgedb.bio/resources/org/bridgedb/bio/jsonld-context.jsonld"
59 ].join(""),
60 dataSourcesMetadataHeadersIri: [
61 BRIDGE_DB_REPO_CDN,
62 BRIDGE_DB_COMMIT_HASH,
63 "/org.bridgedb.bio/resources/org/bridgedb/bio/datasources_headers.txt"
64 ].join(""),
65 dataSourcesMetadataIri: [
66 BRIDGE_DB_REPO_CDN,
67 BRIDGE_DB_COMMIT_HASH,
68 "/org.bridgedb.bio/resources/org/bridgedb/bio/datasources.txt"
69 ].join(""),
70 http: {
71 timeout: 4 * 1000,
72 retryLimit: 2,
73 retryDelay: 3 * 1000
74 }
75};
76// these properties can be trusted to
77// uniquely identify a data source.
78const DATASOURCE_ID_PROPERTIES = [
79 "id",
80 "miriamUrn",
81 "conventionalName",
82 "preferredPrefix",
83 "systemCode"
84];
85const IRI_TO_NAME = {
86 "http://www.w3.org/1999/02/22-rdf-syntax-ns#about": "id",
87 "http://identifiers.org/idot/preferredPrefix": "preferredPrefix",
88 "http://identifiers.org/miriam.collection/": "miriamUrn"
89};
90const NAME_TO_IRI = invert(IRI_TO_NAME);
91/**
92 * miriamUrnToIdentifiersIri
93 *
94 * @param {string} miriamUrn
95 * @return {string} e.g., "http://identifiers.org/ncbigene/"
96 */
97function miriamUrnToIdentifiersIri(miriamUrn) {
98 const preferredPrefix = miriamUrnToPreferredPrefix(miriamUrn);
99 if (preferredPrefix) {
100 return IDENTIFIERS + preferredPrefix + "/";
101 }
102}
103/**
104 * miriamUrnToPreferredPrefix
105 *
106 * @param {string} uri, e.g., "urn:miriam:ncbigene"
107 * @return {string} preferredPrefix from identifiers.org/Miriam, e.g., "ncbigene"
108 */
109function miriamUrnToPreferredPrefix(miriamUrn) {
110 // Make sure it's actually an identifiers.org namespace,
111 // not a BridgeDb system code:
112 if (miriamUrn.indexOf("urn:miriam:") > -1) {
113 return miriamUrn.substring(11, miriamUrn.length);
114 }
115}
116export class BridgeDb {
117 constructor(config = CONFIG_DEFAULT) {
118 this.convertXrefDataSourceTo = curry((targetType, input) => {
119 let bridgeDb = this;
120 return bridgeDb.dataSourceMappings$
121 .map(function (mapping) {
122 return !!mapping[input] && mapping[input][targetType];
123 })
124 .catch(err => {
125 throw new VError(err, "calling bridgedb.convertXrefDataSourceTo");
126 });
127 });
128 this.identifyHeaderNameForXrefDataSource = (input) => {
129 let bridgeDb = this;
130 return bridgeDb.dataSourceMappings$
131 .map(mapping => mapping[input])
132 .filter(negate(isEmpty))
133 .map(dataSource => {
134 return toPairs(dataSource)
135 .filter(([key, value]) => value === input)
136 .map(([key, value]) => key)
137 .reduce(function (acc, key) {
138 // we want to return the IRI, if it's available.
139 return acc.length > key.length ? acc : key;
140 });
141 })
142 .catch(err => {
143 throw new VError(err, "calling bridgedb.identifyHeaderNameForXrefDataSource");
144 });
145 };
146 this.dataSourceProperties = (input) => {
147 let bridgeDb = this;
148 return bridgeDb.dataSourceMappings$
149 .map(mapping => mapping[input])
150 .catch(err => {
151 throw new VError(err, "calling bridgedb.dataSourceProperties");
152 });
153 };
154 this.parseXrefRow = ([xrefIdentifier, dataSourceConventionalName, symbol]) => {
155 let bridgeDb = this;
156 if (!xrefIdentifier || !dataSourceConventionalName) {
157 return Observable.empty();
158 }
159 return bridgeDb.dataSourceMappings$
160 .map(mapping => mapping[dataSourceConventionalName])
161 .map(function (dataSource) {
162 let xref = {
163 xrefIdentifier: xrefIdentifier,
164 isDataItemIn: dataSource
165 };
166 if (symbol) {
167 xref.symbol = symbol;
168 }
169 if (dataSource.hasOwnProperty("id")) {
170 xref.id = encodeURI(dataSource.id + xref.xrefIdentifier);
171 }
172 return xref;
173 });
174 };
175 this.xrefsBatch = (organism, oneOrMoreXrefDataSources, xrefIdentifiers, desiredXrefDataSourceOrSources) => {
176 let bridgeDb = this;
177 const desiredXrefDataSources = arrayify(desiredXrefDataSourceOrSources);
178 const dataSourceFilterParamSection = desiredXrefDataSources.length === 1
179 ? "?dataSource=" + desiredXrefDataSources[0]
180 : "";
181 const xrefDataSources = isArray(oneOrMoreXrefDataSources)
182 ? oneOrMoreXrefDataSources
183 : fill(new Array(xrefIdentifiers.length), oneOrMoreXrefDataSources);
184 const convertXrefDataSourceToConventionalName = bridgeDb.convertXrefDataSourceTo("conventionalName");
185 const callString = `Called xrefsBatch(
186 ${organism},
187 ${oneOrMoreXrefDataSources},
188 ${xrefIdentifiers},
189 ${desiredXrefDataSourceOrSources}
190)`;
191 const postURL = bridgeDb.config.baseIri +
192 organism +
193 "/xrefsBatch" +
194 dataSourceFilterParamSection;
195 const inputXrefDataSourceHeaderName$ = Observable.from(xrefDataSources)
196 .mergeMap(function (xrefDataSource) {
197 return bridgeDb.identifyHeaderNameForXrefDataSource(xrefDataSource);
198 })
199 .find(isString);
200 const desiredXrefDataSourceHeaderName$ = isEmpty(desiredXrefDataSources)
201 ? inputXrefDataSourceHeaderName$
202 : Observable.from(desiredXrefDataSources)
203 .mergeMap(function (xrefDataSource) {
204 return bridgeDb.identifyHeaderNameForXrefDataSource(xrefDataSource);
205 })
206 .find(isString);
207 const dataSourceConventionalNames$ = Observable.from(xrefDataSources)
208 .mergeMap(function (xrefDataSource) {
209 return convertXrefDataSourceToConventionalName(xrefDataSource);
210 })
211 .toArray();
212 return Observable.forkJoin(inputXrefDataSourceHeaderName$, desiredXrefDataSourceHeaderName$, dataSourceConventionalNames$).mergeMap(function ([inputXrefDataSourceHeaderName, desiredXrefDataSourceHeaderName, dataSourceConventionalNames]) {
213 // TODO: find out how we're getting duplicate rows in the body.
214 // For at least one example, see RefSeqSample.tsv in test dir.
215 // It has duplicates.
216 const body = uniq(zip(xrefIdentifiers, dataSourceConventionalNames)
217 .filter(pair => !!pair[1])
218 .map(x => x.join("\t"))).join("\n");
219 if (isEmpty(body.replace(/[\ \n\t]/g, ""))) {
220 return Observable.throw(new Error(`Error: body is empty. ${callString}`));
221 }
222 const convertXrefDataSourceToInputFormat = bridgeDb.convertXrefDataSourceTo(inputXrefDataSourceHeaderName);
223 const convertXrefDataSourceToDesiredInputFormat = bridgeDb.convertXrefDataSourceTo(desiredXrefDataSourceHeaderName);
224 return bridgeDb
225 .getTSV(postURL, "POST", body)
226 .mergeMap(function (xrefStringsByInput) {
227 const inputXrefIdentifier = xrefStringsByInput[0];
228 const inputXrefDataSource = xrefStringsByInput[1];
229 const xrefsString = xrefStringsByInput[2];
230 // NOTE: splitting by comma, e.g.:
231 // 'T:GO:0031966,Il:ILMN_1240829' -> ['T:GO:0031966', 'Il:ILMN_1240829']
232 return Observable.from(xrefsString.split(","))
233 .mergeMap(function (xrefString) {
234 if (xrefString === "N/A") {
235 return Observable.empty();
236 }
237 // NOTE: splitting by FIRST colon only, e.g.:
238 // 'T:GO:0031966' -> ['T', 'GO:0031966']
239 const [returnedXrefDataSource, returnedXrefIdentifier] = xrefString.split(/:(.+)/);
240 return convertXrefDataSourceToDesiredInputFormat(returnedXrefDataSource).map(function (desiredXrefDataSource) {
241 return {
242 xrefDataSource: desiredXrefDataSource,
243 xrefIdentifier: returnedXrefIdentifier
244 };
245 });
246 })
247 .filter(({ xrefDataSource }) => {
248 return (!isEmpty(xrefDataSource) &&
249 (desiredXrefDataSources.length === 0 ||
250 desiredXrefDataSources.indexOf(xrefDataSource) > -1));
251 })
252 .toArray()
253 .mergeMap(function (xrefs) {
254 if (desiredXrefDataSources.length > 0) {
255 // Sort xrefs in the order matching the order that the user specified
256 // in desiredXrefDataSource1, desiredXrefDataSource2, ...
257 xrefs.sort(function (a, b) {
258 const aIndex = desiredXrefDataSources.indexOf(a.xrefDataSource);
259 const bIndex = desiredXrefDataSources.indexOf(b.xrefDataSource);
260 if (aIndex < bIndex) {
261 return -1;
262 }
263 else if (aIndex > bIndex) {
264 return 1;
265 }
266 else {
267 return 0;
268 }
269 });
270 }
271 return convertXrefDataSourceToInputFormat(inputXrefDataSource).map(function (inputXrefDataSource) {
272 return {
273 organism,
274 inputXrefDataSource,
275 inputXrefIdentifier,
276 xrefs,
277 // NOTE: return desiredXrefDataSources for use in xrefsResponseQueue
278 desiredXrefDataSources
279 };
280 });
281 });
282 })
283 .catch(null, function (err) {
284 throw new VError(err, `Error: ${callString}`);
285 });
286 });
287 };
288 let bridgeDb = this;
289 defaultsDeep(config, CONFIG_DEFAULT);
290 bridgeDb.config = config;
291 var xrefsRequestQueue = (bridgeDb.xrefsRequestQueue = new Subject());
292 var debounceSignel = xrefsRequestQueue.debounceTime(XREF_REQUEST_DEBOUNCE_TIME);
293 bridgeDb.xrefsResponseQueue = xrefsRequestQueue
294 .filter(({ organism, xrefDataSource, xrefIdentifier }) => !isEmpty(organism) &&
295 !isEmpty(xrefDataSource) &&
296 !isEmpty(xrefIdentifier))
297 /* TODO should we use this? It doesn't seem to work, and we could just use caching.
298 .distinctUntilChanged(function(
299 a: { xrefDataSource; xrefIdentifier },
300 b: { xrefDataSource; xrefIdentifier }
301 ) {
302 return JSON.stringify(a) === JSON.stringify(b);
303 })
304 //*/
305 //.buffer(Observable.race(debounceSignel, xrefsRequestQueue.skip(2000)))
306 .bufferWhen(() => Observable.race(debounceSignel, xrefsRequestQueue.skip(2000)))
307 .filter(x => !isEmpty(x))
308 .mergeMap(function (inputs) {
309 const firstInput = inputs[0];
310 const organism = firstInput.organism;
311 const xrefDataSources = inputs.map(input => input.xrefDataSource);
312 const xrefIdentifiers = inputs.map(input => input.xrefIdentifier);
313 const desiredXrefDataSources = firstInput.desiredXrefDataSources;
314 return bridgeDb.xrefsBatch(organism, xrefDataSources, xrefIdentifiers, desiredXrefDataSources);
315 })
316 .multicast(new Subject());
317 // toggle from cold to hot
318 bridgeDb.xrefsResponseQueue.connect();
319 const getTSV = (bridgeDb.getTSV = new TSVGetter(config.http).get);
320 const dataSourcesMetadataHeaders$ = getTSV(config.dataSourcesMetadataHeadersIri).map(function (fields) {
321 const id = fields[4];
322 return {
323 // NOTE: the column number could be confusing, because it's one-based,
324 // so I'll just use the index instead and ignore the column number.
325 //column: parseFloat(fields[0]),
326 header: fields[1],
327 description: fields[2],
328 example_entry: fields[3],
329 id: id,
330 name: IRI_TO_NAME.hasOwnProperty(id)
331 ? IRI_TO_NAME[id]
332 : id.split(/[\/|#]/).pop(),
333 "http://www.w3.org/1999/02/22-rdf-syntax-ns#datatype": fields[5]
334 };
335 });
336 bridgeDb.dataSourceMappings$ = Observable.forkJoin(dataSourcesMetadataHeaders$.toArray(), getTSV(config.dataSourcesMetadataIri).toArray())
337 .mergeMap(function (results) {
338 var metadataByColumnIndex = results[0];
339 var rows = results[1];
340 return Observable.from(rows).map(function (fields) {
341 return fields.reduce(function (acc, field, i) {
342 const metadata = metadataByColumnIndex[i];
343 const { id, name } = metadata;
344 // NOTE: side effects
345 if (!!id && !(id in IRI_TO_NAME)) {
346 IRI_TO_NAME[id] = name;
347 NAME_TO_IRI[name] = id;
348 }
349 acc[name] = dataTypeParsers[metadata[RDF + "datatype"]](field);
350 return acc;
351 }, {});
352 });
353 })
354 .map(function (dataSource) {
355 // remove empty properties, ie., properties with these values:
356 // ''
357 // NaN
358 // null
359 // undefined
360 // TODO what about empty plain object {} or array []
361 return omitBy(dataSource, function (value) {
362 return (value === "" || isNaN(value) || isNull(value) || isUndefined(value));
363 });
364 })
365 .map(function (dataSource) {
366 // Kludge to temporarily handle this issue:
367 // https://github.com/bridgedb/BridgeDb/issues/58
368 if (dataSource.id === "Sp") {
369 dataSource.id = "urn:miriam:uniprot";
370 }
371 // If the Miriam URN is unknown or unspecified, datasources.txt uses
372 // the BridgeDb system code as a placeholder value.
373 // So here we make sure "id" is actually a Miriam URN.
374 if (dataSource.hasOwnProperty("id") &&
375 dataSource.id.indexOf("urn:miriam:") > -1) {
376 // switch "id" property from Miriam URN to identifiers.org IRI
377 const miriamUrn = dataSource.id;
378 dataSource.miriamUrn = miriamUrn;
379 const preferredPrefix = miriamUrnToPreferredPrefix(miriamUrn);
380 if (preferredPrefix) {
381 dataSource.preferredPrefix = preferredPrefix;
382 dataSource.sameAs = dataSource.sameAs || [];
383 dataSource.sameAs.push(miriamUrn);
384 const identifiersIri = miriamUrnToIdentifiersIri(miriamUrn);
385 if (identifiersIri) {
386 dataSource.id = dataSource.hasIdentifiersOrgPattern = identifiersIri;
387 }
388 }
389 }
390 else {
391 delete dataSource.id;
392 }
393 return dataSource;
394 })
395 .map(function (dataSource) {
396 const primaryUriPattern = dataSource.hasPrimaryUriPattern;
397 if (!!primaryUriPattern) {
398 const regexXrefIdentifierPattern = dataSource.hasRegexPattern || ".*";
399 dataSource.hasRegexUriPattern = primaryUriPattern.replace("$id",
400 // removing ^ (start) and $ (end) from regexXrefIdentifierPattern
401 "(" + regexXrefIdentifierPattern.replace(/(^\^|\$$)/g, "") + ")");
402 // if '$id' is at the end of the primaryUriPattern
403 var indexOfDollaridWhenAtEnd = primaryUriPattern.length - 3;
404 if (primaryUriPattern.indexOf("$id") === indexOfDollaridWhenAtEnd) {
405 dataSource.sameAs = dataSource.sameAs || [];
406 dataSource.sameAs.push(primaryUriPattern.substr(0, indexOfDollaridWhenAtEnd));
407 }
408 }
409 if (dataSource.type) {
410 dataSource[BDB + "type"] = dataSource.type;
411 }
412 dataSource.type = "Dataset";
413 return dataSource;
414 })
415 .map(function (dataSource) {
416 const bdbType = dataSource[BDB + "type"];
417 if (!!bdbType) {
418 dataSource.subject = [];
419 /* Example of using 'subject' (from the VOID docs <http://www.w3.org/TR/void/#subject>):
420 :Bio2RDF a void:Dataset;
421 dcterms:subject <http://purl.uniprot.org/core/Gene>;
422 .
423
424 The closest concepts from the WP, BioPAX and MESH vocabularies are included below,
425 with the default vocabulary being WP.
426
427 Note that in BioPAX, 'ProteinReference' is to 'Protein' as
428 'Class' is to 'Instance' or
429 'platonic ideal of http://identifiers.org/uniprot/P78527' is to
430 'one specific example of http://identifiers.org/uniprot/P78527'
431 with the same logic applying for Dna, Rna and SmallMolecule. As such, it appears the
432 subject of Uniprot is best described in BioPAX terms as biopax:ProteinReference instead
433 of biopax:Protein.
434
435 It is unclear whether the subject of Entrez Gene is biopax:DnaReference or biopax:Gene,
436 but I'm going with biopax:DnaReference for now because it appears to be analogous to
437 ProteinReference and SmallMoleculeReference.
438 //*/
439 if (bdbType === "gene" ||
440 // TODO should the following two conditions be removed?
441 bdbType === "probe" ||
442 dataSource.preferredPrefix === "go") {
443 dataSource.subject.push("GeneProduct");
444 dataSource.subject.push(BIOPAX + "DnaReference");
445 }
446 else if (bdbType === "rna") {
447 dataSource.subject.push("Rna");
448 dataSource.subject.push(BIOPAX + "RnaReference");
449 }
450 else if (bdbType === "protein") {
451 dataSource.subject.push("Protein");
452 dataSource.subject.push(BIOPAX + "ProteinReference");
453 }
454 else if (bdbType === "metabolite") {
455 dataSource.subject.push("Metabolite");
456 dataSource.subject.push(BIOPAX + "SmallMoleculeReference");
457 }
458 else if (bdbType === "pathway") {
459 // BioPAX does not have a term for pathways that is analogous to
460 // biopax:ProteinReference for proteins.
461 dataSource.subject.push("Pathway");
462 dataSource.subject.push(BIOPAX + "Pathway");
463 }
464 else if (bdbType === "ontology") {
465 dataSource.subject.push(OWL + "Ontology");
466 }
467 else if (bdbType === "interaction") {
468 dataSource.subject.push("Interaction");
469 dataSource.subject.push(BIOPAX + "Interaction");
470 }
471 }
472 dataSource.alternatePrefix = [dataSource.systemCode];
473 return dataSource;
474 })
475 .reduce(function (acc, dataSource) {
476 DATASOURCE_ID_PROPERTIES.forEach(function (propertyName) {
477 const propertyValue = dataSource[propertyName];
478 const propertyId = NAME_TO_IRI[propertyName];
479 dataSource[propertyId] = propertyValue;
480 acc[propertyValue] = dataSource;
481 });
482 return acc;
483 }, {})
484 .catch(err => {
485 throw new VError(err, "Setting up dataSourceMappings$ in constructor");
486 })
487 .publishReplay();
488 // toggle from cold to hot
489 bridgeDb.dataSourceMappings$.connect();
490 } // end constructor
491 attributes(organism, xrefDataSource, xrefIdentifier) {
492 let bridgeDb = this;
493 return bridgeDb
494 .getTSV(bridgeDb.config.baseIri +
495 organism +
496 "/attributes/" +
497 xrefDataSource +
498 "/" +
499 xrefIdentifier)
500 .reduce(function (acc, fields) {
501 const key = camelCase(fields[0]);
502 const value = fields[1];
503 acc[key] = value;
504 return acc;
505 }, {})
506 .catch(err => {
507 throw new VError(err, "calling bridgedb.attributes");
508 });
509 }
510 attributeSearch(organism, query, attrName) {
511 let bridgeDb = this;
512 const attrNameParamSection = attrName ? "?attrName=" + attrName : "";
513 return bridgeDb
514 .getTSV(bridgeDb.config.baseIri +
515 organism +
516 "/attributeSearch/" +
517 query +
518 attrNameParamSection)
519 .mergeMap(bridgeDb.parseXrefRow)
520 .catch(err => {
521 throw new VError(err, "calling bridgedb.attributeSearch");
522 });
523 }
524 attributeSet(organism) {
525 let bridgeDb = this;
526 return bridgeDb
527 .getTSV(bridgeDb.config.baseIri + organism + "/attributeSet")
528 .reduce(function (acc, row) {
529 acc.push(row[0]);
530 return acc;
531 }, [])
532 .catch(err => {
533 throw new VError(err, "calling bridgedb.attributeSet");
534 });
535 }
536 isFreeSearchSupported(organism) {
537 let bridgeDb = this;
538 const ajaxRequest = {
539 url: bridgeDb.config.baseIri + organism + "/isFreeSearchSupported",
540 method: "GET",
541 responseType: "text",
542 timeout: bridgeDb.config.http.timeout,
543 crossDomain: true
544 };
545 return (Observable.ajax(ajaxRequest)
546 .map((ajaxResponse) => ajaxResponse.xhr.response)
547 // NOTE: must compare with 'true' as a string, because the response is just a string, not a parsed JS boolean.
548 .map(res => res === "true")
549 // TODO is this TS correct?
550 .catch((err) => {
551 throw new VError(err, "calling bridgedb.isFreeSearchSupported");
552 }));
553 }
554 isMappingSupported(organism, sourceXrefDataSource, targetXrefDataSource) {
555 let bridgeDb = this;
556 const ajaxRequest = {
557 url: `${bridgeDb.config.baseIri +
558 organism}/isMappingSupported/${sourceXrefDataSource}/${targetXrefDataSource}`,
559 method: "GET",
560 responseType: "text",
561 timeout: bridgeDb.config.http.timeout,
562 crossDomain: true
563 };
564 return (Observable.ajax(ajaxRequest)
565 .map((ajaxResponse) => ajaxResponse.xhr.response)
566 // NOTE: must compare with 'true' as a string, because the response is just a string, not a parsed JS boolean.
567 .map(res => res === "true")
568 // TODO is this TS correct?
569 .catch((err) => {
570 throw new VError(err, "calling bridgedb.isMappingSupported");
571 }));
572 }
573 organismProperties(organism) {
574 let bridgeDb = this;
575 return bridgeDb
576 .getTSV(bridgeDb.config.baseIri + organism + "/properties")
577 .reduce(function (acc, fields) {
578 const key = camelCase(fields[0]);
579 const value = fields[1];
580 acc[key] = value;
581 return acc;
582 }, {})
583 .catch(err => {
584 throw new VError(err, "calling bridgedb.organismProperties");
585 });
586 }
587 organisms() {
588 let bridgeDb = this;
589 return bridgeDb
590 .getTSV(bridgeDb.config.baseIri + "contents")
591 .map(function (fields) {
592 return {
593 en: fields[0],
594 la: fields[1]
595 };
596 })
597 .catch(err => {
598 throw new VError(err, "calling bridgedb.organisms");
599 });
600 }
601 search(organism, query) {
602 let bridgeDb = this;
603 return bridgeDb
604 .getTSV(bridgeDb.config.baseIri + organism + "/search/" + query)
605 .mergeMap(bridgeDb.parseXrefRow)
606 .catch(err => {
607 throw new VError(err, "calling bridgedb.search");
608 });
609 }
610 sourceDataSources(organism) {
611 let bridgeDb = this;
612 return bridgeDb
613 .getTSV(bridgeDb.config.baseIri + organism + "/sourceDataSources")
614 .map(function (fields) {
615 return fields[0];
616 })
617 .mergeMap(bridgeDb.dataSourceProperties)
618 .catch(err => {
619 throw new VError(err, "calling bridgedb.sourceDataSources");
620 });
621 }
622 targetDataSources(organism) {
623 let bridgeDb = this;
624 return bridgeDb
625 .getTSV(bridgeDb.config.baseIri + organism + "/targetDataSources")
626 .map(function (fields) {
627 return fields[0];
628 })
629 .mergeMap(bridgeDb.dataSourceProperties)
630 .catch(err => {
631 throw new VError(err, "calling bridgedb.targetDataSources");
632 });
633 }
634 // TODO check whether dataSource exists before calling webservice re:
635 // dataSource AND identifier
636 xrefExists(organism, xrefDataSource, xrefIdentifier) {
637 let bridgeDb = this;
638 const ajaxRequest = {
639 url: `${bridgeDb.config.baseIri +
640 organism}/xrefExists/${xrefDataSource}/${xrefIdentifier}`,
641 method: "GET",
642 responseType: "text",
643 timeout: bridgeDb.config.http.timeout,
644 crossDomain: true
645 };
646 return (Observable.ajax(ajaxRequest)
647 .map((ajaxResponse) => ajaxResponse.xhr.response)
648 // NOTE: must compare with 'true' as a string, because the response is just a string, not a parsed JS boolean.
649 .map(res => res === "true")
650 // TODO is this TS correct?
651 .catch((err) => {
652 throw new VError(err, "calling bridgedb.xrefExists");
653 }));
654 }
655 xrefs(organism, xrefDataSource, xrefIdentifier, desiredXrefDataSourceOrSources) {
656 let bridgeDb = this;
657 let xrefsRequestQueue = bridgeDb.xrefsRequestQueue;
658 let xrefsResponseQueue = bridgeDb.xrefsResponseQueue;
659 const desiredXrefDataSources = arrayify(desiredXrefDataSourceOrSources);
660 xrefsRequestQueue.next({
661 organism,
662 xrefDataSource,
663 xrefIdentifier,
664 desiredXrefDataSources
665 });
666 return (xrefsResponseQueue
667 .find(function (xrefBatchEnvelope) {
668 return (xrefBatchEnvelope.organism === organism &&
669 // NOTE: we are not using the dataSource test in the line below.
670 // Instead, we are matching dataSources in the mergeMap further below.
671 // The reason is that the inputXrefDataSource and the returned dataSource
672 // may not match, e.g., 'L' vs. 'Entrez Gene'.
673 xrefBatchEnvelope.inputXrefDataSource === xrefDataSource &&
674 xrefBatchEnvelope.inputXrefIdentifier === xrefIdentifier &&
675 xrefBatchEnvelope.desiredXrefDataSources.join() ===
676 desiredXrefDataSources.join());
677 })
678 .map(x => x.xrefs)
679 //.do(null, xrefsRequestQueue.complete)
680 .catch(err => {
681 throw new VError(err, "calling bridgedb.xrefs");
682 }));
683 }
684}
685//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQnJpZGdlRGIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvQnJpZGdlRGIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsOENBQThDO0FBRTlDLDRCQUE0QjtBQUM1QiwyQ0FBMkM7QUFFM0MsT0FBTyw2QkFBNkIsQ0FBQztBQWVyQyxJQUFJLENBQUMsTUFBTSxDQUFDLGNBQWMsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFO0lBQzVDLE1BQU0sQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0NBQ3pDO0FBRUQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ2hELE9BQU8sRUFDTCxTQUFTLEVBQ1QsWUFBWSxFQUNaLElBQUksRUFDSixNQUFNLEVBQ04sT0FBTyxFQUNQLE9BQU8sRUFDUCxLQUFLLEVBQ0wsTUFBTSxFQUNOLFFBQVEsRUFDUixXQUFXLEVBQ1gsTUFBTSxFQUNOLE9BQU8sRUFDUCxHQUFHLEVBQ0osTUFBTSxRQUFRLENBQUM7QUFDaEIsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRzdDLE9BQU8sOEJBQThCLENBQUM7QUFDdEMsT0FBTywyQkFBMkIsQ0FBQztBQUNuQyxPQUFPLDhCQUE4QixDQUFDO0FBQ3RDLE9BQU8sMEJBQTBCLENBQUM7QUFDbEMsT0FBTywyQkFBMkIsQ0FBQztBQUNuQyxPQUFPLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyw4QkFBOEIsQ0FBQztBQUN0QyxPQUFPLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sNkJBQTZCLENBQUM7QUFDckMsT0FBTyxnQ0FBZ0MsQ0FBQztBQUN4QyxPQUFPLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sd0NBQXdDLENBQUM7QUFDaEQsT0FBTyxzQkFBc0IsQ0FBQztBQUM5QixPQUFPLDBCQUEwQixDQUFDO0FBQ2xDLE9BQU8sd0JB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
\No newline at end of file