From 4224088ff45592873bec26589faaadcc328be77c Mon Sep 17 00:00:00 2001 From: Zakir Gowani Date: Thu, 12 Aug 2021 23:22:16 -0700 Subject: [PATCH 01/15] fix: bug in permalinks that have slashes --- src/Discovery/Discovery.tsx | 5 +++-- src/Discovery/DiscoveryDetails.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Discovery/Discovery.tsx b/src/Discovery/Discovery.tsx index 7f2a70b23b..fc652ab301 100644 --- a/src/Discovery/Discovery.tsx +++ b/src/Discovery/Discovery.tsx @@ -245,10 +245,11 @@ const Discovery: React.FunctionComponent = (props: Props) => { useEffect(() => { // If opening to a study by default, open that study - if (props.params.studyUID) { - const studyID = props.params.studyUID; + if (props.params.studyUID && props.studies.length > 0) { + const studyID = decodeURIComponent(props.params.studyUID); const defaultModalData = props.studies.find( (r) => r[config.minimalFieldMapping.uid] === studyID); + if (defaultModalData) { setPermalinkCopied(false); setModalData(defaultModalData); diff --git a/src/Discovery/DiscoveryDetails.tsx b/src/Discovery/DiscoveryDetails.tsx index 12c10b171f..35b30f1982 100644 --- a/src/Discovery/DiscoveryDetails.tsx +++ b/src/Discovery/DiscoveryDetails.tsx @@ -51,7 +51,7 @@ const DiscoveryDetails = (props: Props) => ( - )} + ) + :
} {/* Export to workspaces button */} { ( From 5aedd3a0bbf2824a893c4a6c27e27e5007269fbc Mon Sep 17 00:00:00 2001 From: Michael Ingram Date: Mon, 9 Aug 2021 16:54:04 -0500 Subject: [PATCH 04/15] #): Sort accessible data to top of table by default --- src/Discovery/Discovery.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Discovery/Discovery.tsx b/src/Discovery/Discovery.tsx index 5093186f68..c145e873a0 100644 --- a/src/Discovery/Discovery.tsx +++ b/src/Discovery/Discovery.tsx @@ -18,10 +18,10 @@ import DiscoveryAccessibilityLinks from './DiscoveryAccessibilityLinks'; export const accessibleFieldName = '__accessible'; export enum AccessLevel { - ACCESSIBLE, - UNACCESSIBLE, - NOT_AVAILABLE, - PENDING + ACCESSIBLE = 1, + UNACCESSIBLE = 2, + PENDING = 3, + NOT_AVAILABLE = 4, } const ARBORIST_READ_PRIV = 'read'; @@ -369,16 +369,19 @@ const Discovery: React.FunctionComponent = (props: Props) => { text:  Unaccessible, value: AccessLevel.UNACCESSIBLE, id: 'unaccessible-data-filter', - }, { - text:  No Data, - value: AccessLevel.NOT_AVAILABLE, - id: 'not-available-data-filter', }, { text:  Pending, value: AccessLevel.PENDING, id: 'pending-data-filter', + }, { + text:  No Data, + value: AccessLevel.NOT_AVAILABLE, + id: 'not-available-data-filter', }], onFilter: (value, record) => record[accessibleFieldName] === value, + // This will sort the values in the order defined by the AccessLevel enum. (AccessLevel.ACCESSIBLE=1, AccessLevel.UNACCESSIBLE=2, etc) + sorter: (a, b) => b[accessibleFieldName] - a[accessibleFieldName], + defaultSortOrder: 'descend', ellipsis: false, width: '106px', textWrap: 'word-break', From 0e71986563cb15bb2a60eb13085d6f8628558726 Mon Sep 17 00:00:00 2001 From: Michael Ingram Date: Thu, 12 Aug 2021 09:20:35 -0500 Subject: [PATCH 05/15] #): Add new n/a icon to table --- src/Discovery/Discovery.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discovery/Discovery.tsx b/src/Discovery/Discovery.tsx index c145e873a0..cdb30e8ee9 100644 --- a/src/Discovery/Discovery.tsx +++ b/src/Discovery/Discovery.tsx @@ -414,7 +414,7 @@ const Discovery: React.FunctionComponent = (props: Props) => {
)} > - n/a + ); } From 86ab1e60e8899b366ef3c3b49e2d44fc52844992 Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Thu, 12 Aug 2021 01:20:10 -0500 Subject: [PATCH 06/15] #): #): feat(download): add UI components and polling for zip downloads --- src/Discovery/Discovery.tsx | 6 + src/Discovery/DiscoveryActionBar.tsx | 166 ++++++++++++++++++++++++++- src/Discovery/DiscoveryConfig.d.ts | 3 + 3 files changed, 174 insertions(+), 1 deletion(-) diff --git a/src/Discovery/Discovery.tsx b/src/Discovery/Discovery.tsx index cdb30e8ee9..73cd791c94 100644 --- a/src/Discovery/Discovery.tsx +++ b/src/Discovery/Discovery.tsx @@ -195,6 +195,8 @@ const Discovery: React.FunctionComponent = (props: Props) => { const [permalinkCopied, setPermalinkCopied] = useState(false); const [exportingToWorkspace, setExportingToWorkspace] = useState(false); const [advSearchFilterHeight, setAdvSearchFilterHeight] = useState('100vh'); + const [downloadInProgress, setDownloadInProgress] = useState(false); + const [downloadStatusMessage, setDownloadStatusMessage] = useState({ message: '', title: '', active: false }); const handleSearchChange = (ev) => { const { value } = ev.currentTarget; @@ -513,6 +515,10 @@ const Discovery: React.FunctionComponent = (props: Props) => { setExportingToWorkspace={setExportingToWorkspace} filtersVisible={filtersVisible} setFiltersVisible={setFiltersVisible} + downloadInProgress={downloadInProgress} + setDownloadInProgress={setDownloadInProgress} + downloadStatusMessage={downloadStatusMessage} + setDownloadStatusMessage={setDownloadStatusMessage} /> {/* Advanced search panel */} diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index 5712a13473..2a26430f39 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -10,11 +10,13 @@ import { RightOutlined, ExportOutlined, DownloadOutlined, + FileTextOutlined, } from '@ant-design/icons'; import FileSaver from 'file-saver'; import { DiscoveryConfig } from './DiscoveryConfig'; import { fetchWithCreds } from '../actions'; import { manifestServiceApiPath, hostname } from '../localconf'; +import Popup from '../components/Popup'; interface User { username: string @@ -27,8 +29,117 @@ interface Props { filtersVisible: boolean; setFiltersVisible: (boolean) => void; user: User, + downloadInProgress: boolean, + setDownloadInProgress: (boolean) => void, + downloadStatusMessage: { + title: string, + message: string, + active: boolean + }, + setDownloadStatusMessage: (Object) => void; } +const handleDownloadZipClick = async ( + selectedResources: any[], + setDownloadInProgress: (boolean) => void, + setDownloadStatusMessage: (object) => void, + manifestFieldName: string, + maxDownloadSizeBytes: number, +) => { + const studyIDs = selectedResources.map((study) => study.project_number); + let downloadSize = 0; + selectedResources.forEach( + (study) => { + study[manifestFieldName].forEach( + (file) => { + downloadSize += file.file_size; + }, + ); + }, + ); + + if (downloadSize > maxDownloadSizeBytes) { + const maxSizeMB = (maxDownloadSizeBytes / 1000000).toFixed(1); + const downloadSizeMb = (downloadSize / 1000000).toFixed(1); + setDownloadStatusMessage( + { + title: 'Download limit exceeded', + message: ` + The selected studies contain ${downloadSizeMb} MB of data, which exceeds the download limit of ${maxSizeMB} MB. + Please deselect some studies and try again, or use the gen3 client. + `, + active: true, + }, + ); + return; + } + + setDownloadInProgress(true); + setDownloadStatusMessage( + { + title: 'Your download is being prepared', + message: 'Please remain on this page while your download is being prepared.\n\n' + + 'When your download is ready, it will begin automatically. You can close this window.', + active: true, + }, + ); + + const triggerDownloadResponse = await fetch( + '/job/dispatch', + { + method: 'POST', + body: JSON.stringify({ + action: 'batch-export', + input: { + study_ids: studyIDs, + }, + }), + }, + ); + const initialialJobState = await triggerDownloadResponse.json(); + const downloadFailedMessage = { + title: 'Download failed', + message: 'There was a problem preparing your download.' + + 'Please consider using the gen3 client to download these files via a manifest.', + active: true, + }; + + if (initialialJobState === null) { + console.warn('Sower is not configured for batch-export job.'); + setDownloadInProgress(false); + setDownloadStatusMessage(downloadFailedMessage); + } else { + const { uid } = initialialJobState; + const pollForUpdate = async () => { + const statusResponse = await fetch(`/job/status?UID=${uid}`); + const statusObject = await statusResponse.json(); + const { status } = statusObject; + + if (status === 'Failed') { + setDownloadStatusMessage(downloadFailedMessage); + setDownloadInProgress(false); + } else if (status === 'Completed') { + const outputResponse = await fetch(`/job/output?UID=${uid}`); + const outputJSON = await outputResponse.json(); + const url = outputJSON.output; + const message = `Your download has been prepared. If your download doesn't start automatically, please copy and paste this url into your browser:\n\n${url}`; + setDownloadStatusMessage( + { + title: 'Your download is ready', + message, + active: true, + }, + ); + setDownloadInProgress(false); + setTimeout(() => window.open(url), 2000); + } else { + setTimeout(pollForUpdate, 5000); + } + }; + setTimeout(pollForUpdate, 5000); + } +}; + const handleDownloadManifestClick = (config: DiscoveryConfig, selectedResources: any[]) => { const { manifestFieldName } = config.features.exportToWorkspace; if (!manifestFieldName) { @@ -130,6 +241,42 @@ const DiscoveryActionBar = (props: Props) => { && ( {props.selectedResources.length} selected + { + props.config.features.exportToWorkspace.enableDownloadZip + && ( + + ) + } { props.config.features.exportToWorkspace.enableDownloadManifest && ( { : () => { handleRedirectToLoginClick(); }} type='text' disabled={props.selectedResources.length === 0} - icon={} + icon={} > {(props.user.username) ? `${props.config.features.exportToWorkspace.downloadManifestButtonText || 'Download Manifest'}` : `Login to ${props.config.features.exportToWorkspace.downloadManifestButtonText || 'Download Manifest'}`} + )} { {(props.user.username) ? 'Open In Workspace' : 'Login to Open In Workspace'} + { + props.downloadStatusMessage.active + && ( + props.setDownloadStatusMessage({ title: '', message: '', active: false }), + }, + ]} + /> + ) + } + )} diff --git a/src/Discovery/DiscoveryConfig.d.ts b/src/Discovery/DiscoveryConfig.d.ts index 41c9b3e29e..ac70921159 100644 --- a/src/Discovery/DiscoveryConfig.d.ts +++ b/src/Discovery/DiscoveryConfig.d.ts @@ -6,6 +6,9 @@ export interface DiscoveryConfig { enableDownloadManifest: boolean downloadManifestButtonText?: string manifestFieldName: string + enableDownloadZip: boolean + downloadZipButtonText: string + downloadZipMaxSizeBytes: number } // explorationIntegration: { // enabled: boolean // not supported From 1cfcda9c8301d10e4ef7edb73f618b21d78cfd1e Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Fri, 13 Aug 2021 08:23:35 -0500 Subject: [PATCH 07/15] feat(download): refactor polling, switch to modal from popup --- src/Discovery/Discovery.tsx | 4 +- src/Discovery/DiscoveryActionBar.tsx | 204 ++++++++++++--------------- src/Discovery/DiscoveryConfig.d.ts | 1 - 3 files changed, 97 insertions(+), 112 deletions(-) diff --git a/src/Discovery/Discovery.tsx b/src/Discovery/Discovery.tsx index 73cd791c94..29de7728cc 100644 --- a/src/Discovery/Discovery.tsx +++ b/src/Discovery/Discovery.tsx @@ -196,7 +196,9 @@ const Discovery: React.FunctionComponent = (props: Props) => { const [exportingToWorkspace, setExportingToWorkspace] = useState(false); const [advSearchFilterHeight, setAdvSearchFilterHeight] = useState('100vh'); const [downloadInProgress, setDownloadInProgress] = useState(false); - const [downloadStatusMessage, setDownloadStatusMessage] = useState({ message: '', title: '', active: false }); + const [downloadStatusMessage, setDownloadStatusMessage] = useState({ + url: '', message: '', title: '', active: false, + }); const handleSearchChange = (ev) => { const { value } = ev.currentTarget; diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index 2a26430f39..78d8da272e 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -3,6 +3,7 @@ import { Space, Popover, Button, + Modal, } from 'antd'; import { useHistory, useLocation } from 'react-router-dom'; import { @@ -15,8 +16,7 @@ import { import FileSaver from 'file-saver'; import { DiscoveryConfig } from './DiscoveryConfig'; import { fetchWithCreds } from '../actions'; -import { manifestServiceApiPath, hostname } from '../localconf'; -import Popup from '../components/Popup'; +import { manifestServiceApiPath, hostname, jobAPIPath } from '../localconf'; interface User { username: string @@ -34,7 +34,8 @@ interface Props { downloadStatusMessage: { title: string, message: string, - active: boolean + active: boolean, + url: string }, setDownloadStatusMessage: (Object) => void; } @@ -43,101 +44,80 @@ const handleDownloadZipClick = async ( selectedResources: any[], setDownloadInProgress: (boolean) => void, setDownloadStatusMessage: (object) => void, - manifestFieldName: string, - maxDownloadSizeBytes: number, ) => { const studyIDs = selectedResources.map((study) => study.project_number); - let downloadSize = 0; - selectedResources.forEach( - (study) => { - study[manifestFieldName].forEach( - (file) => { - downloadSize += file.file_size; - }, - ); - }, - ); - - if (downloadSize > maxDownloadSizeBytes) { - const maxSizeMB = (maxDownloadSizeBytes / 1000000).toFixed(1); - const downloadSizeMb = (downloadSize / 1000000).toFixed(1); - setDownloadStatusMessage( - { - title: 'Download limit exceeded', - message: ` - The selected studies contain ${downloadSizeMb} MB of data, which exceeds the download limit of ${maxSizeMB} MB. - Please deselect some studies and try again, or use the gen3 client. - `, - active: true, - }, - ); - return; - } - setDownloadInProgress(true); - setDownloadStatusMessage( - { - title: 'Your download is being prepared', - message: 'Please remain on this page while your download is being prepared.\n\n' - + 'When your download is ready, it will begin automatically. You can close this window.', - active: true, - }, - ); - - const triggerDownloadResponse = await fetch( - '/job/dispatch', - { - method: 'POST', - body: JSON.stringify({ - action: 'batch-export', - input: { - study_ids: studyIDs, - }, - }), - }, - ); - const initialialJobState = await triggerDownloadResponse.json(); - const downloadFailedMessage = { - title: 'Download failed', - message: 'There was a problem preparing your download.' - + 'Please consider using the gen3 client to download these files via a manifest.', + setDownloadStatusMessage({ + title: 'Your download is being prepared', + message: 'Please remain on this page while your download is being prepared.\n\n' + + 'When your download is ready, it will begin automatically. You can close this window.', active: true, - }; + url: '', + }); - if (initialialJobState === null) { - console.warn('Sower is not configured for batch-export job.'); + const handleJobError = (err) => { + console.error(err); setDownloadInProgress(false); - setDownloadStatusMessage(downloadFailedMessage); - } else { - const { uid } = initialialJobState; - const pollForUpdate = async () => { - const statusResponse = await fetch(`/job/status?UID=${uid}`); - const statusObject = await statusResponse.json(); - const { status } = statusObject; + setDownloadStatusMessage({ + title: 'Download failed', + message: 'There was a problem preparing your download.' + + 'Please consider using the gen3 client to download these files via a manifest.', + active: true, + url: '', + }); + }; - if (status === 'Failed') { - setDownloadStatusMessage(downloadFailedMessage); - setDownloadInProgress(false); - } else if (status === 'Completed') { - const outputResponse = await fetch(`/job/output?UID=${uid}`); - const outputJSON = await outputResponse.json(); - const url = outputJSON.output; - const message = `Your download has been prepared. If your download doesn't start automatically, please copy and paste this url into your browser:\n\n${url}`; - setDownloadStatusMessage( - { - title: 'Your download is ready', - message, - active: true, + fetchWithCreds({ + path: `${jobAPIPath}dispatch`, + method: 'POST', + body: JSON.stringify({ + action: 'batch-export', + input: { + study_ids: studyIDs, + }, + }), + }).then( + (dispatchResponse) => { + const { uid } = dispatchResponse.data; + const pollForJobStatusUpdate = () => { + fetchWithCreds({ path: `${jobAPIPath}status?UID=${uid}` }).then( + (statusResponse) => { + const { status } = statusResponse.data; + if (status === 'Failed') { + fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( + (outputResponse) => { + setDownloadStatusMessage({ + title: 'Download failed', + message: outputResponse.data.output, + active: true, + url: '', + }); + setDownloadInProgress(false); + }, + ).catch(handleJobError); + } else if (status === 'Completed') { + fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( + (outputResponse) => { + const { output } = outputResponse.data; + setDownloadStatusMessage({ + title: 'Your download is ready', + message: 'Your download has been prepared. If your download doesn\'t start automatically, please follow this direct link:', + active: true, + url: output, + }); + setDownloadInProgress(false); + setTimeout(() => window.open(output), 2000); + }, + ).catch(handleJobError); + } else { + setTimeout(pollForJobStatusUpdate, 5000); + } }, ); - setDownloadInProgress(false); - setTimeout(() => window.open(url), 2000); - } else { - setTimeout(pollForUpdate, 5000); - } - }; - setTimeout(pollForUpdate, 5000); - } + }; + setTimeout(pollForJobStatusUpdate, 5000); + }, + ).catch(handleJobError); }; const handleDownloadManifestClick = (config: DiscoveryConfig, selectedResources: any[]) => { @@ -220,7 +200,7 @@ const DiscoveryActionBar = (props: Props) => {
{/* Advanced search show/hide UI */} { (props.config.features.advSearchFilters && props.config.features.advSearchFilters.enabled) - ? ( + && ( - ) - :
} + )} {/* Export to workspaces button */} { ( @@ -251,8 +230,6 @@ const DiscoveryActionBar = (props: Props) => { props.selectedResources, props.setDownloadInProgress, props.setDownloadStatusMessage, - props.config.features.exportToWorkspace.manifestFieldName, - props.config.features.exportToWorkspace.downloadZipMaxSizeBytes || 250000000, ); } else { handleRedirectToLoginClick(); @@ -261,6 +238,7 @@ const DiscoveryActionBar = (props: Props) => { type='text' disabled={props.selectedResources.length === 0 || props.downloadInProgress === true} icon={} + loading={props.downloadInProgress} > {( () => { @@ -337,22 +315,28 @@ const DiscoveryActionBar = (props: Props) => { {(props.user.username) ? 'Open In Workspace' : 'Login to Open In Workspace'} - { - props.downloadStatusMessage.active - && ( - props.setDownloadStatusMessage({ title: '', message: '', active: false }), - }, - ]} - /> - ) - } - + props.setDownloadStatusMessage({ + title: '', message: '', active: false, url: '', + }) + } + > + Close + + )} + > + { props.downloadStatusMessage.message } + { + props.downloadStatusMessage.url + && {props.downloadStatusMessage.url} + } + )}
diff --git a/src/Discovery/DiscoveryConfig.d.ts b/src/Discovery/DiscoveryConfig.d.ts index ac70921159..d4c3abd746 100644 --- a/src/Discovery/DiscoveryConfig.d.ts +++ b/src/Discovery/DiscoveryConfig.d.ts @@ -8,7 +8,6 @@ export interface DiscoveryConfig { manifestFieldName: string enableDownloadZip: boolean downloadZipButtonText: string - downloadZipMaxSizeBytes: number } // explorationIntegration: { // enabled: boolean // not supported From 5c87ec720818a486927bf0cfca2f4c83a0fe7938 Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Fri, 13 Aug 2021 09:40:24 -0500 Subject: [PATCH 08/15] optional download button text --- src/Discovery/DiscoveryConfig.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discovery/DiscoveryConfig.d.ts b/src/Discovery/DiscoveryConfig.d.ts index d4c3abd746..6cfb801ed5 100644 --- a/src/Discovery/DiscoveryConfig.d.ts +++ b/src/Discovery/DiscoveryConfig.d.ts @@ -7,7 +7,7 @@ export interface DiscoveryConfig { downloadManifestButtonText?: string manifestFieldName: string enableDownloadZip: boolean - downloadZipButtonText: string + downloadZipButtonText?: string } // explorationIntegration: { // enabled: boolean // not supported From 831313217f7081d74ecd1110ffb79235c51510a5 Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Fri, 13 Aug 2021 11:45:20 -0500 Subject: [PATCH 09/15] Update DiscoveryActionBar.tsx --- src/Discovery/DiscoveryActionBar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index 1673f72cf6..c28217303d 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -56,7 +56,6 @@ const handleDownloadZipClick = async ( }); const handleJobError = (err) => { - console.error(err); setDownloadInProgress(false); setDownloadStatusMessage({ title: 'Download failed', From 2e8ba71d3cb029ffe3643c90791d33f9d9667c6e Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Fri, 13 Aug 2021 11:54:42 -0500 Subject: [PATCH 10/15] Update DiscoveryActionBar.tsx --- src/Discovery/DiscoveryActionBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index c28217303d..11b172a3f4 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -55,7 +55,7 @@ const handleDownloadZipClick = async ( url: '', }); - const handleJobError = (err) => { + const handleJobError = () => { setDownloadInProgress(false); setDownloadStatusMessage({ title: 'Download failed', From ebbdc36a3160db9c8fcee8bb544d55eb3470b65a Mon Sep 17 00:00:00 2001 From: Zakir Gowani Date: Fri, 13 Aug 2021 10:50:02 -0700 Subject: [PATCH 11/15] PR feedback - action bar message --- src/Discovery/DiscoveryActionBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index 11b172a3f4..4b64533966 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -60,7 +60,7 @@ const handleDownloadZipClick = async ( setDownloadStatusMessage({ title: 'Download failed', message: 'There was a problem preparing your download.' - + 'Please consider using the gen3 client to download these files via a manifest.', + + 'Please consider using the Gen3 SDK for Python (w/ CLI) to download these files via a manifest.', active: true, url: '', }); From ffd6fc23d94958c02f64220d769ddb684b1d8bd4 Mon Sep 17 00:00:00 2001 From: Zakir Gowani Date: Fri, 13 Aug 2021 13:12:51 -0700 Subject: [PATCH 12/15] fix: links overflow --- src/Discovery/Discovery.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Discovery/Discovery.css b/src/Discovery/Discovery.css index 04b109b27c..d2db7c0d2e 100644 --- a/src/Discovery/Discovery.css +++ b/src/Discovery/Discovery.css @@ -412,6 +412,12 @@ .discovery-modal__attribute-value { text-align: left; padding: 4px; + overflow: hidden; + white-space: pre-wrap; /* CSS3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ } .discovery-modal__attribute-value--multiline { From 912db88ef6a9f43328045e652c76da789beacf59 Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Fri, 13 Aug 2021 16:09:56 -0500 Subject: [PATCH 13/15] feat(download): throw exception on Sower non-200, raise an error message to user --- src/Discovery/DiscoveryActionBar.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index 1673f72cf6..f9360baf59 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -78,14 +78,23 @@ const handleDownloadZipClick = async ( }), }).then( (dispatchResponse) => { + if (dispatchResponse.status !== 200) { + throw new Error(); + } const { uid } = dispatchResponse.data; const pollForJobStatusUpdate = () => { fetchWithCreds({ path: `${jobAPIPath}status?UID=${uid}` }).then( (statusResponse) => { + if (statusResponse.status !== 200) { + throw new Error(); + } const { status } = statusResponse.data; if (status === 'Failed') { fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( (outputResponse) => { + if (outputResponse.status !== 200) { + throw new Error(); + } setDownloadStatusMessage({ title: 'Download failed', message: outputResponse.data.output, @@ -98,6 +107,9 @@ const handleDownloadZipClick = async ( } else if (status === 'Completed') { fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( (outputResponse) => { + if (outputResponse.status !== 200) { + throw new Error(); + } const { output } = outputResponse.data; setDownloadStatusMessage({ title: 'Your download is ready', From 1b0448688bf30ae6517cd982b68e7ec06b772e1d Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Fri, 13 Aug 2021 17:41:51 -0500 Subject: [PATCH 14/15] feat(download): handle empty messages from sower, set download to failed state --- src/Discovery/DiscoveryActionBar.tsx | 65 +++++++++++++++------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index d2b686ebb2..df61f0feb5 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -59,7 +59,7 @@ const handleDownloadZipClick = async ( setDownloadInProgress(false); setDownloadStatusMessage({ title: 'Download failed', - message: 'There was a problem preparing your download.' + message: 'There was a problem preparing your download. ' + 'Please consider using the Gen3 SDK for Python (w/ CLI) to download these files via a manifest.', active: true, url: '', @@ -77,47 +77,54 @@ const handleDownloadZipClick = async ( }), }).then( (dispatchResponse) => { - if (dispatchResponse.status !== 200) { - throw new Error(); - } const { uid } = dispatchResponse.data; + if (dispatchResponse.status !== 200 || !uid) { + handleJobError(); + } const pollForJobStatusUpdate = () => { fetchWithCreds({ path: `${jobAPIPath}status?UID=${uid}` }).then( (statusResponse) => { - if (statusResponse.status !== 200) { - throw new Error(); - } const { status } = statusResponse.data; - if (status === 'Failed') { + if (statusResponse.status !== 200 || !status) { + // usually empty status message means Sower can't find a job by its UID + handleJobError(); + } + else if (status === 'Failed') { fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( (outputResponse) => { - if (outputResponse.status !== 200) { - throw new Error(); + const { output } = outputResponse.data; + if (outputResponse.status !== 200 || !output) { + handleJobError(); + } + else { + setDownloadStatusMessage({ + title: 'Download failed', + message: output, + active: true, + url: '', + }); + setDownloadInProgress(false); } - setDownloadStatusMessage({ - title: 'Download failed', - message: outputResponse.data.output, - active: true, - url: '', - }); - setDownloadInProgress(false); }, ).catch(handleJobError); - } else if (status === 'Completed') { + } + else if (status === 'Completed') { fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( (outputResponse) => { - if (outputResponse.status !== 200) { - throw new Error(); - } const { output } = outputResponse.data; - setDownloadStatusMessage({ - title: 'Your download is ready', - message: 'Your download has been prepared. If your download doesn\'t start automatically, please follow this direct link:', - active: true, - url: output, - }); - setDownloadInProgress(false); - setTimeout(() => window.open(output), 2000); + if (outputResponse.status !== 200 || !output) { + handleJobError(); + } + else { + setDownloadStatusMessage({ + title: 'Your download is ready', + message: 'Your download has been prepared. If your download doesn\'t start automatically, please follow this direct link: ', + active: true, + url: output, + }); + setDownloadInProgress(false); + setTimeout(() => window.open(output), 2000); + } }, ).catch(handleJobError); } else { From 990d7b5ca1f82747e3705092c998db58d7f932c3 Mon Sep 17 00:00:00 2001 From: Matthew Cannalte Date: Mon, 16 Aug 2021 13:18:43 -0500 Subject: [PATCH 15/15] feat(download): add dispatch status checks --- src/Discovery/DiscoveryActionBar.tsx | 114 ++++++++++++++------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/src/Discovery/DiscoveryActionBar.tsx b/src/Discovery/DiscoveryActionBar.tsx index df61f0feb5..fbc92f2caf 100644 --- a/src/Discovery/DiscoveryActionBar.tsx +++ b/src/Discovery/DiscoveryActionBar.tsx @@ -78,62 +78,68 @@ const handleDownloadZipClick = async ( }).then( (dispatchResponse) => { const { uid } = dispatchResponse.data; - if (dispatchResponse.status !== 200 || !uid) { + if (dispatchResponse.status === 403 || dispatchResponse.status === 302) { + setDownloadInProgress(false); + setDownloadStatusMessage({ + title: 'Download failed', + message: 'Unable to authorize download. ' + + 'Please refresh the page and ensure you are logged in.', + active: true, + url: '', + }); + } else if (dispatchResponse.status !== 200 || !uid) { handleJobError(); + } else { + const pollForJobStatusUpdate = () => { + fetchWithCreds({ path: `${jobAPIPath}status?UID=${uid}` }).then( + (statusResponse) => { + const { status } = statusResponse.data; + if (statusResponse.status !== 200 || !status) { + // usually empty status message means Sower can't find a job by its UID + handleJobError(); + } else if (status === 'Failed') { + fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( + (outputResponse) => { + const { output } = outputResponse.data; + if (outputResponse.status !== 200 || !output) { + handleJobError(); + } else { + setDownloadStatusMessage({ + title: 'Download failed', + message: output, + active: true, + url: '', + }); + setDownloadInProgress(false); + } + }, + ).catch(handleJobError); + } else if (status === 'Completed') { + fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( + (outputResponse) => { + const { output } = outputResponse.data; + if (outputResponse.status !== 200 || !output) { + handleJobError(); + } else { + setDownloadStatusMessage({ + title: 'Your download is ready', + message: 'Your download has been prepared. If your download doesn\'t start automatically, please follow this direct link: ', + active: true, + url: output, + }); + setDownloadInProgress(false); + setTimeout(() => window.open(output), 2000); + } + }, + ).catch(handleJobError); + } else { + setTimeout(pollForJobStatusUpdate, 5000); + } + }, + ); + }; + setTimeout(pollForJobStatusUpdate, 5000); } - const pollForJobStatusUpdate = () => { - fetchWithCreds({ path: `${jobAPIPath}status?UID=${uid}` }).then( - (statusResponse) => { - const { status } = statusResponse.data; - if (statusResponse.status !== 200 || !status) { - // usually empty status message means Sower can't find a job by its UID - handleJobError(); - } - else if (status === 'Failed') { - fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( - (outputResponse) => { - const { output } = outputResponse.data; - if (outputResponse.status !== 200 || !output) { - handleJobError(); - } - else { - setDownloadStatusMessage({ - title: 'Download failed', - message: output, - active: true, - url: '', - }); - setDownloadInProgress(false); - } - }, - ).catch(handleJobError); - } - else if (status === 'Completed') { - fetchWithCreds({ path: `${jobAPIPath}output?UID=${uid}` }).then( - (outputResponse) => { - const { output } = outputResponse.data; - if (outputResponse.status !== 200 || !output) { - handleJobError(); - } - else { - setDownloadStatusMessage({ - title: 'Your download is ready', - message: 'Your download has been prepared. If your download doesn\'t start automatically, please follow this direct link: ', - active: true, - url: output, - }); - setDownloadInProgress(false); - setTimeout(() => window.open(output), 2000); - } - }, - ).catch(handleJobError); - } else { - setTimeout(pollForJobStatusUpdate, 5000); - } - }, - ); - }; - setTimeout(pollForJobStatusUpdate, 5000); }, ).catch(handleJobError); };