UNPKG

@plantinformatics/vcf-genotype-brapi

Version:

Client and server functions to access genotype data from VCF via a custom web API and BrAPI

207 lines (191 loc) 7.26 kB
// const util = require('util'); // promisify = util.promisify; // import bluebird from 'bluebird'; // const promisify = bluebird/*Promise*/.promisify; import { promisifyFn as promisify } from './promisify.js'; import { Germinate } from './germinate.js'; // const { ErrorStatus } = require('./errorStatus.js'); //------------------------------------------------------------------------------ /** The scope of this module is : * . use of Germinate server connection to implement Pretzel genotype requests. * * Related ./germinate.js */ //------------------------------------------------------------------------------ let germinateInstance; export { useGerminate }; /** Used by API requests to ensure Germinate session is connected and * authenticated, so they can use it to fulfill requests. * @param url domain URL - base path of endpoint URLs, e.g. germinate.plantinformatics.io * @param username, password optional. Used in frontend not in backend. * @param token alternative to username and password if no password required, * i.e. token may be a string meaning no authentication required (use a * non-empty string because '' is falsey, and germinate.js : connectedP() checks * if (! this.token) ). * @return a promise which resolves with germinateInstance or * rejects with ErrorStatus(), which the caller can pass to response cb. */ function useGerminate(url, username, password, token) { const fnName = 'useGerminate'; let connectedP; if (! germinateInstance) { germinateInstance = new Germinate(url); } else if (url && (url != germinateInstance.serverURL)) { if (germinateInstance.serverURL) { console.log(fnName, url, 'replacing', germinateInstance.serverURL); } germinateInstance.serverURL = url; } if (typeof token === 'string') { germinateInstance.setToken(token); } else // if pre-existing germinateInstance and it has these fields, they are overridden. if (username && password) { germinateInstance.setCredentials(username, password); } connectedP = germinateInstance.connectedP() .then(() => { if (! germinateInstance.data_serverinfo && url && ! url.match(/germinate/i)) { germinateInstance.serverinfo(); // sets : germinateInstance.data_serverinfo } return germinateInstance;}) .catch(error => { console.log(fnName, 'Germinate', error); // ErrorStatus() is used in server, not useful in frontend/. throw Error(error); // ErrorStatus(503, error) ; // statusCode }); /* if (! germinate) { try { console.log(fnName, germinate); // germinate.serverinfo(); // germplasm(); // callsets(); } catch (error) { // throw } } */ return connectedP; } /** refn : https://developer.mozilla.org/en-US/docs/Web/HTTP/Status 502 Bad Gateway This error response means that the server, while working as a gateway to get a response needed to handle the request, got an invalid response. 503 Service Unavailable */ //------------------------------------------------------------------------------ /** [datasetId + scope] -> [callSetName] -> callSetDbId */ const callSetName2DbId = {}; /** @return sample name cache for datasetId:scope */ function callSetCacheForBlock(datasetId, scope) { const blockName = datasetId + '_' + scope, name2DbId = callSetName2DbId[blockName] || (callSetName2DbId[blockName] = {}); return name2DbId; } const germinateGenotypeSamplesP = promisify(germinateGenotypeSamples); /** if sample name cache for datasetId:scope is not empty, yield it, otherwise * get samples then yield it. * @return promise */ function callSetCacheForBlockP(datasetId, scope) { const name2DbId = callSetCacheForBlock(datasetId, scope), /** if name2DbId is not empty, yield it, otherwise get samples then yield it. */ p = name2DbId && Object.keys(name2DbId).length ? Promise.resolve(name2DbId) : germinateGenotypeSamplesP(datasetId, scope) .then(samples => callSetCacheForBlock(datasetId, scope)); return p; } export { germinateGenotypeSamples }; function germinateGenotypeSamples(datasetId, scope, cb) { useGerminate() .then((germinate) => { const samplesP = germinate.samples(datasetId); samplesP .then(response => { const samples = response.result.data.map(d => d.callSetName); const name2Id = callSetCacheForBlock(datasetId, scope); response.result.data.forEach(d => (name2Id[d.callSetName] = d.callSetDbId)); cb(null, samples); }) .catch(error => cb(error)); }) .catch(cb); } export { germinateGenotypeLookup }; function germinateGenotypeLookup(datasetId, scope, preArgs, nLines, undefined, cb) { const fnName = 'germinateGenotypeLookup', /** get samples for datasetId : scope if not cached. */ name2IdP = callSetCacheForBlockP(datasetId, scope); Promise.all([name2IdP, useGerminate()]) .then(([name2Id, germinate]) => { // preArgs.samples '1-593' // preArgs.region : scope + ':' + domainInteger.join('-'), const samples = preArgs.samples, match = preArgs.region.match(/.+:([0-9]+)-([0-9]+)/); let all, start, end; if (match) { [all, start, end] = match; } console.log(fnName, 'samples', typeof samples, Array.isArray(samples) ? samples.slice(0,5) : samples.slice(0,50)); const sampleNames = Array.isArray(samples) ? samples : samples.split('\n'), samplesDataP = sampleNames.map(sampleName => { // e.g. '1-593' const callSetDbId = name2Id[sampleName]; let mapid, sampleId; /* may change - perhaps implement the Germinate callSetDbId format in Spark server. */ if (callSetDbId.match(/^[0-9]+-[0-9]+$/)) { // Germinate [mapid, sampleId] = callSetDbId.split('-'); } else { // Spark server mapid = datasetId; // sampleId is callSetDbId } const linkageGroupName = preArgs.linkageGroupName, dataP = ! callSetDbId ? Promise.resolve([]) : germinate.callsetsCalls(mapid, callSetDbId, linkageGroupName, start, end, nLines) .then(response => response.result.data); return dataP; }); Promise.all(samplesDataP) .then(samplesData => { cb(null, samplesData.flat()); }) .catch(error => cb(error)); }) .catch(cb); } //------------------------------------------------------------------------------ /** copied from lb4app/lb3app/common/models/block.js : Block.vcfGenotypeLookup() : ensureSamplesParam() */ export { ensureSamplesParam }; /** Ensure that preArgs.samples is defined : if undefined, request samples * and use the first one. */ function ensureSamplesParam(datasetId, scope, preArgs) { let argsP; if (! preArgs?.samples?.length) { argsP = germinateGenotypeSamplesP(datasetId, scope) .then(samples => { let sample; if (samples.length) { sample = samples[0]; } else { sample = ''; } // could use Object.assign() to avoid mutating preArgs. preArgs.samples = sample; return preArgs; }); } else { argsP = Promise.resolve(preArgs); } return argsP; } //------------------------------------------------------------------------------