feat(DataSource): Allow for dynamic data source configuration and creation (#3543)

This commit is contained in:
Joe Boccanfuso authored and GitHub committed 2023-07-20 11:43:05 -04:00
1 parent 6d88978710
commit 7bbc213f58
36 files changed
+640 -298

No files matched your search

@@ -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')}