From 45c6a1db6a12fc0b58f488d62515e591adc98952 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Mon, 8 Jan 2024 16:57:01 -0500 Subject: [PATCH 1/2] Document canceler parameters, and remove some API URL uses --- src/APIInterface.mjs | 67 +++++++++++++++++++----------------- src/ServerAPI.mjs | 5 ++- src/components/HeaderForm.js | 16 ++++++--- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/APIInterface.mjs b/src/APIInterface.mjs index 4e7b86ef..899694f1 100644 --- a/src/APIInterface.mjs +++ b/src/APIInterface.mjs @@ -3,40 +3,45 @@ // Abstract class expecting different implmentations of the following functions // Substituting different subclasses should allow the functions to give the same result export class APIInterface { - // Takes in and process a tube map view(viewTarget) from the tubemap container - // Expects a object to be returned with the necessary information to draw a tubemap from vg - // object should contain keys: graph, gam, region, coloredNodes - async getChunkedData(viewTarget) { - throw new Error("getChunkedData function not implemented"); - } + // Takes in and process a tube map view(viewTarget) from the tubemap container. + // Expects a object to be returned with the necessary information to draw a tubemap from vg + // object should contain keys: graph, gam, region, coloredNodes. + // cancelSignal is an AbortSignal that can be used to cancel the request. + async getChunkedData(viewTarget, cancelSignal) { + throw new Error("getChunkedData function not implemented"); + } - // Returns files used to determine what options are available in the track picker - // Returns object with keys: files, bedFiles - async getFilenames() { - throw new Error("getFilenames function not implemented"); - } + // Returns files used to determine what options are available in the track picker. + // Returns object with keys: files, bedFiles. + // cancelSignal is an AbortSignal that can be used to cancel the request. + async getFilenames(cancelSignal) { + throw new Error("getFilenames function not implemented"); + } - // Takes in a bedfile path or a url pointing to a raw bed file - // Returns object with key: bedRegions - // bedRegions contains information extrapolated from each line of the bedfile - async getBedRegions(bedFile) { - throw new Error("getBedRegions function not implemented"); - } + // Takes in a bedfile path or a url pointing to a raw bed file. + // Returns object with key: bedRegions. + // bedRegions contains information extrapolated from each line of the bedfile. + // cancelSignal is an AbortSignal that can be used to cancel the request. + async getBedRegions(bedFile, cancelSignal) { + throw new Error("getBedRegions function not implemented"); + } - // Takes in a graphFile path - // Returns object with key: pathNames - // Returns pathnames available in a graphfile - async getPathNames(graphFile) { - throw new Error("getPathNames function not implemented"); - } + // Takes in a graphFile path. + // Returns object with key: pathNames. + // Returns pathnames available in a graphfile. + // cancelSignal is an AbortSignal that can be used to cancel the request. + async getPathNames(graphFile, cancelSignal) { + throw new Error("getPathNames function not implemented"); + } - // Expects a bed file(or url) and a chunk name - // Attempts to download tracks associated with the chunk name from the bed file if it is a URL - // Returns object with key: tracks - // Returns tracks found from local directories as a tracks object - async getChunkTracks(bedFile, chunk) { - throw new Error("getChunkTracks function not implemented"); - } + // Expects a bed file(or url) and a chunk name. + // Attempts to download tracks associated with the chunk name from the bed file if it is a URL. + // Returns object with key: tracks. + // Returns tracks found from local directories as a tracks object. + // cancelSignal is an AbortSignal that can be used to cancel the request. + async getChunkTracks(bedFile, chunk, cancelSignal) { + throw new Error("getChunkTracks function not implemented"); + } } -export default APIInterface; \ No newline at end of file +export default APIInterface; diff --git a/src/ServerAPI.mjs b/src/ServerAPI.mjs index cf7467ef..81f55fba 100644 --- a/src/ServerAPI.mjs +++ b/src/ServerAPI.mjs @@ -1,6 +1,9 @@ import { fetchAndParse } from "./fetchAndParse.js"; import { APIInterface } from "./APIInterface.mjs"; +/** + * API implementation that uses vg running on the server to manipulate files. + */ export class ServerAPI extends APIInterface { constructor(apiUrl) { super(); @@ -69,4 +72,4 @@ export class ServerAPI extends APIInterface { } } -export default ServerAPI; \ No newline at end of file +export default ServerAPI; diff --git a/src/components/HeaderForm.js b/src/components/HeaderForm.js index f52b2b7e..c429926d 100644 --- a/src/components/HeaderForm.js +++ b/src/components/HeaderForm.js @@ -341,7 +341,7 @@ class HeaderForm extends Component { } catch (error) { this.handleFetchError( error, - `GET to ${this.props.apiUrl}/getFilenames failed:` + `API getFilenames failed:` ); } }; @@ -365,7 +365,7 @@ class HeaderForm extends Component { } catch (error) { this.handleFetchError( error, - `POST to ${this.props.apiUrl}/getBedRegions failed:` + `API getBedRegions failed:` ); } }; @@ -393,7 +393,7 @@ class HeaderForm extends Component { } catch (error) { this.handleFetchError( error, - `POST to ${this.props.apiUrl}/getPathNames failed:` + `API getPathNames failed:` ); } }; @@ -689,10 +689,16 @@ class HeaderForm extends Component { setUpWebsocket = () => { this.ws = new WebSocket(this.props.apiUrl.replace(/^http/, "ws")); this.ws.onmessage = (message) => { - this.getMountedFilenames(); + if (!this.cancelSignal.aborted) { + this.getMountedFilenames(); + } else { + this.ws.close(); + } }; this.ws.onclose = (event) => { - setTimeout(this.setUpWebsocket, 1000); + if (!this.cancelSignal.aborted) { + setTimeout(this.setUpWebsocket, 1000); + } }; this.ws.onerror = (event) => { this.ws.close(); From da5007f796843978f91b65430bd6d36fd88851cd Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Mon, 8 Jan 2024 17:00:35 -0500 Subject: [PATCH 2/2] Run the formatter --- src/APIInterface.mjs | 3 +- src/App.css | 3 +- src/App.js | 45 +- src/App.test.js | 12 +- src/ServerAPI.mjs | 118 +- src/common.mjs | 38 +- src/components/BedFileDropdown.demo.js | 46 +- src/components/BedFileDropdown.js | 10 +- src/components/ColorPicker.demo.js | 39 +- src/components/ColorPicker.js | 139 +-- src/components/CopyLink.js | 31 +- src/components/CustomizationAccordion.js | 18 +- src/components/DataPositionFormRow.js | 10 +- src/components/DemoLibrary.js | 43 +- src/components/Footer.js | 48 +- src/components/HeaderForm.js | 309 ++--- src/components/HelpButton.demo.js | 19 +- src/components/HelpButton.js | 112 +- src/components/HelpButton.test.js | 33 +- src/components/PickerTypeDropdown.js | 82 +- src/components/PopUpInfoDialog.js | 59 +- src/components/PopupDialog.js | 66 +- src/components/RadioRow.js | 59 +- src/components/RegionInput.demo.js | 50 +- src/components/RegionInput.js | 19 +- src/components/RegionInput.test.js | 7 +- src/components/SafeLink.js | 22 +- src/components/TrackAddButton.js | 42 +- src/components/TrackDeleteButton.demo.js | 38 +- src/components/TrackDeleteButton.js | 31 +- src/components/TrackDeleteButton.test.js | 26 +- src/components/TrackFilePicker.demo.js | 60 +- src/components/TrackFilePicker.js | 166 +-- src/components/TrackFilePicker.test.js | 276 ++--- src/components/TrackList.demo.js | 100 +- src/components/TrackList.js | 69 +- src/components/TrackList.test.js | 327 ++--- src/components/TrackListItem.demo.js | 68 +- src/components/TrackListItem.js | 262 ++-- src/components/TrackListItem.test.js | 317 ++--- src/components/TrackPicker.demo.js | 49 +- src/components/TrackPicker.js | 92 +- src/components/TrackPicker.test.js | 379 +++--- src/components/TrackPickerDisplay.demo.js | 91 +- src/components/TrackPickerDisplay.js | 210 ++-- src/components/TrackPickerDisplay.test.js | 374 +++--- src/components/TrackSettings.demo.js | 45 +- src/components/TrackSettings.js | 255 ++-- src/components/TrackSettings.test.js | 123 +- src/components/TrackSettingsButton.demo.js | 45 +- src/components/TrackSettingsButton.js | 86 +- src/components/TrackSettingsButton.test.js | 40 +- src/components/TrackTypeDropdown.demo.js | 42 +- src/components/TrackTypeDropdown.js | 49 +- src/components/TrackTypeDropdown.test.js | 40 +- src/components/TubeMap.js | 13 +- src/components/TubeMapContainer.js | 51 +- src/config-client.js | 2 +- src/config-global.mjs | 8 +- src/config-server.mjs | 6 +- src/end-to-end.test.js | 47 +- src/index.js | 49 +- src/server.mjs | 307 ++--- src/util/demo-data.js | 576 ++++----- src/util/tubemap.js | 114 +- src/util/tubemap.test.js | 1285 ++++++++++---------- 66 files changed, 3961 insertions(+), 3639 deletions(-) diff --git a/src/APIInterface.mjs b/src/APIInterface.mjs index 899694f1..29c2e7f6 100644 --- a/src/APIInterface.mjs +++ b/src/APIInterface.mjs @@ -1,7 +1,6 @@ - // Interface for handling function called from the tubemap frontend // Abstract class expecting different implmentations of the following functions -// Substituting different subclasses should allow the functions to give the same result +// Substituting different subclasses should allow the functions to give the same result export class APIInterface { // Takes in and process a tube map view(viewTarget) from the tubemap container. // Expects a object to be returned with the necessary information to draw a tubemap from vg diff --git a/src/App.css b/src/App.css index ae8188bd..aad9f671 100644 --- a/src/App.css +++ b/src/App.css @@ -126,7 +126,6 @@ input[type="file"] { border-color: #b61515; } - .btn-secondary { background-color: #868e96; border-color: #868e96; @@ -218,7 +217,7 @@ input[type="file"] { /* Close Popup */ -.closePopup{ +.closePopup { align-items: center; scale: 0.7; border-radius: 50%; diff --git a/src/App.js b/src/App.js index d46f0dc5..2ebe9404 100644 --- a/src/App.js +++ b/src/App.js @@ -19,12 +19,11 @@ import ServerAPI from "./ServerAPI.mjs"; const EXAMPLE_TRACKS = [ // Fake tracks for the generated examples. // TODO: Move over there. - {"files": [{"type": "graph", "name": "fakeGraph"}]}, - {"files": [{"type": "read", "name": "fakeReads"}]} + { files: [{ type: "graph", name: "fakeGraph" }] }, + { files: [{ type: "read", name: "fakeReads" }] }, ]; function getColorSchemesFromTracks(tracks) { - let schemes = []; for (const key in tracks) { @@ -33,13 +32,13 @@ function getColorSchemesFromTracks(tracks) { if (tracks[key].trackColorSettings !== undefined) { schemes[key] = tracks[key].trackColorSettings; } else if (tracks[key].trackType === "read") { - schemes[key] = {...config.defaultReadColorPalette}; + schemes[key] = { ...config.defaultReadColorPalette }; } else { - schemes[key] = {...config.defaultHaplotypeColorPalette}; + schemes[key] = { ...config.defaultHaplotypeColorPalette }; } } } - + return schemes; } @@ -49,7 +48,7 @@ class App extends Component { this.APIInterface = new ServerAPI(props.apiUrl); - console.log('App component starting up with API URL: ' + props.apiUrl) + console.log("App component starting up with API URL: " + props.apiUrl); // Set defaultViewTarget to either URL params (if present) or the first example this.defaultViewTarget = @@ -100,23 +99,21 @@ class App extends Component { !isEqual(this.state.viewTarget, newViewTarget) || this.state.dataOrigin !== dataOriginTypes.API ) { - - console.log("Adopting view target: ", newViewTarget) + console.log("Adopting view target: ", newViewTarget); this.setState((state) => { // Make sure we have color schemes. let newColorSchemes = getColorSchemesFromTracks(newViewTarget.tracks); - - console.log("Adopting color schemes: ", newColorSchemes) + console.log("Adopting color schemes: ", newColorSchemes); return { viewTarget: newViewTarget, dataOrigin: dataOriginTypes.API, visOptions: { ...state.visOptions, - colorSchemes: newColorSchemes, - } + colorSchemes: newColorSchemes, + }, }; }); } @@ -150,21 +147,21 @@ class App extends Component { // index is the index in the tracks array of the track to operate on. For now, // haplotypes and paths are lumped together as track 0 here, with up to two // tracks of reads afterward; eventually this will follow the indexing of the real - // tracks array. + // tracks array. // // value is the value to set. For "mainPalette" and "auxPalette" this is the name // of a color palette, such as "reds". setColorSetting = (key, index, value) => { this.setState((state) => { - let newcolors = [...state.visOptions.colorSchemes] + let newcolors = [...state.visOptions.colorSchemes]; if (newcolors[index] === undefined) { // Handle the set call from example data maybe coming before we set up any nonempty real tracks. // TODO: Come up with a better way to do this. - newcolors[index] = {...config.defaultReadColorPalette}; + newcolors[index] = { ...config.defaultReadColorPalette }; } - newcolors[index] = {...newcolors[index], [key]: value}; - console.log('Set index ' + index + ' key ' + key + ' to ' + value); - console.log('New colors: ', newcolors); + newcolors[index] = { ...newcolors[index], [key]: value }; + console.log("Set index " + index + " key " + key + " to " + value); + console.log("New colors: ", newcolors); return { visOptions: { ...state.visOptions, @@ -175,7 +172,7 @@ class App extends Component { }; setDataOrigin = (dataOrigin) => { - this.setState({dataOrigin}); + this.setState({ dataOrigin }); }; render() { @@ -200,14 +197,18 @@ class App extends Component { /> -