From d8f6991dbe72465080cfc5de39c7ea225702f2e0 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Mon, 17 Jun 2024 22:36:03 -0400 Subject: [PATCH] fix: Use correct external URL for rendered responses with relative URI (#4236) --- .../default/src/DicomWebDataSource/index.js | 7 +-- .../utils/fixBulkDataURI.ts | 21 ++++--- .../src/utils/createRenderedRetrieve.js | 5 ++ .../default/src/utils/getBulkdataValue.js | 7 ++- .../{getDirectURL.js => getDirectURL.ts} | 0 .../src/getSopClassHandlerModule.js | 13 ++-- platform/app/public/config/default.js | 57 ++++++++++++++++- platform/app/public/config/e2e.js | 34 +++++++++- platform/app/public/config/netlify.js | 62 ++++++++++++++++++- 9 files changed, 182 insertions(+), 24 deletions(-) rename extensions/default/src/utils/{getDirectURL.js => getDirectURL.ts} (100%) diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index cbb8c2028..5e9a35bfb 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -14,7 +14,7 @@ import getImageId from './utils/getImageId.js'; import dcmjs from 'dcmjs'; import { retrieveStudyMetadata, deleteStudyMetadataPromise } from './retrieveStudyMetadata.js'; import StaticWadoClient from './utils/StaticWadoClient'; -import getDirectURL from '../utils/getDirectURL.js'; +import getDirectURL from '../utils/getDirectURL'; import { fixBulkDataURI } from './utils/fixBulkDataURI'; const { DicomMetaDictionary, DicomDict } = dcmjs.data; @@ -393,10 +393,10 @@ function createDicomWebApi(dicomWebConfig, servicesManager) { // The value.Value will be set with the bulkdata read value // in which case it isn't necessary to re-read this. if (value && value.BulkDataURI && !value.Value) { + // handle the scenarios where bulkDataURI is relative path + fixBulkDataURI(value, naturalized, dicomWebConfig); // Provide a method to fetch bulkdata value.retrieveBulkData = (options = {}) => { - // handle the scenarios where bulkDataURI is relative path - fixBulkDataURI(value, naturalized, dicomWebConfig); const { mediaType } = options; const useOptions = { @@ -415,7 +415,6 @@ function createDicomWebApi(dicomWebConfig, servicesManager) { : undefined, ...options, }; - // Todo: this needs to be from wado dicom web client return qidoDicomWebClient.retrieveBulkData(useOptions).then(val => { // There are DICOM PDF cases where the first ArrayBuffer in the array is // the bulk data and DICOM video cases where the second ArrayBuffer is diff --git a/extensions/default/src/DicomWebDataSource/utils/fixBulkDataURI.ts b/extensions/default/src/DicomWebDataSource/utils/fixBulkDataURI.ts index 2e592c757..953a7e4ce 100644 --- a/extensions/default/src/DicomWebDataSource/utils/fixBulkDataURI.ts +++ b/extensions/default/src/DicomWebDataSource/utils/fixBulkDataURI.ts @@ -20,16 +20,23 @@ function fixBulkDataURI(value, instance, dicomWebConfig) { // in case of the relative path, make it absolute. The current DICOM standard says // the bulkdataURI is relative to the series. However, there are situations where // it can be relative to the study too - if (!value.BulkDataURI.startsWith('http') && !value.BulkDataURI.startsWith('/')) { - if (dicomWebConfig.bulkDataURI?.relativeResolution === 'studies') { - value.BulkDataURI = `${dicomWebConfig.wadoRoot}/studies/${instance.StudyInstanceUID}/${value.BulkDataURI}`; + const { BulkDataURI } = value; + if (!BulkDataURI.startsWith('http') && !value.BulkDataURI.startsWith('/')) { + const { StudyInstanceUID, SeriesInstanceUID } = instance; + const isInstanceStart = BulkDataURI.startsWith('instances/') || BulkDataURI.startsWith('../'); + if ( + BulkDataURI.startsWith('series/') || + BulkDataURI.startsWith('bulkdata/') || + (dicomWebConfig.bulkDataURI?.relativeResolution === 'studies' && isInstanceStart) + ) { + value.BulkDataURI = `${dicomWebConfig.wadoRoot}/studies/${StudyInstanceUID}/${BulkDataURI}`; } else if ( + isInstanceStart || dicomWebConfig.bulkDataURI?.relativeResolution === 'series' || !dicomWebConfig.bulkDataURI?.relativeResolution ) { - value.BulkDataURI = `${dicomWebConfig.wadoRoot}/studies/${instance.StudyInstanceUID}/series/${instance.SeriesInstanceUID}/${value.BulkDataURI}`; + value.BulkDataURI = `${dicomWebConfig.wadoRoot}/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/${BulkDataURI}`; } - return; } @@ -39,11 +46,11 @@ function fixBulkDataURI(value, instance, dicomWebConfig) { // uri (e.g., bulkData: /bulk/1e, wado root: http://myserver.com/dicomweb, output: http://myserver.com/bulk/1e) // and in case of relative wado root, we need to prepend the bulkdata uri to the wado root (e.g,. bulkData: /bulk/1e // wado root: /dicomweb, output: /bulk/1e) - if (value.BulkDataURI[0] === '/') { + if (BulkDataURI[0] === '/') { if (dicomWebConfig.wadoRoot.startsWith('http')) { // Absolute wado root const url = new URL(dicomWebConfig.wadoRoot); - value.BulkDataURI = `${url.origin}${value.BulkDataURI}`; + value.BulkDataURI = `${url.origin}${BulkDataURI}`; } else { // Relative wado root, we don't need to do anything, bulkdata uri is already correct } diff --git a/extensions/default/src/utils/createRenderedRetrieve.js b/extensions/default/src/utils/createRenderedRetrieve.js index e92e542dd..6e411e31b 100644 --- a/extensions/default/src/utils/createRenderedRetrieve.js +++ b/extensions/default/src/utils/createRenderedRetrieve.js @@ -17,7 +17,12 @@ const createRenderedRetrieve = (config, params) => { const { wadoRoot } = config; const { instance, tag = 'PixelData' } = params; const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance; + const value = instance[tag]; + if (value?.BulkDataURI?.indexOf('?') !== -1) { + // The value instance has parameters, so it should not revert to the rendered + return; + } if (tag === 'PixelData' || tag === 'EncapsulatedDocument') { return `${wadoRoot}/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/instances/${SOPInstanceUID}/rendered`; } diff --git a/extensions/default/src/utils/getBulkdataValue.js b/extensions/default/src/utils/getBulkdataValue.js index 1df10fb97..8857babcf 100644 --- a/extensions/default/src/utils/getBulkdataValue.js +++ b/extensions/default/src/utils/getBulkdataValue.js @@ -22,7 +22,7 @@ const getBulkdataValue = (config, params) => { const value = instance[tag]; - const { SeriesInstanceUID, SOPInstanceUID } = instance; + const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance; const BulkDataURI = (value && value.BulkDataURI) || @@ -32,6 +32,11 @@ const getBulkdataValue = (config, params) => { const acceptUri = BulkDataURI + (hasAccept ? '' : (hasQuery ? '&' : '?') + `accept=${defaultType}`); + if (acceptUri.startsWith('series/')) { + const { wadoRoot } = config; + return `${wadoRoot}/studies/${StudyInstanceUID}/${acceptUri}`; + } + // The DICOMweb standard states that the default is multipart related, and then // separately states that the accept parameter is the URL parameter equivalent of the accept header. return acceptUri; diff --git a/extensions/default/src/utils/getDirectURL.js b/extensions/default/src/utils/getDirectURL.ts similarity index 100% rename from extensions/default/src/utils/getDirectURL.js rename to extensions/default/src/utils/getDirectURL.ts diff --git a/extensions/dicom-video/src/getSopClassHandlerModule.js b/extensions/dicom-video/src/getSopClassHandlerModule.js index 5b0a2162e..e2db17632 100644 --- a/extensions/dicom-video/src/getSopClassHandlerModule.js +++ b/extensions/dicom-video/src/getSopClassHandlerModule.js @@ -53,6 +53,12 @@ const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager) const { Modality, SOPInstanceUID, SeriesDescription = 'VIDEO' } = instance; const { SeriesNumber, SeriesDate, SeriesInstanceUID, StudyInstanceUID, NumberOfFrames, url } = instance; + const videoUrl = dataSource.retrieve.directURL({ + instance, + singlepart: 'video', + tag: 'PixelData', + url, + }); const displaySet = { //plugin: id, Modality, @@ -66,12 +72,7 @@ const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager) SOPClassHandlerId, referencedImages: null, measurements: null, - videoUrl: dataSource.retrieve.directURL({ - instance, - singlepart: 'video', - tag: 'PixelData', - url, - }), + videoUrl, instances: [instance], thumbnailSrc: dataSource.retrieve.directURL({ instance, diff --git a/platform/app/public/config/default.js b/platform/app/public/config/default.js index 62ff50cf9..89154c606 100644 --- a/platform/app/public/config/default.js +++ b/platform/app/public/config/default.js @@ -62,9 +62,10 @@ const config = { omitQuotationForMultipartRequest: true, }, }, + { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb2', + sourceName: 'ohif2', configuration: { friendlyName: 'AWS S3 Static wado secondary server', name: 'aws', @@ -90,6 +91,60 @@ const config = { omitQuotationForMultipartRequest: true, }, }, + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'ohif3', + configuration: { + friendlyName: 'AWS S3 Static wado secondary server', + name: 'aws', + wadoUriRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + qidoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + wadoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'bulkdata,video', + // whether the data source should use retrieveBulkData to grab metadata, + // and in case of relative path, what would it be relative to, options + // are in the series level or study level (some servers like series some study) + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + }, + omitQuotationForMultipartRequest: true, + }, + }, + + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'local5000', + configuration: { + friendlyName: 'Static WADO Local Data', + name: 'DCM4CHEE', + qidoRoot: 'http://localhost:5000/dicomweb', + wadoRoot: 'http://localhost:5000/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: true, + supportsStow: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'video', + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + }, + }, + }, + { namespace: '@ohif/extension-default.dataSourcesModule.dicomwebproxy', sourceName: 'dicomwebproxy', diff --git a/platform/app/public/config/e2e.js b/platform/app/public/config/e2e.js index 018c61044..ed7dcdc75 100644 --- a/platform/app/public/config/e2e.js +++ b/platform/app/public/config/e2e.js @@ -49,7 +49,7 @@ window.config = { }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', - sourceName: 'local', + sourceName: 'local5000', configuration: { friendlyName: 'Static WADO Local Data', name: 'DCM4CHEE', @@ -118,9 +118,10 @@ window.config = { }, }, }, + { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb2', + sourceName: 'ohif2', configuration: { friendlyName: 'AWS S3 Static wado secondary server', name: 'aws', @@ -146,6 +147,35 @@ window.config = { omitQuotationForMultipartRequest: true, }, }, + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'ohif3', + configuration: { + friendlyName: 'AWS S3 Static wado secondary server', + name: 'aws', + wadoUriRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + qidoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + wadoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'bulkdata,video', + // whether the data source should use retrieveBulkData to grab metadata, + // and in case of relative path, what would it be relative to, options + // are in the series level or study level (some servers like series some study) + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + }, + omitQuotationForMultipartRequest: true, + }, + }, + { friendlyName: 'StaticWado default data', namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', diff --git a/platform/app/public/config/netlify.js b/platform/app/public/config/netlify.js index 531bb9b3f..1369fa3bd 100644 --- a/platform/app/public/config/netlify.js +++ b/platform/app/public/config/netlify.js @@ -12,11 +12,11 @@ window.config = { strictZSpacingForVolumeViewport: true, groupEnabledModesFirst: true, // filterQueryParam: false, - defaultDataSourceName: 'dicomweb', + defaultDataSourceName: 'ohif', dataSources: [ { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb', + sourceName: 'ohif', configuration: { friendlyName: 'AWS S3 Static wado server', name: 'aws', @@ -41,9 +41,10 @@ window.config = { omitQuotationForMultipartRequest: true, }, }, + { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb2', + sourceName: 'ohif2', configuration: { friendlyName: 'AWS S3 Static wado secondary server', name: 'aws', @@ -69,6 +70,61 @@ window.config = { omitQuotationForMultipartRequest: true, }, }, + + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'ohif3', + configuration: { + friendlyName: 'AWS S3 Static wado secondary server', + name: 'aws', + wadoUriRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + qidoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + wadoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'bulkdata,video', + // whether the data source should use retrieveBulkData to grab metadata, + // and in case of relative path, what would it be relative to, options + // are in the series level or study level (some servers like series some study) + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + }, + omitQuotationForMultipartRequest: true, + }, + }, + + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'local5000', + configuration: { + friendlyName: 'Static WADO Local Data', + name: 'DCM4CHEE', + qidoRoot: 'http://localhost:5000/dicomweb', + wadoRoot: 'http://localhost:5000/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: true, + supportsStow: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'video', + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + }, + }, + }, + { namespace: '@ohif/extension-default.dataSourcesModule.dicomjson', sourceName: 'dicomjson',