From 21b586b08f3dde6613859712a9e0577dece564db Mon Sep 17 00:00:00 2001 From: ladeirarodolfo <39910206+ladeirarodolfo@users.noreply.github.com> Date: Fri, 25 Oct 2019 09:49:48 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Allow=20routes=20to=20lo?= =?UTF-8?q?ad=20Google=20Cloud=20DICOM=20Stores=20in=20the=20Study=20List?= =?UTF-8?q?=20(#1069)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- platform/core/src/utils/urlUtil.js | 14 ++- platform/viewer/CHANGELOG.md | 8 -- .../viewer/src/customHooks/usePrevious.js | 9 ++ platform/viewer/src/customHooks/useServer.js | 87 +++++++++++++++---- .../src/googleCloud/api/GoogleCloudApi.js | 4 + .../src/googleCloud/utils/getServers.js | 29 ++++++- platform/viewer/src/routes/routesUtil.js | 47 +++++++--- .../viewer/src/studylist/StudyListRouting.js | 15 +++- .../viewer/src/studylist/StudyListWithData.js | 26 +++++- 9 files changed, 194 insertions(+), 45 deletions(-) create mode 100644 platform/viewer/src/customHooks/usePrevious.js diff --git a/platform/core/src/utils/urlUtil.js b/platform/core/src/utils/urlUtil.js index 83bb52dd0..ddc91ac9b 100644 --- a/platform/core/src/utils/urlUtil.js +++ b/platform/core/src/utils/urlUtil.js @@ -6,7 +6,7 @@ const PARAM_PATTERN_IDENTIFIER = ':'; function toLowerCaseFirstLetter(word) { return word[0].toLowerCase() + word.slice(1); } -const getFilters = (location = {}) => { +const getQueryFilters = (location = {}) => { const { search } = location; if (!search) { @@ -55,13 +55,19 @@ const replaceParam = (path = '', paramKey, paramValue) => { return path; }; +const isValidPath = path => { + const paramPatternPiece = `/${PARAM_PATTERN_IDENTIFIER}`; + return path.indexOf(paramPatternPiece) < 0; +}; + const queryString = { - getQueryFilters: getFilters, + getQueryFilters, }; const paramString = { - parseParam: parseParam, - replaceParam: replaceParam, + isValidPath, + parseParam, + replaceParam, }; export { parse, queryString, paramString }; diff --git a/platform/viewer/CHANGELOG.md b/platform/viewer/CHANGELOG.md index b5eac8cf9..a85e2e459 100644 --- a/platform/viewer/CHANGELOG.md +++ b/platform/viewer/CHANGELOG.md @@ -7,10 +7,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @ohif/viewer - - - - ## [1.11.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.3...@ohif/viewer@1.11.4) (2019-10-23) @@ -18,10 +14,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline * Revert "Revert "fix: MPR initialization"" ([#1065](https://github.com/OHIF/Viewers/issues/1065)) ([c680720](https://github.com/OHIF/Viewers/commit/c680720ce5ead58fdb399e3a356edac18093f5c0)), closes [#1062](https://github.com/OHIF/Viewers/issues/1062) [#1064](https://github.com/OHIF/Viewers/issues/1064) - - - - ## [1.11.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@1.11.2...@ohif/viewer@1.11.3) (2019-10-23) diff --git a/platform/viewer/src/customHooks/usePrevious.js b/platform/viewer/src/customHooks/usePrevious.js new file mode 100644 index 000000000..e6164b564 --- /dev/null +++ b/platform/viewer/src/customHooks/usePrevious.js @@ -0,0 +1,9 @@ +import React, { useEffect, useRef } from 'react'; +export default function usePrevious(value) { + const ref = useRef(); + useEffect(() => { + ref.current = value; + }, [value]); + + return ref.current; +} diff --git a/platform/viewer/src/customHooks/useServer.js b/platform/viewer/src/customHooks/useServer.js index 5d2efb8f3..69b0fa525 100644 --- a/platform/viewer/src/customHooks/useServer.js +++ b/platform/viewer/src/customHooks/useServer.js @@ -1,8 +1,10 @@ import React, { useContext } from 'react'; import GoogleCloudApi from '../googleCloud/api/GoogleCloudApi'; +import usePrevious from './usePrevious'; import * as GoogleCloudUtilServers from '../googleCloud/utils/getServers'; import { useSelector, useDispatch } from 'react-redux'; +import isEqual from 'lodash.isequal'; // Contexts import AppContext from '../context/AppContext'; @@ -32,28 +34,68 @@ const getServers = (appConfig, project, location, dataset, dicomStore) => { wadoRoot: pathUrl, }; servers = GoogleCloudUtilServers.getServers(data, dicomStore); + if (!isValidServer(servers[0], appConfig)) { + return; + } } return servers; }; -const updateServer = ( +const isValidServer = (server, appConfig) => { + if (appConfig.enableGoogleCloudAdapter) { + return GoogleCloudUtilServers.isValidServer(server); + } + + return !!server; +}; + +const setServers = (dispatch, servers) => { + const action = { + type: 'SET_SERVERS', + servers, + }; + dispatch(action); +}; + +const useServerFromUrl = ( + servers = [], + previousServers, + activeServer, + urlBasedServers, appConfig, - dispatch, project, location, dataset, dicomStore ) => { - const servers = getServers(appConfig, project, location, dataset, dicomStore); - - if (servers && servers.length) { - const action = { - type: 'SET_SERVERS', - servers, - }; - dispatch(action); + // update state from url available only when gcloud on + if (!appConfig.enableGoogleCloudAdapter) { + return false; } + + const serverHasChanged = previousServers !== servers && previousServers; + + // do not update from url. use state instead. + if (serverHasChanged) { + return false; + } + + // if no valid urlbased servers + if (!urlBasedServers || !urlBasedServers.length) { + return false; + } else if (!servers.length || !activeServer) { + // no current valid server + return true; + } + + const newServer = urlBasedServers[0]; + + let exists = servers.some( + GoogleCloudUtilServers.isEqualServer.bind(undefined, newServer) + ); + + return !exists; }; export default function useServer({ @@ -64,14 +106,29 @@ export default function useServer({ } = {}) { // Hooks const servers = useSelector(state => state && state.servers); + const previousServers = usePrevious(servers); const dispatch = useDispatch(); + const { appConfig = {} } = useContext(AppContext); - const server = getActiveServer(servers); + const activeServer = getActiveServer(servers); + const urlBasedServers = + getServers(appConfig, project, location, dataset, dicomStore) || []; + const shouldUpdateServer = useServerFromUrl( + servers.servers, + previousServers, + activeServer, + urlBasedServers, + appConfig, + project, + location, + dataset, + dicomStore + ); - if (!server) { - updateServer(appConfig, dispatch, project, location, dataset, dicomStore); - } else { - return server; + if (shouldUpdateServer) { + setServers(dispatch, urlBasedServers); + } else if (isValidServer(activeServer, appConfig)) { + return activeServer; } } diff --git a/platform/viewer/src/googleCloud/api/GoogleCloudApi.js b/platform/viewer/src/googleCloud/api/GoogleCloudApi.js index 2bd527fee..334e84263 100644 --- a/platform/viewer/src/googleCloud/api/GoogleCloudApi.js +++ b/platform/viewer/src/googleCloud/api/GoogleCloudApi.js @@ -29,6 +29,10 @@ class GoogleCloudApi { ); } + getUrlPath(project, location, dataset, dicomStore) { + `/projects/${project}/locations/${location}/datasets/${dataset}/dicomStores/${dicomStore}`; + } + async doRequest(urlStr, config = {}, params = {}) { const url = new URL(urlStr); let data = null; diff --git a/platform/viewer/src/googleCloud/utils/getServers.js b/platform/viewer/src/googleCloud/utils/getServers.js index bab857d39..8fcac04e1 100644 --- a/platform/viewer/src/googleCloud/utils/getServers.js +++ b/platform/viewer/src/googleCloud/utils/getServers.js @@ -29,4 +29,31 @@ const getServers = (data, name) => { ]; }; -export { getServers }; +const isValidServer = server => { + return ( + server && + !!server.dataset && + !!server.dicomStore && + !!server.location && + !!server.project + ); +}; + +const isEqualServer = (server = {}, toCompare = {}) => { + const serverLength = Object.keys(server).length; + const toCompareLength = Object.keys(toCompare).length; + + if (!serverLength || !toCompareLength) { + return false; + } + + return ( + server.dataset === toCompare.dataset && + server.dataset === toCompare.dataset && + server.dicomStore === toCompare.dicomStore && + server.location === toCompare.location && + server.project === toCompare.project + ); +}; + +export { getServers, isValidServer, isEqualServer }; diff --git a/platform/viewer/src/routes/routesUtil.js b/platform/viewer/src/routes/routesUtil.js index fc8aa43c8..552eda18a 100644 --- a/platform/viewer/src/routes/routesUtil.js +++ b/platform/viewer/src/routes/routesUtil.js @@ -65,6 +65,19 @@ const ROUTES_DEF = { return !!appConfig.enableGoogleCloudAdapter; }, }, + list: { + path: + '/projects/:project/locations/:location/datasets/:dataset/dicomStores/:dicomStore', + component: StudyListRouting, + condition: appConfig => { + const showList = + appConfig.showStudyList !== undefined + ? appConfig.showStudyList + : true; + + return showList && !!appConfig.enableGoogleCloudAdapter; + }, + }, }, }; @@ -92,23 +105,33 @@ const getRoutes = appConfig => { return routes; }; +const parsePath = (path, server, params) => { + let _path = path; + const _paramsCopy = Object.assign({}, server, params); + + for (let key in _paramsCopy) { + _path = UrlUtil.paramString.replaceParam(_path, key, _paramsCopy[key]); + } + + return _path; +}; + const parseViewerPath = (appConfig = {}, server = {}, params) => { let viewerPath = ROUTES_DEF.default.viewer.path; if (appConfig.enableGoogleCloudAdapter) { viewerPath = ROUTES_DEF.gcloud.viewer.path; } - const _paramsCopy = Object.assign({}, server, params); - - for (let key in _paramsCopy) { - viewerPath = UrlUtil.paramString.replaceParam( - viewerPath, - key, - _paramsCopy[key] - ); - } - - return viewerPath; + return parsePath(viewerPath, server, params); }; -export { getRoutes, parseViewerPath, reload }; +const parseStudyListPath = (appConfig = {}, server = {}, params) => { + let studyListPath = ROUTES_DEF.default.list.path; + if (appConfig.enableGoogleCloudAdapter) { + studyListPath = ROUTES_DEF.gcloud.list.path || studyListPath; + } + + return parsePath(studyListPath, server, params); +}; + +export { getRoutes, parseViewerPath, parseStudyListPath, reload }; diff --git a/platform/viewer/src/studylist/StudyListRouting.js b/platform/viewer/src/studylist/StudyListRouting.js index 3cb72c0ad..7981538f5 100644 --- a/platform/viewer/src/studylist/StudyListRouting.js +++ b/platform/viewer/src/studylist/StudyListRouting.js @@ -2,17 +2,26 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { withRouter } from 'react-router-dom'; import ConnectedStudyList from './ConnectedStudyList'; - +import useServer from '../customHooks/useServer'; import OHIF from '@ohif/core'; const { urlUtil: UrlUtil } = OHIF.utils; // Contexts import AppContext from '../context/AppContext'; -function StudyListRouting({ location }) { +function StudyListRouting({ match: routeMatch, location: routeLocation }) { + const { + project, + location, + dataset, + dicomStore, + studyInstanceUids, + seriesInstanceUids, + } = routeMatch.params; + const server = useServer({ project, location, dataset, dicomStore }); const { appConfig = {} } = useContext(AppContext); - const filters = UrlUtil.queryString.getQueryFilters(location); + const filters = UrlUtil.queryString.getQueryFilters(routeLocation); let studyListFunctionsEnabled = false; if (appConfig.studyListFunctionsEnabled) { diff --git a/platform/viewer/src/studylist/StudyListWithData.js b/platform/viewer/src/studylist/StudyListWithData.js index cce0c0646..135be828f 100644 --- a/platform/viewer/src/studylist/StudyListWithData.js +++ b/platform/viewer/src/studylist/StudyListWithData.js @@ -13,6 +13,7 @@ import ConnectedDicomFilesUploader from '../googleCloud/ConnectedDicomFilesUploa import ConnectedDicomStorePicker from '../googleCloud/ConnectedDicomStorePicker'; import filesToStudies from '../lib/filesToStudies.js'; +const { urlUtil: UrlUtil } = OHIF.utils; // Contexts import UserManagerContext from '../context/UserManagerContext'; import WhiteLabellingContext from '../context/WhiteLabellingContext'; @@ -202,9 +203,27 @@ class StudyListWithData extends Component { const viewerPath = RoutesUtil.parseViewerPath(appConfig, server, { studyInstanceUids: studyInstanceUID, }); - this.props.history.push(viewerPath); + + if (UrlUtil.paramString.isValidPath(viewerPath)) { + this.props.history.push(viewerPath); + } }; + updateURL(modalOpened) { + if (!modalOpened) { + const { appConfig = {} } = this.context; + const { server } = this.props; + const listPath = RoutesUtil.parseStudyListPath(appConfig, server); + + if (UrlUtil.paramString.isValidPath(listPath)) { + const { location = {} } = this.props.history; + if (location.pathname !== listPath) { + this.props.history.replace(listPath); + } + } + } + } + onSearch = searchData => { this.searchForStudies(searchData); }; @@ -235,9 +254,12 @@ class StudyListWithData extends Component { let healthCareApiWindows = null; if (appConfig.enableGoogleCloudAdapter) { + const modalOpened = this.state.modalComponentId === 'DicomStorePicker'; + this.updateURL(modalOpened); + healthCareApiWindows = ( );