UNPKG

auspice

Version:

Web app for visualizing pathogen evolution

136 lines (115 loc) 5.06 kB
const utils = require("../utils"); const queryString = require("query-string"); const getAvailable = require("./getAvailable"); const path = require("path"); /* * Note that a lot of this code is replicated in the nextstrain.org repo * and it could instead be exposed by "auspice" (and imported by the nextstrain.org repo) * Note also https://github.com/nextstrain/auspice/issues/687 which proposes * changing the server-client API */ const handleError = (res, clientMsg, serverMsg="") => { res.statusMessage = clientMsg; utils.warn(`${clientMsg} -- ${serverMsg}`); return res.status(500).end(); }; const guessTreeName = (prefixParts) => { const guesses = ["HA", "NA", "PB1", "PB2", "PA", "NP", "NS", "MP", "L", "S", "SEGMENT1", "SEGMENT2", "SEGMENT3", "SEGMENT4", "SEGMENT5", "SEGMENT6", "SEGMENT7", "SEGMENT8", "SEGMENT9", "SEGMENT10"]; for (const part of prefixParts) { if (guesses.indexOf(part.toUpperCase()) !== -1) return part; } return undefined; }; const splitPrefixIntoParts = (url) => url .replace(/^\//, '') .replace(/\/$/, '') .split("/"); const parseClientUrl = (clientUrl, otherQueries) => { let auspiceDisplayUrl = ""; const fetchSuffixes = {}; let treeName; const prefixParts = splitPrefixIntoParts(clientUrl); /* does the URL specify two trees? */ let secondTreeName; for (let i=0; i<prefixParts.length; i++) { if (prefixParts[i].indexOf(":") !== -1) { [treeName, secondTreeName] = prefixParts[i].split(":"); prefixParts[i] = treeName; // only use the first tree from now on break; } } if (!secondTreeName && otherQueries.deprecatedSecondTree) { secondTreeName = otherQueries.deprecatedSecondTree; } if (!treeName) { utils.verbose("Guessing tree name -- this should be improved"); treeName = guessTreeName(prefixParts); } auspiceDisplayUrl += prefixParts.join("/"); if (secondTreeName) { const idxOfTree = prefixParts.indexOf(treeName); const secondTreePrefixParts = prefixParts.slice(); secondTreePrefixParts[idxOfTree] = secondTreeName; fetchSuffixes.secondTree = `${secondTreePrefixParts.join("_")}_tree.json`; const re = new RegExp(`\\/${treeName}(/|$)`); // note the double escape for special char auspiceDisplayUrl = auspiceDisplayUrl.replace(re, `/${treeName}:${secondTreeName}/`); } auspiceDisplayUrl = auspiceDisplayUrl.replace(/\/$/, ''); // remove any trailing slash fetchSuffixes.tree = `${prefixParts.join("_")}_tree.json`; fetchSuffixes.meta = `${prefixParts.join("_")}_meta.json`; const prefixToMatch = prefixParts.join("/"); if (otherQueries.type) { fetchSuffixes.additional = `${prefixParts.join("_")}_${otherQueries.type}.json`; } return ({fetchSuffixes, prefixToMatch, auspiceDisplayUrl, treeName, secondTreeName}); }; const setUpGetDatasetHandler = ({datasetsPath}) => { return async (req, res) => { const rawQuery = req.url.split('?')[1]; utils.log(`Getting datasets for: ${rawQuery}`); const query = queryString.parse(rawQuery); const availableDatasets = await getAvailable.getAvailableDatasets(datasetsPath); let datasetInfo; try { datasetInfo = parseClientUrl(query.prefix, query); } catch (err) { return handleError(res, `Couldn't parse the url "${query.prefix}"`, err.message); } /* there must be an exact match in the available datasets */ if (!availableDatasets.map((d) => d.request).includes(datasetInfo.prefixToMatch)) { return handleError(res, `${datasetInfo.prefixToMatch} not in available datasets`); } /* Are we requesting a certain file type? */ if (datasetInfo.fetchSuffixes.additional) { try { const jsonData = await utils.readFilePromise(path.join(datasetsPath, datasetInfo.fetchSuffixes.additional)); utils.verbose(`Success fetching ${datasetInfo.fetchSuffixes.additional} JSON.`); if (query.type === "tree") { return res.json({tree: jsonData}); } return res.json(jsonData); } catch (err) { return handleError(res, `Couldn't fetch JSON: ${datasetInfo.fetchSuffixes.additional}`, err.message); } } /* else combine the meta & tree(s) */ const unifiedJson = {}; try { unifiedJson.tree = await utils.readFilePromise(path.join(datasetsPath, datasetInfo.fetchSuffixes.tree)); unifiedJson.meta = await utils.readFilePromise(path.join(datasetsPath, datasetInfo.fetchSuffixes.meta)); if (datasetInfo.fetchSuffixes.secondTree) { unifiedJson.treeTwo = await utils.readFilePromise(path.join(datasetsPath, datasetInfo.fetchSuffixes.secondTree)); unifiedJson._treeTwoName = datasetInfo.secondTreeName; } unifiedJson._treeName = datasetInfo.treeName; unifiedJson._url = datasetInfo.auspiceDisplayUrl; } catch (err) { return handleError(res, `Couldn't fetch JSONs`, err.message); } utils.verbose("Success fetching v1 JSONs. Sending as a single JSON."); res.json(unifiedJson); }; }; module.exports = { setUpGetDatasetHandler };