feat(DataSource): Allow for dynamic data source configuration and creation (#3543)
This commit is contained in:
parent
6d88978710
commit
7bbc213f58
@ -13,6 +13,7 @@ const mappings = {
|
||||
|
||||
let _store = {
|
||||
urls: [],
|
||||
studyInstanceUIDMap: new Map(), // map of urls to array of study instance UIDs
|
||||
// {
|
||||
// url: url1
|
||||
// studies: [Study1, Study2], // if multiple studies
|
||||
@ -41,10 +42,10 @@ const findStudies = (key, value) => {
|
||||
};
|
||||
|
||||
function createDicomJSONApi(dicomJsonConfig) {
|
||||
const { name, wadoRoot } = dicomJsonConfig;
|
||||
const { wadoRoot } = dicomJsonConfig;
|
||||
|
||||
const implementation = {
|
||||
initialize: async ({ params, query, url }) => {
|
||||
initialize: async ({ query, url }) => {
|
||||
if (!url) url = query.get('url');
|
||||
let metaData = getMetaDataByURL(url);
|
||||
|
||||
@ -58,11 +59,7 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
}
|
||||
|
||||
const response = await fetch(url);
|
||||
let data = await response.json();
|
||||
|
||||
const studyInstanceUIDs = data.studies.map(
|
||||
study => study.StudyInstanceUID
|
||||
);
|
||||
const data = await response.json();
|
||||
|
||||
let StudyInstanceUID;
|
||||
let SeriesInstanceUID;
|
||||
@ -89,12 +86,14 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
url,
|
||||
studies: [...data.studies],
|
||||
});
|
||||
|
||||
return studyInstanceUIDs;
|
||||
_store.studyInstanceUIDMap.set(
|
||||
url,
|
||||
data.studies.map(study => study.StudyInstanceUID)
|
||||
);
|
||||
},
|
||||
query: {
|
||||
studies: {
|
||||
mapParams: () => { },
|
||||
mapParams: () => {},
|
||||
search: async param => {
|
||||
const [key, value] = Object.entries(param)[0];
|
||||
const mappedParam = mappings[key];
|
||||
@ -252,12 +251,13 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
return imageIds;
|
||||
},
|
||||
getImageIdsForInstance({ instance, frame }) {
|
||||
const imageIds = getImageId({
|
||||
instance,
|
||||
frame,
|
||||
});
|
||||
const imageIds = getImageId({ instance, frame });
|
||||
return imageIds;
|
||||
},
|
||||
getStudyInstanceUIDs: ({ params, query }) => {
|
||||
const url = query.get('url');
|
||||
return _store.studyInstanceUIDMap.get(url);
|
||||
},
|
||||
};
|
||||
return IWebApiDataSource.create(implementation);
|
||||
}
|
||||
|
||||
@ -41,25 +41,7 @@ function createDicomLocalApi(dicomLocalConfig) {
|
||||
const { name } = dicomLocalConfig;
|
||||
|
||||
const implementation = {
|
||||
initialize: ({ params, query }) => {
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
|
||||
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
|
||||
|
||||
const StudyInstanceUIDs =
|
||||
queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
: [StudyInstanceUIDs];
|
||||
|
||||
// Put SRs at the end of series list to make sure images are loaded first
|
||||
StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
|
||||
const study = DicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
study.series = study.series.sort(customSort);
|
||||
});
|
||||
|
||||
return StudyInstanceUIDsAsArray;
|
||||
},
|
||||
initialize: ({ params, query }) => {},
|
||||
query: {
|
||||
studies: {
|
||||
mapParams: () => {},
|
||||
@ -246,6 +228,29 @@ function createDicomLocalApi(dicomLocalConfig) {
|
||||
deleteStudyMetadataPromise() {
|
||||
console.log('deleteStudyMetadataPromise not implemented');
|
||||
},
|
||||
getStudyInstanceUIDs: ({ params, query }) => {
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
|
||||
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
|
||||
|
||||
const StudyInstanceUIDs =
|
||||
queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
: [StudyInstanceUIDs];
|
||||
|
||||
// Put SRs at the end of series list to make sure images are loaded first
|
||||
let isStudyInCache = false;
|
||||
StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
|
||||
const study = DicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
if (study) {
|
||||
study.series = study.series.sort(customSort);
|
||||
isStudyInCache = true;
|
||||
}
|
||||
});
|
||||
|
||||
return isStudyInCache ? StudyInstanceUIDsAsArray : [];
|
||||
},
|
||||
};
|
||||
return IWebApiDataSource.create(implementation);
|
||||
}
|
||||
|
||||
@ -51,87 +51,77 @@ const metadataProvider = classes.MetadataProvider;
|
||||
* @param {string|bool} singlepart - indicates of the retrieves can fetch singlepart. Options are bulkdata, video, image or boolean true
|
||||
*/
|
||||
function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
const {
|
||||
qidoRoot,
|
||||
wadoRoot,
|
||||
enableStudyLazyLoad,
|
||||
supportsFuzzyMatching,
|
||||
supportsWildcard,
|
||||
supportsReject,
|
||||
staticWado,
|
||||
singlepart,
|
||||
omitQuotationForMultipartRequest,
|
||||
acceptHeader,
|
||||
requestTransferSyntaxUID,
|
||||
} = dicomWebConfig;
|
||||
|
||||
const dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
|
||||
|
||||
const getAuthrorizationHeader = () => {
|
||||
const xhrRequestHeaders = {};
|
||||
const authHeaders = userAuthenticationService.getAuthorizationHeader();
|
||||
if (authHeaders && authHeaders.Authorization) {
|
||||
xhrRequestHeaders.Authorization = authHeaders.Authorization;
|
||||
}
|
||||
return xhrRequestHeaders;
|
||||
};
|
||||
|
||||
const generateWadoHeader = () => {
|
||||
let authorizationHeader = getAuthrorizationHeader();
|
||||
//Generate accept header depending on config params
|
||||
let formattedAcceptHeader = utils.generateAcceptHeader(
|
||||
acceptHeader,
|
||||
requestTransferSyntaxUID,
|
||||
omitQuotationForMultipartRequest
|
||||
);
|
||||
|
||||
return {
|
||||
...authorizationHeader,
|
||||
Accept: formattedAcceptHeader,
|
||||
};
|
||||
};
|
||||
|
||||
const qidoConfig = {
|
||||
url: qidoRoot,
|
||||
staticWado,
|
||||
singlepart,
|
||||
headers: getAuthrorizationHeader(),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
const wadoConfig = {
|
||||
url: wadoRoot,
|
||||
staticWado,
|
||||
singlepart,
|
||||
headers: generateWadoHeader(),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
// TODO -> Two clients sucks, but its better than 1000.
|
||||
// TODO -> We'll need to merge auth later.
|
||||
const qidoDicomWebClient = staticWado
|
||||
? new StaticWadoClient(qidoConfig)
|
||||
: new api.DICOMwebClient(qidoConfig);
|
||||
|
||||
const wadoDicomWebClient = staticWado
|
||||
? new StaticWadoClient(wadoConfig)
|
||||
: new api.DICOMwebClient(wadoConfig);
|
||||
let dicomWebConfigCopy,
|
||||
qidoConfig,
|
||||
wadoConfig,
|
||||
qidoDicomWebClient,
|
||||
wadoDicomWebClient,
|
||||
getAuthrorizationHeader,
|
||||
generateWadoHeader;
|
||||
|
||||
const implementation = {
|
||||
initialize: ({ params, query }) => {
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
|
||||
const queryStudyInstanceUIDs = utils.splitComma(
|
||||
query.getAll('StudyInstanceUIDs')
|
||||
);
|
||||
if (
|
||||
dicomWebConfig.onConfiguration &&
|
||||
typeof dicomWebConfig.onConfiguration === 'function'
|
||||
) {
|
||||
dicomWebConfig = dicomWebConfig.onConfiguration(dicomWebConfig, {
|
||||
params,
|
||||
query,
|
||||
});
|
||||
}
|
||||
|
||||
const StudyInstanceUIDs =
|
||||
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) ||
|
||||
paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
: [StudyInstanceUIDs];
|
||||
return StudyInstanceUIDsAsArray;
|
||||
dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
|
||||
|
||||
getAuthrorizationHeader = () => {
|
||||
const xhrRequestHeaders = {};
|
||||
const authHeaders = userAuthenticationService.getAuthorizationHeader();
|
||||
if (authHeaders && authHeaders.Authorization) {
|
||||
xhrRequestHeaders.Authorization = authHeaders.Authorization;
|
||||
}
|
||||
return xhrRequestHeaders;
|
||||
};
|
||||
|
||||
generateWadoHeader = () => {
|
||||
let authorizationHeader = getAuthrorizationHeader();
|
||||
//Generate accept header depending on config params
|
||||
let formattedAcceptHeader = utils.generateAcceptHeader(
|
||||
dicomWebConfig.acceptHeader,
|
||||
dicomWebConfig.requestTransferSyntaxUID,
|
||||
dicomWebConfig.omitQuotationForMultipartRequest
|
||||
);
|
||||
|
||||
return {
|
||||
...authorizationHeader,
|
||||
Accept: formattedAcceptHeader,
|
||||
};
|
||||
};
|
||||
|
||||
qidoConfig = {
|
||||
url: dicomWebConfig.qidoRoot,
|
||||
staticWado: dicomWebConfig.staticWado,
|
||||
singlepart: dicomWebConfig.singlepart,
|
||||
headers: userAuthenticationService.getAuthorizationHeader(),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
wadoConfig = {
|
||||
url: dicomWebConfig.wadoRoot,
|
||||
staticWado: dicomWebConfig.staticWado,
|
||||
singlepart: dicomWebConfig.singlepart,
|
||||
headers: userAuthenticationService.getAuthorizationHeader(),
|
||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||
};
|
||||
|
||||
// TODO -> Two clients sucks, but its better than 1000.
|
||||
// TODO -> We'll need to merge auth later.
|
||||
qidoDicomWebClient = dicomWebConfig.staticWado
|
||||
? new StaticWadoClient(qidoConfig)
|
||||
: new api.DICOMwebClient(qidoConfig);
|
||||
|
||||
wadoDicomWebClient = dicomWebConfig.staticWado
|
||||
? new StaticWadoClient(wadoConfig)
|
||||
: new api.DICOMwebClient(wadoConfig);
|
||||
},
|
||||
query: {
|
||||
studies: {
|
||||
@ -140,8 +130,8 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
qidoDicomWebClient.headers = getAuthrorizationHeader();
|
||||
const { studyInstanceUid, seriesInstanceUid, ...mappedParams } =
|
||||
mapParams(origParams, {
|
||||
supportsFuzzyMatching,
|
||||
supportsWildcard,
|
||||
supportsFuzzyMatching: dicomWebConfig.supportsFuzzyMatching,
|
||||
supportsWildcard: dicomWebConfig.supportsWildcard,
|
||||
}) || {};
|
||||
|
||||
const results = await qidoSearch(
|
||||
@ -194,7 +184,13 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
* or is already retrieved, or a promise to a URL for such use if a BulkDataURI
|
||||
*/
|
||||
directURL: params => {
|
||||
return getDirectURL({ wadoRoot, singlepart }, params);
|
||||
return getDirectURL(
|
||||
{
|
||||
wadoRoot: dicomWebConfig.wadoRoot,
|
||||
singlepart: dicomWebConfig.singlepart,
|
||||
},
|
||||
params
|
||||
);
|
||||
},
|
||||
bulkDataURI: async ({ StudyInstanceUID, BulkDataURI }) => {
|
||||
qidoDicomWebClient.headers = getAuthrorizationHeader();
|
||||
@ -222,7 +218,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
);
|
||||
}
|
||||
|
||||
if (enableStudyLazyLoad) {
|
||||
if (dicomWebConfig.enableStudyLazyLoad) {
|
||||
return implementation._retrieveSeriesMetadataAsync(
|
||||
StudyInstanceUID,
|
||||
filters,
|
||||
@ -522,10 +518,26 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
getConfig() {
|
||||
return dicomWebConfigCopy;
|
||||
},
|
||||
getStudyInstanceUIDs({ params, query }) {
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
|
||||
const queryStudyInstanceUIDs = utils.splitComma(
|
||||
query.getAll('StudyInstanceUIDs')
|
||||
);
|
||||
|
||||
const StudyInstanceUIDs =
|
||||
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) ||
|
||||
paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
: [StudyInstanceUIDs];
|
||||
|
||||
return StudyInstanceUIDsAsArray;
|
||||
},
|
||||
};
|
||||
|
||||
if (supportsReject) {
|
||||
implementation.reject = dcm4cheeReject(wadoRoot);
|
||||
if (dicomWebConfig.supportsReject) {
|
||||
implementation.reject = dcm4cheeReject(dicomWebConfig.wadoRoot);
|
||||
}
|
||||
|
||||
return IWebApiDataSource.create(implementation);
|
||||
|
||||
@ -18,15 +18,6 @@ function createDicomWebProxyApi(
|
||||
|
||||
const implementation = {
|
||||
initialize: async ({ params, query }) => {
|
||||
let studyInstanceUIDs = [];
|
||||
|
||||
// there seem to be a couple of variations of the case for this parameter
|
||||
const queryStudyInstanceUIDs =
|
||||
query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
|
||||
if (!queryStudyInstanceUIDs) {
|
||||
throw new Error(`No studyInstanceUids in request for '${name}'`);
|
||||
}
|
||||
|
||||
const url = query.get('url');
|
||||
|
||||
if (!url) {
|
||||
@ -39,12 +30,11 @@ function createDicomWebProxyApi(
|
||||
}
|
||||
|
||||
dicomWebDelegate = createDicomWebApi(
|
||||
data.servers.dicomWeb[0],
|
||||
data.servers.dicomWeb[0].configuration,
|
||||
UserAuthenticationService
|
||||
);
|
||||
studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
|
||||
dicomWebDelegate.initialize({ params, query });
|
||||
}
|
||||
return studyInstanceUIDs;
|
||||
},
|
||||
query: {
|
||||
studies: {
|
||||
@ -77,6 +67,18 @@ function createDicomWebProxyApi(
|
||||
dicomWebDelegate.getImageIdsForDisplaySet(...args),
|
||||
getImageIdsForInstance: (...args) =>
|
||||
dicomWebDelegate.getImageIdsForInstance(...args),
|
||||
getStudyInstanceUIDs({ params, query }) {
|
||||
let studyInstanceUIDs = [];
|
||||
|
||||
// there seem to be a couple of variations of the case for this parameter
|
||||
const queryStudyInstanceUIDs =
|
||||
query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
|
||||
if (!queryStudyInstanceUIDs) {
|
||||
throw new Error(`No studyInstanceUids in request for '${name}'`);
|
||||
}
|
||||
studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
|
||||
return studyInstanceUIDs;
|
||||
},
|
||||
};
|
||||
return IWebApiDataSource.create(implementation);
|
||||
}
|
||||
|
||||
@ -31,7 +31,9 @@ function DataSourceSelector() {
|
||||
)
|
||||
.map(ds => (
|
||||
<div key={ds.sourceName}>
|
||||
<h1 className="text-white">{ds.friendlyName}</h1>
|
||||
<h1 className="text-white">
|
||||
{ds.configuration?.friendlyName || ds.friendlyName}
|
||||
</h1>
|
||||
<Button
|
||||
type={ButtonEnums.type.primary}
|
||||
className={classnames('ml-2')}
|
||||
|
||||
@ -13,10 +13,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'DCM4CHEE',
|
||||
// Something here to check build
|
||||
wadoUriRoot: 'https://myserver.com/dicomweb',
|
||||
@ -34,18 +34,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -36,10 +36,10 @@ window.config = {
|
||||
// },
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'aws',
|
||||
// old server
|
||||
// wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
@ -70,26 +70,27 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicomweb delegating proxy',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomwebproxy',
|
||||
sourceName: 'dicomwebproxy',
|
||||
configuration: {
|
||||
friendlyName: 'dicomweb delegating proxy',
|
||||
name: 'dicomwebproxy',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -10,10 +10,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'DCM4CHEE Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'DCM4CHEE Server',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
|
||||
qidoRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
|
||||
|
||||
@ -12,10 +12,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'http://localhost:5985',
|
||||
qidoRoot: 'http://localhost:5985',
|
||||
@ -31,18 +31,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -13,10 +13,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: '/dicomweb',
|
||||
qidoRoot: '/dicomweb',
|
||||
@ -34,18 +34,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -11,10 +11,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'Orthanc Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'Orthanc Server',
|
||||
name: 'Orthanc',
|
||||
wadoUriRoot: '/wado',
|
||||
qidoRoot: '/dicom-web',
|
||||
@ -26,18 +26,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -12,10 +12,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'Orthanc Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'Orthanc Server',
|
||||
name: 'Orthanc',
|
||||
wadoUriRoot: 'http://127.0.0.1/pacs/wado',
|
||||
qidoRoot: 'http://127.0.0.1/pacs/dicom-web',
|
||||
@ -27,18 +27,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -12,10 +12,10 @@ window.config = {
|
||||
defaultDataSourceName: 'e2e',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'StaticWado test data',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'e2e',
|
||||
configuration: {
|
||||
friendlyName: 'StaticWado test data',
|
||||
// The most important field to set for static WADO
|
||||
staticWado: true,
|
||||
name: 'StaticWADO',
|
||||
@ -34,10 +34,10 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'local',
|
||||
configuration: {
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
name: 'DCM4CHEE',
|
||||
qidoRoot: '/dicomweb',
|
||||
wadoRoot: '/dicomweb',
|
||||
@ -54,10 +54,10 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'ohif',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'aws',
|
||||
// old server
|
||||
// wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
@ -98,18 +98,19 @@ window.config = {
|
||||
// },
|
||||
// },
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -36,10 +36,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'GCP',
|
||||
wadoUriRoot:
|
||||
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
|
||||
@ -58,18 +58,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -15,10 +15,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'DCM4CHEE Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'DCM4CHEE Server',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
qidoRoot: 'http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
@ -42,18 +42,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
studyListFunctionsEnabled: true,
|
||||
|
||||
@ -16,10 +16,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'http://localhost/dicom-web',
|
||||
qidoRoot: 'http://localhost/dicom-web',
|
||||
@ -38,18 +38,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -16,10 +16,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
name: 'DCM4CHEE',
|
||||
qidoRoot: '/dicomweb',
|
||||
wadoRoot: '/dicomweb',
|
||||
@ -36,18 +36,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -24,10 +24,10 @@ window.config = {
|
||||
defaultDataSourceName: 'default',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'default',
|
||||
configuration: {
|
||||
friendlyName: 'Static WADO Local Data',
|
||||
name: 'DCM4CHEE',
|
||||
qidoRoot: '/dicomweb',
|
||||
wadoRoot: '/dicomweb',
|
||||
@ -45,10 +45,10 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'ohif',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'aws',
|
||||
// old server
|
||||
// wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
@ -70,10 +70,10 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'AWS S3 OHIF',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'aws',
|
||||
configuration: {
|
||||
friendlyName: 'AWS S3 OHIF',
|
||||
name: 'aws',
|
||||
qidoRoot: 'https://dd32w2rfebxel.cloudfront.net/dicomweb',
|
||||
wadoRoot: 'https://dd32w2rfebxel.cloudfront.net/dicomweb',
|
||||
@ -90,10 +90,10 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'E2E Test Data',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'e2e',
|
||||
configuration: {
|
||||
friendlyName: 'E2E Test Data',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: '/viewer-testdata',
|
||||
qidoRoot: '/viewer-testdata',
|
||||
@ -111,18 +111,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -12,10 +12,10 @@ window.config = {
|
||||
defaultDataSourceName: 'dicomweb',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'aws',
|
||||
wadoUriRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
|
||||
qidoRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
|
||||
@ -41,18 +41,19 @@ window.config = {
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom json',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
|
||||
sourceName: 'dicomjson',
|
||||
configuration: {
|
||||
friendlyName: 'dicom json',
|
||||
name: 'json',
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'dicom local',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
|
||||
sourceName: 'dicomlocal',
|
||||
configuration: {},
|
||||
configuration: {
|
||||
friendlyName: 'dicom local',
|
||||
},
|
||||
},
|
||||
],
|
||||
httpErrorHandler: error => {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import useDebounce from './useDebounce.js';
|
||||
import useQuery from './useQuery.js';
|
||||
import useSearchParams from './useSearchParams.js';
|
||||
import useSearchParams from './useSearchParams';
|
||||
|
||||
export { useDebounce, useQuery, useSearchParams };
|
||||
export { useDebounce, useSearchParams };
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
export default function useQuery() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import useQuery from './useQuery';
|
||||
|
||||
/**
|
||||
* It returns a Map of the query parameters in the URL, where the keys are
|
||||
* lowercase
|
||||
* @returns A function that returns a Map of the query parameters.
|
||||
*/
|
||||
export default function useSearchParams() {
|
||||
const query = useQuery();
|
||||
// make query params case-insensitive
|
||||
const searchParams = new Map();
|
||||
for (const [key, value] of query) {
|
||||
searchParams.set(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
return searchParams;
|
||||
}
|
||||
24
platform/app/src/hooks/useSearchParams.ts
Normal file
24
platform/app/src/hooks/useSearchParams.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { useLocation } from 'react-router';
|
||||
|
||||
/**
|
||||
* It returns a URLSearchParams of the query parameters in the URL, where the keys are
|
||||
* either lowercase or maintain their case based on the lowerCaseKeys parameter.
|
||||
* @param {lowerCaseKeys:boolean} true to return lower case keys; false (default) to maintain casing;
|
||||
* @returns {URLSearchParams}
|
||||
*/
|
||||
export default function useSearchParams(options = { lowerCaseKeys: false }) {
|
||||
const { lowerCaseKeys } = options;
|
||||
const searchParams = new URLSearchParams(useLocation().search);
|
||||
|
||||
if (!lowerCaseKeys) {
|
||||
return searchParams;
|
||||
}
|
||||
|
||||
const lowerCaseSearchParams = new URLSearchParams();
|
||||
|
||||
for (const [key, value] of searchParams) {
|
||||
lowerCaseSearchParams.set(key.toLowerCase(), value);
|
||||
}
|
||||
|
||||
return lowerCaseSearchParams;
|
||||
}
|
||||
@ -1,10 +1,22 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MODULE_TYPES } from '@ohif/core';
|
||||
import { ExtensionManager, MODULE_TYPES } from '@ohif/core';
|
||||
//
|
||||
import { extensionManager } from '../App.tsx';
|
||||
import { useParams, useLocation } from 'react-router';
|
||||
import useSearchParams from '../hooks/useSearchParams.ts';
|
||||
|
||||
/**
|
||||
* Determines if two React Router location objects are the same.
|
||||
*/
|
||||
const areLocationsTheSame = (location0, location1) => {
|
||||
return (
|
||||
location0.pathname === location1.pathname &&
|
||||
location0.search === location1.search &&
|
||||
location0.hash === location1.hash
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Uses route properties to determine the data source that should be passed
|
||||
@ -18,37 +30,8 @@ function DataSourceWrapper(props) {
|
||||
const { children: LayoutTemplate, ...rest } = props;
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
|
||||
// TODO - get the variable from the props all the time...
|
||||
let dataSourceName = new URLSearchParams(location.search).get('datasources');
|
||||
const dataPath = dataSourceName ? `/${dataSourceName}` : '';
|
||||
|
||||
if (!dataSourceName && window.config.defaultDataSourceName) {
|
||||
dataSourceName = window.config.defaultDataSourceName;
|
||||
} else if (!dataSourceName) {
|
||||
// Gets the first defined datasource with the right name
|
||||
// Mostly for historical reasons - new configs should use the defaultDataSourceName
|
||||
const dataSourceModules =
|
||||
extensionManager.modules[MODULE_TYPES.DATA_SOURCE];
|
||||
// TODO: Good usecase for flatmap?
|
||||
const webApiDataSources = dataSourceModules.reduce((acc, curr) => {
|
||||
const mods = [];
|
||||
curr.module.forEach(mod => {
|
||||
if (mod.type === 'webApi') {
|
||||
mods.push(mod);
|
||||
}
|
||||
});
|
||||
return acc.concat(mods);
|
||||
}, []);
|
||||
dataSourceName = webApiDataSources
|
||||
.map(ds => ds.name)
|
||||
.find(it => extensionManager.getDataSources(it)?.[0] !== undefined);
|
||||
}
|
||||
const dataSource = extensionManager.getDataSources(dataSourceName)?.[0];
|
||||
if (!dataSource) {
|
||||
throw new Error(`No data source found for ${dataSourceName}`);
|
||||
}
|
||||
|
||||
const lowerCaseSearchParams = useSearchParams({ lowerCaseKeys: true });
|
||||
const query = useSearchParams();
|
||||
// Route props --> studies.mapParams
|
||||
// mapParams --> studies.search
|
||||
// studies.search --> studies.processResults
|
||||
@ -63,10 +46,102 @@ function DataSourceWrapper(props) {
|
||||
pageNumber: 1,
|
||||
location: 'Not a valid location, causes first load to occur',
|
||||
};
|
||||
|
||||
const getInitialDataSourceName = useCallback(() => {
|
||||
// TODO - get the variable from the props all the time...
|
||||
let dataSourceName = lowerCaseSearchParams.get('datasources');
|
||||
|
||||
if (!dataSourceName && window.config.defaultDataSourceName) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!dataSourceName) {
|
||||
// Gets the first defined datasource with the right name
|
||||
// Mostly for historical reasons - new configs should use the defaultDataSourceName
|
||||
const dataSourceModules =
|
||||
extensionManager.modules[MODULE_TYPES.DATA_SOURCE];
|
||||
// TODO: Good usecase for flatmap?
|
||||
const webApiDataSources = dataSourceModules.reduce((acc, curr) => {
|
||||
const mods = [];
|
||||
curr.module.forEach(mod => {
|
||||
if (mod.type === 'webApi') {
|
||||
mods.push(mod);
|
||||
}
|
||||
});
|
||||
return acc.concat(mods);
|
||||
}, []);
|
||||
dataSourceName = webApiDataSources
|
||||
.map(ds => ds.name)
|
||||
.find(it => extensionManager.getDataSources(it)?.[0] !== undefined);
|
||||
}
|
||||
|
||||
return dataSourceName;
|
||||
}, []);
|
||||
|
||||
const [isDataSourceInitialized, setIsDataSourceInitialized] = useState(false);
|
||||
|
||||
// The path to the data source to be used in the URL for a mode (e.g. mode/dataSourcePath?StudyIntanceUIDs=1.2.3)
|
||||
const [dataSourcePath, setDataSourcePath] = useState(() => {
|
||||
const dataSourceName = getInitialDataSourceName();
|
||||
return dataSourceName ? `/${dataSourceName}` : '';
|
||||
});
|
||||
|
||||
const [dataSource, setDataSource] = useState(() => {
|
||||
const dataSourceName = getInitialDataSourceName();
|
||||
|
||||
if (!dataSourceName) {
|
||||
return extensionManager.getActiveDataSource()[0];
|
||||
}
|
||||
|
||||
const dataSource = extensionManager.getDataSources(dataSourceName)?.[0];
|
||||
if (!dataSource) {
|
||||
throw new Error(`No data source found for ${dataSourceName}`);
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
});
|
||||
|
||||
const [data, setData] = useState(DEFAULT_DATA);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
/**
|
||||
* The effect to initialize the data source whenever it changes. Similar to
|
||||
* whenever a different Mode is entered, the Mode's data source is initialized, so
|
||||
* too this DataSourceWrapper must initialize its data source whenever a different
|
||||
* data source is activated. Furthermore, a data source might be initialized
|
||||
* several times as it gets activated/deactivated because the location URL
|
||||
* might change and data sources initialize based on the URL.
|
||||
*/
|
||||
useEffect(() => {
|
||||
const initializeDataSource = async () => {
|
||||
await dataSource.initialize({ params, query });
|
||||
setIsDataSourceInitialized(true);
|
||||
};
|
||||
|
||||
initializeDataSource();
|
||||
}, [dataSource]);
|
||||
|
||||
useEffect(() => {
|
||||
const dataSourceChangedCallback = () => {
|
||||
setIsDataSourceInitialized(false);
|
||||
setDataSourcePath('');
|
||||
setDataSource(extensionManager.getActiveDataSource()[0]);
|
||||
// Setting data to DEFAULT_DATA triggers a new query just like it does for the initial load.
|
||||
setData(DEFAULT_DATA);
|
||||
};
|
||||
|
||||
const sub = extensionManager.subscribe(
|
||||
ExtensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED,
|
||||
dataSourceChangedCallback
|
||||
);
|
||||
return () => sub.unsubscribe();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDataSourceInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queryFilterValues = _getQueryFilterValues(
|
||||
location.search,
|
||||
STUDIES_LIMIT
|
||||
@ -75,6 +150,7 @@ function DataSourceWrapper(props) {
|
||||
// 204: no content
|
||||
async function getData() {
|
||||
setIsLoading(true);
|
||||
|
||||
const studies = await dataSource.query.studies.search(queryFilterValues);
|
||||
|
||||
setData({
|
||||
@ -102,7 +178,12 @@ function DataSourceWrapper(props) {
|
||||
STUDIES_LIMIT
|
||||
) *
|
||||
(STUDIES_LIMIT - 1);
|
||||
const isLocationUpdated = data.location !== location;
|
||||
// Simply checking data.location !== location is not sufficient because even though the location href (i.e. entire URL)
|
||||
// has not changed, the React Router still provides a new location reference and would result in two study queries
|
||||
// on initial load. Alternatively, window.location.href could be used.
|
||||
const isLocationUpdated =
|
||||
typeof data.location === 'string' ||
|
||||
!areLocationsTheSame(data.location, location);
|
||||
const isDataInvalid =
|
||||
!isSamePage ||
|
||||
(!isLoading && (newOffset !== previousOffset || isLocationUpdated));
|
||||
@ -114,7 +195,15 @@ function DataSourceWrapper(props) {
|
||||
console.warn(ex);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data, location, params, isLoading, setIsLoading]);
|
||||
}, [
|
||||
data,
|
||||
location,
|
||||
params,
|
||||
isLoading,
|
||||
setIsLoading,
|
||||
dataSource,
|
||||
isDataSourceInitialized,
|
||||
]);
|
||||
// queryFilterValues
|
||||
|
||||
// TODO: Better way to pass DataSource?
|
||||
@ -122,7 +211,7 @@ function DataSourceWrapper(props) {
|
||||
<LayoutTemplate
|
||||
{...rest}
|
||||
data={data.studies}
|
||||
dataPath={dataPath}
|
||||
dataPath={dataSourcePath}
|
||||
dataTotal={data.total}
|
||||
dataSource={dataSource}
|
||||
isLoadingData={isLoading}
|
||||
|
||||
@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||
// TODO: DicomMetadataStore should be injected?
|
||||
import { DicomMetadataStore, ServicesManager, utils } from '@ohif/core';
|
||||
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
|
||||
import { useQuery, useSearchParams } from '@hooks';
|
||||
import { useSearchParams } from '@hooks';
|
||||
import ViewportGrid from '@components/ViewportGrid';
|
||||
import Compose from './Compose';
|
||||
import getStudies from './studiesList';
|
||||
@ -95,9 +95,13 @@ export default function ModeRoute({
|
||||
}) {
|
||||
// Parse route params/querystring
|
||||
const location = useLocation();
|
||||
const query = useQuery();
|
||||
|
||||
// The react router DOM placeholder map (see https://reactrouter.com/en/main/hooks/use-params).
|
||||
const params = useParams();
|
||||
const searchParams = useSearchParams();
|
||||
// The URL's query search parameters where the keys casing is maintained
|
||||
const query = useSearchParams();
|
||||
// The URL's query search parameters where the keys are all lower case.
|
||||
const lowerCaseSearchParams = useSearchParams({ lowerCaseKeys: true });
|
||||
|
||||
const [studyInstanceUIDs, setStudyInstanceUIDs] = useState();
|
||||
|
||||
@ -132,8 +136,10 @@ export default function ModeRoute({
|
||||
hangingProtocol,
|
||||
} = mode;
|
||||
|
||||
const runTimeHangingProtocolId = searchParams.get('hangingprotocolid');
|
||||
const token = searchParams.get('token');
|
||||
const runTimeHangingProtocolId = lowerCaseSearchParams.get(
|
||||
'hangingprotocolid'
|
||||
);
|
||||
const token = lowerCaseSearchParams.get('token');
|
||||
|
||||
if (token) {
|
||||
// if a token is passed in, set the userAuthenticationService to use it
|
||||
@ -163,12 +169,11 @@ export default function ModeRoute({
|
||||
const hotkeys = Array.isArray(hotkeyObj) ? hotkeyObj : hotkeyObj?.hotkeys;
|
||||
const hotkeyName = hotkeyObj?.name || 'hotkey-definitions-v2';
|
||||
|
||||
if (dataSourceName === undefined) {
|
||||
dataSourceName = extensionManager.defaultDataSourceName;
|
||||
// An undefined dataSourceName implies that the active data source that is already set in the ExtensionManager should be used.
|
||||
if (dataSourceName !== undefined) {
|
||||
extensionManager.setActiveDataSource(dataSourceName);
|
||||
}
|
||||
|
||||
extensionManager.setActiveDataSource(dataSourceName);
|
||||
|
||||
const dataSource = extensionManager.getActiveDataSource()[0];
|
||||
|
||||
// Only handling one route per mode for now
|
||||
@ -232,11 +237,11 @@ export default function ModeRoute({
|
||||
|
||||
// Todo: this should not be here, data source should not care about params
|
||||
const initializeDataSource = async (params, query) => {
|
||||
const studyInstanceUIDs = await dataSource.initialize({
|
||||
await dataSource.initialize({
|
||||
params,
|
||||
query,
|
||||
});
|
||||
setStudyInstanceUIDs(studyInstanceUIDs);
|
||||
setStudyInstanceUIDs(dataSource.getStudyInstanceUIDs({ params, query }));
|
||||
};
|
||||
|
||||
initializeDataSource(params, query);
|
||||
|
||||
@ -459,7 +459,7 @@ function WorkList({
|
||||
const { component: dicomUploadComponent } =
|
||||
customizationService.get('dicomUploadComponent') ?? {};
|
||||
const uploadProps =
|
||||
dicomUploadComponent && dataSource.getConfig().dicomUploadEnabled
|
||||
dicomUploadComponent && dataSource.getConfig()?.dicomUploadEnabled
|
||||
? {
|
||||
title: 'Upload files',
|
||||
closeButton: true,
|
||||
|
||||
@ -63,16 +63,14 @@ export default function buildModeRoutes({
|
||||
});
|
||||
});
|
||||
|
||||
const defaultDataSourceName = extensionManager.defaultDataSourceName;
|
||||
|
||||
// Add default DataSource route.
|
||||
// Add active DataSource route.
|
||||
// This is the DataSource route for the active data source defined in ExtensionManager.getActiveDataSource
|
||||
const path = `/${mode.routeName}`;
|
||||
|
||||
// TODO move up.
|
||||
const children = () => (
|
||||
<ModeRoute
|
||||
mode={mode}
|
||||
dataSourceName={defaultDataSourceName}
|
||||
extensionManager={extensionManager}
|
||||
servicesManager={servicesManager}
|
||||
commandsManager={commandsManager}
|
||||
|
||||
@ -56,7 +56,7 @@ const createRoutes = ({
|
||||
path: '/',
|
||||
children: DataSourceWrapper,
|
||||
private: true,
|
||||
props: { children: WorkList, servicesManager },
|
||||
props: { children: WorkList, servicesManager, extensionManager },
|
||||
};
|
||||
|
||||
const customRoutes = customizationService.getGlobalCustomization(
|
||||
|
||||
@ -22,6 +22,7 @@ function create({
|
||||
getImageIdsForDisplaySet,
|
||||
getImageIdsForInstance,
|
||||
getConfig,
|
||||
getStudyInstanceUIDs,
|
||||
}) {
|
||||
const defaultQuery = {
|
||||
studies: {
|
||||
@ -73,6 +74,7 @@ function create({
|
||||
getImageIdsForDisplaySet,
|
||||
getImageIdsForInstance,
|
||||
getConfig: getConfig || defaultGetConfig,
|
||||
getStudyInstanceUIDs: getStudyInstanceUIDs,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import MODULE_TYPES from './MODULE_TYPES';
|
||||
import log from '../log';
|
||||
import { AppConfig } from '../types/AppConfig';
|
||||
import { ServicesManager } from '../services';
|
||||
import { PubSubService, ServicesManager } from '../services';
|
||||
import { HotkeysManager, CommandsManager } from '../classes';
|
||||
import { DataSourceDefinition } from '../types';
|
||||
|
||||
/**
|
||||
* This is the arguments given to create the extension.
|
||||
@ -57,7 +58,11 @@ export type CommandsModule = {
|
||||
defaultContext?: string;
|
||||
};
|
||||
|
||||
export default class ExtensionManager {
|
||||
export default class ExtensionManager extends PubSubService {
|
||||
public static readonly EVENTS = {
|
||||
ACTIVE_DATA_SOURCE_CHANGED: 'event::activedatasourcechanged',
|
||||
};
|
||||
|
||||
private _commandsManager: CommandsManager;
|
||||
private _servicesManager: ServicesManager;
|
||||
private _hotkeysManager: HotkeysManager;
|
||||
@ -68,6 +73,7 @@ export default class ExtensionManager {
|
||||
hotkeysManager,
|
||||
appConfig = {},
|
||||
}: ExtensionConstructor) {
|
||||
super(ExtensionManager.EVENTS);
|
||||
this.modules = {};
|
||||
this.registeredExtensionIds = [];
|
||||
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
||||
@ -85,11 +91,20 @@ export default class ExtensionManager {
|
||||
this.dataSourceMap = {};
|
||||
this.dataSourceDefs = {};
|
||||
this.defaultDataSourceName = appConfig.defaultDataSourceName;
|
||||
this.activeDataSource = undefined;
|
||||
this.activeDataSource = appConfig.defaultDataSourceName;
|
||||
}
|
||||
|
||||
public setActiveDataSource(dataSourceName: string): void {
|
||||
this.activeDataSource = dataSourceName;
|
||||
public setActiveDataSource(dataSource: string): void {
|
||||
if (this.activeDataSource === dataSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeDataSource = dataSource;
|
||||
|
||||
this._broadcastEvent(
|
||||
ExtensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED,
|
||||
this.dataSourceDefs[this.activeDataSource]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -330,8 +345,20 @@ export default class ExtensionManager {
|
||||
return this.dataSourceMap[this.activeDataSource];
|
||||
};
|
||||
|
||||
getDataSource = () => {
|
||||
return this.dataSourceMap[this.activeDataSource];
|
||||
/**
|
||||
* Gets the data source definition for the given data source name.
|
||||
* If no data source name is provided, the active data source definition is
|
||||
* returned.
|
||||
* @param dataSourceName the data source name
|
||||
* @returns the data source definition
|
||||
*/
|
||||
getDataSourceDef = dataSourceName => {
|
||||
if (dataSourceName === undefined) {
|
||||
// Default to the activeDataSource
|
||||
dataSourceName = this.activeDataSource;
|
||||
}
|
||||
|
||||
return this.dataSourceDefs[dataSourceName];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -383,10 +410,92 @@ export default class ExtensionManager {
|
||||
});
|
||||
};
|
||||
|
||||
_initDataSourcesModule(extensionModule, extensionId, dataSources = []) {
|
||||
/**
|
||||
* Adds the given data source and optionally sets it as the active data source.
|
||||
* The method does this by first creating the data source.
|
||||
* @param dataSourceDef the data source definition to be added
|
||||
* @param activate flag to indicate if the added data source should be set to the active data source
|
||||
*/
|
||||
addDataSource(
|
||||
dataSourceDef: DataSourceDefinition,
|
||||
options = { activate: false }
|
||||
) {
|
||||
const existingDataSource = this.getDataSources(dataSourceDef.sourceName);
|
||||
if (existingDataSource?.[0]) {
|
||||
// The data source already exists and cannot be added.
|
||||
return;
|
||||
}
|
||||
|
||||
this._createDataSourceInstance(dataSourceDef);
|
||||
|
||||
if (options.activate) {
|
||||
this.setActiveDataSource(dataSourceDef.sourceName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the configuration of the given data source name. It first creates a new data source with
|
||||
* the existing definition and the new configuration passed in.
|
||||
* @param dataSourceName the name of the data source to update
|
||||
* @param dataSourceConfiguration the new configuration to update the data source with
|
||||
*/
|
||||
updateDataSourceConfiguration(
|
||||
dataSourceName: string,
|
||||
dataSourceConfiguration: any
|
||||
) {
|
||||
const existingDataSource = this.getDataSources(dataSourceName);
|
||||
if (!existingDataSource?.[0]) {
|
||||
// Cannot update a non existent data source.
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceDef = this.dataSourceDefs[dataSourceName];
|
||||
// Update the configuration.
|
||||
dataSourceDef.configuration = dataSourceConfiguration;
|
||||
this._createDataSourceInstance(dataSourceDef);
|
||||
|
||||
if (this.activeDataSource === dataSourceName) {
|
||||
// When the active data source is changed/set, fire an event to indicate that its configuration has changed.
|
||||
this._broadcastEvent(
|
||||
ExtensionManager.EVENTS.ACTIVE_DATA_SOURCE_CHANGED,
|
||||
dataSourceDef
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a data source instance from the given definition. The definition is
|
||||
* added to dataSourceDefs and the created instance is added to dataSourceMap.
|
||||
* @param dataSourceDef
|
||||
* @returns
|
||||
*/
|
||||
_createDataSourceInstance(dataSourceDef: DataSourceDefinition) {
|
||||
const module = this.getModuleEntry(dataSourceDef.namespace);
|
||||
|
||||
if (!module) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dataSourceDefs[dataSourceDef.sourceName] = dataSourceDef;
|
||||
|
||||
const { userAuthenticationService } = this._servicesManager.services;
|
||||
dataSources.forEach(dataSource => {
|
||||
this.dataSourceDefs[dataSource.sourceName] = dataSource;
|
||||
const dataSourceInstance = module.createDataSource(
|
||||
dataSourceDef.configuration,
|
||||
userAuthenticationService
|
||||
);
|
||||
|
||||
this.dataSourceMap[dataSourceDef.sourceName] = [dataSourceInstance];
|
||||
}
|
||||
|
||||
_initDataSourcesModule(
|
||||
extensionModule,
|
||||
extensionId,
|
||||
dataSources: Array<DataSourceDefinition> = []
|
||||
): void {
|
||||
extensionModule.forEach(element => {
|
||||
this.modulesMap[
|
||||
`${extensionId}.${MODULE_TYPES.DATA_SOURCE}.${element.name}`
|
||||
] = element;
|
||||
});
|
||||
|
||||
extensionModule.forEach(element => {
|
||||
@ -394,25 +503,10 @@ export default class ExtensionManager {
|
||||
|
||||
dataSources.forEach(dataSource => {
|
||||
if (dataSource.namespace === namespace) {
|
||||
const dataSourceInstance = element.createDataSource(
|
||||
dataSource.configuration,
|
||||
userAuthenticationService
|
||||
);
|
||||
|
||||
if (this.dataSourceMap[dataSource.sourceName]) {
|
||||
this.dataSourceMap[dataSource.sourceName].push(dataSourceInstance);
|
||||
} else {
|
||||
this.dataSourceMap[dataSource.sourceName] = [dataSourceInstance];
|
||||
}
|
||||
this.addDataSource(dataSource);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
extensionModule.forEach(element => {
|
||||
this.modulesMap[
|
||||
`${extensionId}.${MODULE_TYPES.DATA_SOURCE}.${element.name}`
|
||||
] = element;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
7
platform/core/src/types/DataSource.ts
Normal file
7
platform/core/src/types/DataSource.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export type DataSourceDefinition = {
|
||||
// TODO friendlyName to move to configuration; here now for legacy purposes
|
||||
friendlyName: string;
|
||||
namespace: string;
|
||||
sourceName: string;
|
||||
configuration: any;
|
||||
};
|
||||
@ -3,6 +3,7 @@ import * as HangingProtocol from './HangingProtocol';
|
||||
import Services from './Services';
|
||||
import Hotkey from '../classes/Hotkey';
|
||||
import { DisplaySet } from '../services/DisplaySetService/DisplaySetService';
|
||||
import { DataSourceDefinition } from './DataSource';
|
||||
|
||||
export * from '../services/CustomizationService/types';
|
||||
// Separate out some generic types
|
||||
@ -18,4 +19,11 @@ export * from './Color';
|
||||
* Export the types used within the various services and managers, but
|
||||
* not the services/managers themselves, which are exported at the top level.
|
||||
*/
|
||||
export { Extensions, HangingProtocol, Services, Hotkey, DisplaySet };
|
||||
export {
|
||||
Extensions,
|
||||
HangingProtocol,
|
||||
Services,
|
||||
Hotkey,
|
||||
DisplaySet,
|
||||
DataSourceDefinition,
|
||||
};
|
||||
|
||||
@ -33,10 +33,10 @@ window.config = {
|
||||
showStudyList: true,
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
@ -83,10 +83,10 @@ window.config = ({ servicesManager } = {}) => {
|
||||
routerBasename: '/',
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
@ -138,7 +138,38 @@ Example 2, to restricts to either hosptial.com or othersite.com.<br/>
|
||||
`regex: /(https:\/\/hospital.com(\/[0-9A-Za-z.]+)*)|(https:\/\/othersite.com(\/[0-9A-Za-z.]+)*)/` <br/>
|
||||
Example usage:<br/>
|
||||
`http://localhost:3000/?configUrl=http://localhost:3000/config/example.json`<br/>
|
||||
|
||||
- `onConfiguration`: Currently only available for DicomWebDataSource, this option allows the interception of the data source configuration for dynamic values e.g. values coming from url params or query params. Here is an example of building the dicomweb datasource configuration object with values that are based on the route url params:
|
||||
```
|
||||
{
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'gcpdicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'GCP DICOMWeb Server',
|
||||
name: 'gcpdicomweb',
|
||||
qidoSupportsIncludeField: false,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
enableStudyLazyLoad: true,
|
||||
supportsFuzzyMatching: false,
|
||||
supportsWildcard: false,
|
||||
singlepart: 'bulkdata,video,pdf',
|
||||
useBulkDataURI: false,
|
||||
onConfiguration: (dicomWebConfig, options) => {
|
||||
const { params } = options;
|
||||
const { project, location, dataset, dicomStore } = params;
|
||||
const pathUrl = `https://healthcare.googleapis.com/v1/projects/${project}/locations/${location}/datasets/${dataset}/dicomStores/${dicomStore}/dicomWeb`;
|
||||
return {
|
||||
...dicomWebConfig,
|
||||
wadoRoot: pathUrl,
|
||||
qidoRoot: pathUrl,
|
||||
wadoUri: pathUrl,
|
||||
wadoUriRoot: pathUrl,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
```
|
||||
This configuration would allow the user to build a dicomweb configuration from a GCP healthcare api path e.g. http://localhost:3000/projects/your-gcp-project/locations/us-central1/datasets/your-dataset/dicomStores/your-dicom-store/study/1.3.6.1.4.1.1234.5.2.1.1234.1234.123123123123123123123123123123
|
||||
|
||||
|
||||
<!-- **Embedded Use Note:**
|
||||
|
||||
@ -131,10 +131,10 @@ window.config = {
|
||||
showStudyList: true,
|
||||
dataSources: [
|
||||
{
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
|
||||
qidoRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs',
|
||||
|
||||
@ -100,3 +100,66 @@ In `OHIF-v3` we have a central location for the metadata of studies, and they ar
|
||||
located in `DicomMetadataStore`. Your custom datasource can communicate with
|
||||
`DicomMetadataStore` to store, and fetch Study/Series/Instance metadata. We will
|
||||
learn more about `DicomMetadataStore` in services.
|
||||
|
||||
## Adding a Data Source Outside a Module
|
||||
|
||||
A data source can be added outside a module via `ExtensionManager.addDataSource`.
|
||||
The following snippet of code demonstrates how `addDataSource` can be used to add
|
||||
a new DICOMWeb data source for the Google Cloud Healthcare API and set it as the
|
||||
active data source.
|
||||
|
||||
```js
|
||||
extensionManager.addDataSource({
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'google',
|
||||
configuration: {
|
||||
friendlyName: 'dcmjs DICOMWeb Server',
|
||||
name: 'GCP',
|
||||
wadoUriRoot:
|
||||
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
|
||||
qidoRoot:
|
||||
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
|
||||
wadoRoot:
|
||||
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
|
||||
qidoSupportsIncludeField: true,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
enableStudyLazyLoad: true,
|
||||
supportsFuzzyMatching: true,
|
||||
supportsWildcard: false,
|
||||
dicomUploadEnabled: true,
|
||||
omitQuotationForMultipartRequest: true,
|
||||
},
|
||||
{activate:true}
|
||||
});
|
||||
```
|
||||
|
||||
## Updating a Data Source's Configuration
|
||||
|
||||
An existing data source can have its configuration updated using the
|
||||
`ExtensionManager.updateDataSourceConfiguration` method. The following snippet of
|
||||
code demonstrates how `updateDataSourceConfiguration` can be use to update the
|
||||
configuration of an existing DICOMWeb data source (named `dicomweb`) with the
|
||||
configuration for a Google Cloud Healthcare API data source.
|
||||
|
||||
```js
|
||||
extensionManager.updateDataSourceConfiguration( "dicomweb",
|
||||
{
|
||||
name: 'GCP',
|
||||
wadoUriRoot:
|
||||
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
|
||||
qidoRoot:
|
||||
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
|
||||
wadoRoot:
|
||||
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
|
||||
qidoSupportsIncludeField: true,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
enableStudyLazyLoad: true,
|
||||
supportsFuzzyMatching: true,
|
||||
supportsWildcard: false,
|
||||
dicomUploadEnabled: true,
|
||||
omitQuotationForMultipartRequest: true,
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
@ -21,13 +21,23 @@ const extensionManager = new ExtensionManager({
|
||||
appConfig,
|
||||
});
|
||||
```
|
||||
## Events
|
||||
The following events get published by the `ExtensionManager`:
|
||||
|
||||
The `ExtensionManager` only has a few public members:
|
||||
| Event | Description |
|
||||
| ---------------------------- | ------------------------------------------------------ |
|
||||
| ACTIVE_DATA_SOURCE_CHANGED | Fired when the active data source is changed - either replaced with an entirely different one or the existing active data source gets its definition changed via `updateDataSourceConfiguration`. |
|
||||
|
||||
## API
|
||||
The `ExtensionManager` only has the following public API:
|
||||
|
||||
- `setActiveDataSource` - Sets the active data source for the application
|
||||
- `getDataSources` - Returns the registered data sources
|
||||
- `getActiveDataSource` - Returns the currently active data source
|
||||
- `getModuleEntry` - Returns the module entry by the give id.
|
||||
- `addDataSource` - Dynamically adds a data source and optionally sets it as the active data source
|
||||
- `updateDataSourceConfiguration` - Updates the configuration of a specified data source (name).
|
||||
- `getDataSourceDef` - Gets the data source definition for a particular data source name.
|
||||
|
||||
## Accessing Modules
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user