diff --git a/package.json b/package.json
index b508527c1..65ba11e2f 100644
--- a/package.json
+++ b/package.json
@@ -108,7 +108,7 @@
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-transition-group": "^4.1.1",
- "react-viewerbase": "0.17.0",
+ "react-viewerbase": "0.17.6",
"redux": "^4.0.1",
"redux-logger": "^3.0.6",
"redux-oidc": "3.1.x",
diff --git a/public/config/google.js b/public/config/google.js
index 48d64d3c9..ffde232c3 100644
--- a/public/config/google.js
+++ b/public/config/google.js
@@ -21,31 +21,7 @@ window.config = {
revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=',
automaticSilentRenew: true,
revokeAccessTokenOnSignout: true,
- metadata: {
- issuer: "https://accounts.google.com",
- authorization_endpoint: "https://accounts.google.com/o/oauth2/v2/auth",
- token_endpoint: "https://www.googleapis.com/oauth2/v4/token",
- userinfo_endpoint: "https://www.googleapis.com/oauth2/v3/userinfo",
- jwks_uri: "https://www.googleapis.com/oauth2/v3/certs",
- },
- signingKeys: [
- {
- "kid": "6e5508d27965ad7907c232212defa48ed763727e",
- "e": "AQAB",
- "kty": "RSA",
- "alg": "RS256",
- "n": "vOsmJlsBscTxCcOLa6IfCuUnvXI5cBC-o-NIDC1R5O782U_BC67p9dtMLL2UHpQ1wj_b6I4R8cHzddPrPDZ7eTKJw5q18pZU4B5hCmFe9A0JvzyQe3VFPYhKSI5LV_6UIwWdGWjJic8tCJ05AJaVOogSUCn17ss_8KQHSbs66zVbwls9p_ObdHGuzLQE-Y-fkxO5aD9S09DB5dNKuZNL76wgZAhc-HEyo1HkTbDaGAPCk-EpoqfjjoPY2FZGHg5QRCUMdxnYoebjzyO6oaJ8yVniVpOkf-MNF6HltRBbOzE3u6Y2VGjWoj_W0AUidrLC_KQ57URkcAfk1BhMUVJekQ",
- "use": "sig"
- },
- {
- "use": "sig",
- "kid": "84f294c45160088d079fee68138f52133d3e228c",
- "e": "AQAB",
- "kty": "RSA",
- "alg": "RS256",
- "n": "iyzj9wpDDZLCbgbr2zKv3bs8zqjflcVEd7PYMjKGYpoaY2LdqfjFxrwTqd9Ea4m3NIR2giOx9JLQhtqqSSpBJpBBpHmaEd2FCPwd4GQTKJurEP6Ho9HWAuRTMhs8W04pd__HQ0Bc22AEamieGLtzcYfIaAc9g5RCxZdRVbGK0Z0vSOAwN1PC_S76nWGphouHukU40EiwjqC-D9G2xYFbKNb0_NJMxJ5UCenN85FjEii5-oW0wCBmt_1Sr76Q_e0INxfGu6dRf0vGXPvqxkINz2knjl9ec2SvOK2hnmRN4O9zToKH70_DBrsZE0ePDScTOWPHJU2wOyE6gzkL6FdaFQ"
- }
- ]
},
],
+ studyListFunctionsEnabled: true,
}
diff --git a/public/config/local_dcm4chee.js b/public/config/local_dcm4chee.js
index 7abddc0cf..4416fca0c 100644
--- a/public/config/local_dcm4chee.js
+++ b/public/config/local_dcm4chee.js
@@ -21,4 +21,5 @@ window.config = {
},
],
},
+ studyListFunctionsEnabled: true,
}
diff --git a/src/googleCloud/ConnectedDicomFilesUploader.js b/src/googleCloud/ConnectedDicomFilesUploader.js
index 1edf639f0..46719749a 100644
--- a/src/googleCloud/ConnectedDicomFilesUploader.js
+++ b/src/googleCloud/ConnectedDicomFilesUploader.js
@@ -1,15 +1,18 @@
import { connect } from 'react-redux';
import DicomFileUploaderModal from './DicomFileUploaderModal.js';
+import OHIF from 'ohif-core';
const isActive = a => a.active === true;
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive);
- const { authority, client_id } = window.config.oidc[0];
- const oidcStorageKey = `oidc.user:${authority}:${client_id}`;
+
+ // TODO: Not sure I like this approach since it means we are recreating
+ // this function every time redux changes
+ const retrieveAuthHeaderFunction = () => OHIF.DICOMWeb.getAuthorizationHeader(activeServer);
return {
- oidcStorageKey,
+ retrieveAuthHeaderFunction,
url: activeServer && activeServer.qidoRoot,
};
};
diff --git a/src/googleCloud/DicomFileUploaderModal.js b/src/googleCloud/DicomFileUploaderModal.js
index f0e38fe4a..1f113c683 100644
--- a/src/googleCloud/DicomFileUploaderModal.js
+++ b/src/googleCloud/DicomFileUploaderModal.js
@@ -7,7 +7,7 @@ import { withTranslation } from 'react-i18next';
class DicomFileUploaderModal extends Component {
static propTypes = {
url: PropTypes.string,
- oidcStorageKey: PropTypes.string.isRequired,
+ retrieveAuthHeaderFunction: PropTypes.func,
onClose: PropTypes.func,
};
@@ -32,13 +32,13 @@ class DicomFileUploaderModal extends Component {
>
- {this.props.t('Google Cloud Healthcare API - DICOM Upload')}
+ {this.props.t('Upload DICOM Files')}
diff --git a/src/googleCloud/DicomUploader.css b/src/googleCloud/DicomUploader.css
new file mode 100644
index 000000000..80f0b0920
--- /dev/null
+++ b/src/googleCloud/DicomUploader.css
@@ -0,0 +1,20 @@
+.dicom-uploader .button {
+ float: left;
+ margin: 5px;
+}
+
+.invisible-input {
+ position: absolute;
+ display: none;
+ z-index: -1000;
+ max-width: 0 !important;
+ max-height: 0 !important;
+}
+
+.dicom-uploader .table-header {
+ color: #ffffff;
+ font-size: 16px;
+ font-weight: 28px;
+ text-align: left;
+ margin: 20px auto;
+}
diff --git a/src/googleCloud/DicomUploader.js b/src/googleCloud/DicomUploader.js
index 1f03dd06a..fb17f4ab5 100644
--- a/src/googleCloud/DicomUploader.js
+++ b/src/googleCloud/DicomUploader.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { formatFileSize } from './utils/helpers';
import CancellationToken from './utils/CancellationToken';
import dicomUploader from './api/DicomUploadService';
-import './googleCloud.css';
+import './DicomUploader.css';
export default class DicomUploader extends Component {
state = {
@@ -26,7 +26,7 @@ export default class DicomUploader extends Component {
id: PropTypes.string,
event: PropTypes.string,
url: PropTypes.string,
- oidcKey: PropTypes.string,
+ retrieveAuthHeaderFunction: PropTypes.func,
};
filesLeft() {
@@ -94,11 +94,12 @@ export default class DicomUploader extends Component {
const uploadCallback = (fileId, error) =>
uploadContext === this.state.uploadContext &&
this.uploadCallback.call(this, fileId, error);
- dicomUploader.setOidcStorageKey(this.props.oidcKey);
+
+ dicomUploader.setRetrieveAuthHeaderFunction(this.props.retrieveAuthHeaderFunction);
+
dicomUploader.smartUpload(
files.target.files,
this.props.url,
- this.props.oidcKey,
uploadCallback,
cancellationToken
);
@@ -137,14 +138,13 @@ export default class DicomUploader extends Component {
render() {
if (this.state.files === null) {
return (
-
+
- |
+ |
{this.percents()}% {this.filesLeft()}
|
diff --git a/src/googleCloud/api/DicomUploadService.js b/src/googleCloud/api/DicomUploadService.js
index 6bd6a7d83..6db849ab3 100644
--- a/src/googleCloud/api/DicomUploadService.js
+++ b/src/googleCloud/api/DicomUploadService.js
@@ -1,23 +1,16 @@
-import { httpErrorToStr, getOidcToken, checkDicomFile } from '../utils/helpers';
+import { httpErrorToStr, checkDicomFile } from '../utils/helpers';
import { api } from 'dicomweb-client';
class DicomUploadService {
- setOidcStorageKey(oidcStorageKey) {
- /* eslint-disable */
- if (!oidcStorageKey) console.error('OIDC storage key is empty');
- this.oidcStorageKey = oidcStorageKey;
- }
-
- async smartUpload(files, url, authToken, uploadCallback, cancellationToken) {
- /* eslint-disable */
+ async smartUpload(files, url, uploadCallback, cancellationToken) {
const CHUNK_SIZE = 1; // Only one file per request is supported so far
const MAX_PARALLEL_JOBS = 50; // FIXME: tune MAX_PARALLEL_JOBS number
- //
+
let filesArray = Array.from(files);
if (filesArray.length === 0) {
- console.warn('No files are supplied for uploading');
- return;
+ throw new Error('No files were provided.');
}
+
let parallelJobsCount = Math.min(filesArray.length, MAX_PARALLEL_JOBS);
let completed = false;
@@ -30,7 +23,7 @@ class DicomUploadService {
try {
if (chunk.length > 1) throw new Error('Not implemented');
if (chunk.length === 1)
- await this.simpleUpload(chunk[0], url, authToken);
+ await this.simpleUpload(chunk[0], url);
} catch (err) {
// It looks like a stupid bug of Babel that err is not an actual Exception object
error = httpErrorToStr(err);
@@ -51,13 +44,13 @@ class DicomUploadService {
});
}
- async simpleUpload(file, url, authToken) {
- /* eslint-disable */
+ async simpleUpload(file, url) {
const client = this.getClient(url);
const loadedFile = await this.readFile(file);
const content = loadedFile.content;
if (!checkDicomFile(content))
- throw new Error('The file has a wrong DICOM header');
+ throw new Error('This is not a valid DICOM file.');
+
await client.storeInstances({ datasets: [content] });
}
@@ -77,13 +70,17 @@ class DicomUploadService {
});
}
+ setRetrieveAuthHeaderFunction(func) {
+ this.retrieveAuthHeaderFunc = func;
+ }
+
getClient(url) {
- if (!this.oidcStorageKey) throw new Error('OIDC storage key is not set');
- const accessToken = getOidcToken(this.oidcStorageKey);
- if (!accessToken) throw new Error('OIDC access_token is not set');
+ const headers = this.retrieveAuthHeaderFunc();
+
+ // TODO: a bit weird we are creating a new dicomweb client instance for every upload
return new api.DICOMwebClient({
url,
- headers: { Authorization: 'Bearer ' + accessToken },
+ headers
});
}
}
diff --git a/src/googleCloud/googleCloud.css b/src/googleCloud/googleCloud.css
index b83d718cd..9315165aa 100644
--- a/src/googleCloud/googleCloud.css
+++ b/src/googleCloud/googleCloud.css
@@ -94,13 +94,6 @@
background-color: #000000;
}
-.gcp-invisible-input {
- position: absolute;
- display: none;
- z-index: -1000;
- max-width: 0 !important;
- max-height: 0 !important;
-}
.gcp-hidden {
display: none;
}
@@ -148,7 +141,3 @@
margin: 20px auto;
}
-.gcp-dicom-uploader .button {
- float: left;
- margin: 5px;
-}
diff --git a/src/googleCloud/utils/helpers.js b/src/googleCloud/utils/helpers.js
index fda924c39..ba27ce5ed 100644
--- a/src/googleCloud/utils/helpers.js
+++ b/src/googleCloud/utils/helpers.js
@@ -26,11 +26,6 @@ export function httpErrorToStr(error) {
return error.message || 'Unknown error.';
}
-export function getOidcToken(oidcStorageKey) {
- const oidcConfigStr = sessionStorage.getItem(oidcStorageKey);
- if (oidcConfigStr) return JSON.parse(oidcConfigStr).access_token;
-}
-
/* eslint-disable */
export function checkDicomFile(arrayBuffer) {
if (arrayBuffer.length <= 132) return false;
diff --git a/src/studylist/StudyListRouting.js b/src/studylist/StudyListRouting.js
index e156592fc..8b7ea8a63 100644
--- a/src/studylist/StudyListRouting.js
+++ b/src/studylist/StudyListRouting.js
@@ -24,7 +24,14 @@ function getFilters({ search }) {
function StudyListRouting({ location }) {
const filters = location ? getFilters(location) : undefined;
- return ;
+ let studyListFunctionsEnabled = false;
+ if (window.config && window.config.studyListFunctionsEnabled) {
+ studyListFunctionsEnabled = window.config.studyListFunctionsEnabled;
+ }
+ return ;
}
StudyListRouting.propTypes = {
diff --git a/src/studylist/StudyListWithData.js b/src/studylist/StudyListWithData.js
index 5749baf58..2a51709cc 100644
--- a/src/studylist/StudyListWithData.js
+++ b/src/studylist/StudyListWithData.js
@@ -27,6 +27,11 @@ class StudyListWithData extends Component {
server: PropTypes.object,
user: PropTypes.object,
history: PropTypes.object,
+ studyListFunctionsEnabled: PropTypes.bool
+ };
+
+ static defaultProps = {
+ studyListFunctionsEnabled: true
};
static rowsPerPage = 25;
@@ -147,7 +152,7 @@ class StudyListWithData extends Component {
};
onImport = () => {
- //console.log('onImport');
+ this.openModal('DicomFilesUploader')
};
openModal = modalComponentId => {
@@ -176,9 +181,6 @@ class StudyListWithData extends Component {
render() {
const onDrop = async acceptedFiles => {
- // Do something with the files
- console.warn(acceptedFiles);
-
try {
const studies = await filesToStudies(acceptedFiles);
@@ -200,16 +202,10 @@ class StudyListWithData extends Component {
// TODO: This should probably be a prop
if (window.config.enableGoogleCloudAdapter) {
healthCareApiWindows = (
- <>
-
-
- >
+
);
healthCareApiButtons = (
@@ -223,24 +219,16 @@ class StudyListWithData extends Component {
>
{this.props.t('Change DICOM Store')}
-
);
}
- console.warn(this.state.studies);
-
const studyList = (
{this.state.studies ? (
+ {this.props.studyListFunctionsEnabled ? : null}
{healthCareApiButtons}
{healthCareApiWindows}
diff --git a/yarn.lock b/yarn.lock
index 7aec97441..599e92790 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12700,10 +12700,10 @@ react-transition-group@^4.1.1:
loose-envify "^1.4.0"
prop-types "^15.6.2"
-react-viewerbase@0.17.0:
- version "0.17.0"
- resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.17.0.tgz#e741f94b24b6cef419fdd32b9bcfcdcfe81783dc"
- integrity sha512-fy51pHrdhdXcPg6Pb1l8S5fzxlenFVhRUe80ZFq1aK9omrQXgaBF0uhm537WREyx2CS0/ZOD/wQnnbyjqEvYdQ==
+react-viewerbase@0.17.6:
+ version "0.17.6"
+ resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.17.6.tgz#21dcc3a2a48174d235bda47aad97c339dcf3135a"
+ integrity sha512-8kbXBbz1sIqlpLBWGjeSyua/axeAxNqy28DLPVVZlBuoTjDwyhdliuZhkDiQOHpv0POuYn3zxZdZcus7jAd5iQ==
dependencies:
"@babel/runtime" "7.2.0"
"@ohif/i18n" "0.2.1"