feat(DataSource): Allow for dynamic data source configuration and creation (#3543)
This commit is contained in:
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')}
|
||||
|
||||
Reference in new issue
Block a user