1 | /// <reference path="../typings/index.d.ts" />
|
2 | // TODO use a cache, such as
|
3 | // https://github.com/levelgraph/levelgraph
|
4 | import "source-map-support/register";
|
5 | if (!global.hasOwnProperty("XMLHttpRequest")) {
|
6 | global.XMLHttpRequest = require("xhr2");
|
7 | }
|
8 | import { curry, negate, uniq } from "lodash/fp";
|
9 | import { camelCase, defaultsDeep, fill, invert, isArray, isEmpty, isNaN, isNull, isString, isUndefined, omitBy, toPairs, zip } from "lodash";
|
10 | import { Observable } from "rxjs/Observable";
|
11 | import "rxjs/add/observable/dom/ajax";
|
12 | import "rxjs/add/observable/empty";
|
13 | import "rxjs/add/observable/forkJoin";
|
14 | import "rxjs/add/observable/from";
|
15 | import "rxjs/add/observable/throw";
|
16 | import "rxjs/add/observable/zip";
|
17 | import "rxjs/add/operator/buffer";
|
18 | import "rxjs/add/operator/bufferWhen";
|
19 | import "rxjs/add/operator/catch";
|
20 | import "rxjs/add/operator/concatAll";
|
21 | import "rxjs/add/operator/debounceTime";
|
22 | import "rxjs/add/operator/delay";
|
23 | import "rxjs/add/operator/distinctUntilChanged";
|
24 | import "rxjs/add/operator/do";
|
25 | import "rxjs/add/operator/filter";
|
26 | import "rxjs/add/operator/find";
|
27 | import "rxjs/add/operator/mergeMap";
|
28 | import "rxjs/add/operator/map";
|
29 | import "rxjs/add/operator/multicast";
|
30 | import "rxjs/add/operator/publishReplay";
|
31 | import "rxjs/add/operator/race";
|
32 | import "rxjs/add/operator/reduce";
|
33 | import "rxjs/add/operator/skip";
|
34 | import "rxjs/add/operator/toArray";
|
35 | import "rx-extra/add/operator/throughNodeStream";
|
36 | import { Subject } from "rxjs/Subject";
|
37 | import { TSVGetter } from "./spinoffs/TSVGetter";
|
38 | import { dataTypeParsers } from "./spinoffs/dataTypeParsers";
|
39 | import { arrayify } from "./spinoffs/jsonld-utils";
|
40 | const VError = require("verror");
|
41 | const BDB = "http://vocabularies.bridgedb.org/ops#";
|
42 | const BIOPAX = "http://www.biopax.org/release/biopax-level3.owl#";
|
43 | const IDENTIFIERS = "http://identifiers.org/";
|
44 | const OWL = "http://www.w3.org/2002/07/owl#";
|
45 | const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
46 | const 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()
|
49 | const XREF_REQUEST_DEBOUNCE_TIME = 10; // ms
|
50 | const XREF_REQUEST_CHUNK_SIZE = 100;
|
51 | const BRIDGE_DB_REPO_CDN = "https://raw.githubusercontent.com/bridgedb/BridgeDb/";
|
52 | const BRIDGE_DB_COMMIT_HASH = "465f9f944d09cefbb167eceb9c69499a764100a2";
|
53 | export 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.
|
78 | const DATASOURCE_ID_PROPERTIES = [
|
79 | "id",
|
80 | "miriamUrn",
|
81 | "conventionalName",
|
82 | "preferredPrefix",
|
83 | "systemCode"
|
84 | ];
|
85 | const 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 | };
|
90 | const 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 | */
|
97 | function 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 | */
|
109 | function 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 | }
|
116 | export 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 |