Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Retrieve PDF bulkdata, requesting both octet and pdf #86

Merged
merged 3 commits into from
Jan 5, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 81 additions & 61 deletions src/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { multipartEncode, multipartDecode } from './message.js';
import { multipartEncode, multipartDecode } from './message';
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved

function isObject(obj) {
return typeof obj === 'object' && obj !== null;
Expand Down Expand Up @@ -37,6 +37,8 @@ const MEDIATYPES = {
PNG: 'image/png',
};

let debugLog = () => {};

/**
* @typedef { import("../types/types").InstanceMetadata } InstanceMetadata
*/
Expand Down Expand Up @@ -86,28 +88,28 @@ class DICOMwebClient {
}

if ('qidoURLPrefix' in options) {
console.log(`use URL prefix for QIDO-RS: ${options.qidoURLPrefix}`);
debugLog(`use URL prefix for QIDO-RS: ${options.qidoURLPrefix}`);
this.qidoURL = `${this.baseURL}/${options.qidoURLPrefix}`;
} else {
this.qidoURL = this.baseURL;
}

if ('wadoURLPrefix' in options) {
console.log(`use URL prefix for WADO-RS: ${options.wadoURLPrefix}`);
debugLog(`use URL prefix for WADO-RS: ${options.wadoURLPrefix}`);
this.wadoURL = `${this.baseURL}/${options.wadoURLPrefix}`;
} else {
this.wadoURL = this.baseURL;
}

if ('stowURLPrefix' in options) {
console.log(`use URL prefix for STOW-RS: ${options.stowURLPrefix}`);
debugLog(`use URL prefix for STOW-RS: ${options.stowURLPrefix}`);
this.stowURL = `${this.baseURL}/${options.stowURLPrefix}`;
} else {
this.stowURL = this.baseURL;
}

if (options.singlepart) {
console.log('use singlepart', options.singlepart);
debugLog('use singlepart', options.singlepart);
this.singlepart = options.singlepart === true ? 'bulkdata,video,image' : options.singlepart;
} else {
this.singlepart = '';
Expand All @@ -125,8 +127,21 @@ class DICOMwebClient {

// Verbose - print to console request warnings and errors, default true
this.verbose = options.verbose !== false;

this.setDebug(options.debug);
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved


}

setDebug(debugLevel = false, debugLogFunction = null) {
this.debugLevel = !!debugLevel;
debugLog = debugLogFunction || debugLevel ? console.log : () => {};
}

getDebug() {
return this.debugLevel;
}
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Sets verbose flag.
*
Expand Down Expand Up @@ -194,12 +209,12 @@ class DICOMwebClient {

// Event triggered when upload starts
request.onloadstart = function onloadstart() {
// console.log('upload started: ', url)
debugLog('upload started: ', url)
};

// Event triggered when upload ends
request.onloadend = function onloadend() {
// console.log('upload finished')
debugLog('upload finished')
};

// Handle response message
Expand Down Expand Up @@ -699,7 +714,8 @@ class DICOMwebClient {

/**
* Performs an HTTP GET request that accepts a multipart message
* with a application/octet-stream media type.
* with a application/octet-stream, OR any of the equivalencies for that (eg
* application/pdf etc)
*
* @param {String} url - Unique resource locator
* @param {Object[]} mediaTypes - Acceptable media types and optionally the UIDs of the
Expand All @@ -721,7 +737,7 @@ class DICOMwebClient {
const headers = {};
const defaultMediaType = 'application/octet-stream';
const supportedMediaTypes = {
'1.2.840.10008.1.2.1': [defaultMediaType],
'1.2.840.10008.1.2.1': [...Object.values(MEDIATYPES)],
};

let acceptableMediaTypes = mediaTypes;
Expand Down Expand Up @@ -827,7 +843,8 @@ class DICOMwebClient {

/**
* Builds an accept header field value for HTTP GET multipart request
messages.
messages. Will throw an exception if not acceptable types were found, but
will only log a verbose level message for unacceptable types.
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param {Object[]} mediaTypes - Acceptable media types
* @param {Object[]} supportedMediaTypes - Supported media types
Expand Down Expand Up @@ -863,9 +880,10 @@ class DICOMwebClient {
.includes(mediaType)
) {
if (!mediaType.endsWith('/*') || !mediaType.endsWith('/')) {
throw new Error(
debugLog(
`Media type ${mediaType} is not supported for requested resource`,
);
return;
}
}

Expand Down Expand Up @@ -907,14 +925,21 @@ class DICOMwebClient {
Array.isArray(supportedMediaTypes) &&
!supportedMediaTypes.includes(mediaType)
) {
throw new Error(
`Media type ${mediaType} is not supported for requested resource`,
);
if( this.verbose ) {
console.warn(
`Media type ${mediaType} is not supported for requested resource`,
);
}
return;
}

fieldValueParts.push(fieldValue);
});

if( !fieldValueParts.length ) {
throw new Error(`No acceptable media types found among ${JSON.stringify(mediaTypes)}`);
}

return fieldValueParts.join(', ');
}

Expand Down Expand Up @@ -961,15 +986,15 @@ class DICOMwebClient {
}

/**
* Gets common type of acceptable media types and asserts that only
* Gets common base type of acceptable media types and asserts that only
one type is specified. For example, ``("image/jpeg", "image/jp2")``
will pass, but ``("image/jpeg", "video/mpeg2")`` will raise an
exception.
*
* @param {Object[]} mediaTypes - Acceptable media types and optionally the UIDs of the
corresponding transfer syntaxes
* @private
* @returns {String[]} Common media type
* @returns {String[]} Common media type, eg `image/` for the above example.
*/
static _getCommonMediaType(mediaTypes) {
if (!mediaTypes || !mediaTypes.length) {
Expand All @@ -994,7 +1019,7 @@ class DICOMwebClient {
* @return {Object[]} Study representations (http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_6.7.html#table_6.7.1-2)
*/
searchForStudies(options = {}) {
console.log('search for studies');
debugLog('search for studies');
let withCredentials = false;
let url = `${this.qidoURL}/studies`;
if ('queryParams' in options) {
Expand Down Expand Up @@ -1022,7 +1047,7 @@ class DICOMwebClient {
'Study Instance UID is required for retrieval of study metadata',
);
}
console.log(`retrieve metadata of study ${options.studyInstanceUID}`);
debugLog(`retrieve metadata of study ${options.studyInstanceUID}`);
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/metadata`;
let withCredentials = false;
if ('withCredentials' in options) {
Expand All @@ -1044,7 +1069,7 @@ class DICOMwebClient {
searchForSeries(options = {}) {
let url = this.qidoURL;
if ('studyInstanceUID' in options) {
console.log(`search series of study ${options.studyInstanceUID}`);
debugLog(`search series of study ${options.studyInstanceUID}`);
url += `/studies/${options.studyInstanceUID}`;
}
url += '/series';
Expand Down Expand Up @@ -1081,7 +1106,7 @@ class DICOMwebClient {
);
}

console.log(`retrieve metadata of series ${options.seriesInstanceUID}`);
debugLog(`retrieve metadata of series ${options.seriesInstanceUID}`);
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${options.seriesInstanceUID}/metadata`;
let withCredentials = false;
if ('withCredentials' in options) {
Expand All @@ -1107,17 +1132,17 @@ class DICOMwebClient {
if ('studyInstanceUID' in options) {
url += `/studies/${options.studyInstanceUID}`;
if ('seriesInstanceUID' in options) {
console.log(
debugLog(
`search for instances of series ${options.seriesInstanceUID}`,
);
url += `/series/${options.seriesInstanceUID}`;
} else {
console.log(
debugLog(
`search for instances of study ${options.studyInstanceUID}`,
);
}
} else {
console.log('search for instances');
debugLog('search for instances');
}
url += '/instances';
if ('queryParams' in options) {
Expand Down Expand Up @@ -1191,7 +1216,7 @@ class DICOMwebClient {
'SOP Instance UID is required for retrieval of instance metadata',
);
}
console.log(`retrieve metadata of instance ${options.sopInstanceUID}`);
debugLog(`retrieve metadata of instance ${options.sopInstanceUID}`);
const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${options.seriesInstanceUID}/instances/${options.sopInstanceUID}/metadata`;
let withCredentials = false;
if ('withCredentials' in options) {
Expand Down Expand Up @@ -1232,7 +1257,7 @@ class DICOMwebClient {
'frame numbers are required for retrieval of instance frames',
);
}
console.log(
debugLog(
`retrieve frames ${options.frameNumbers.toString()} of instance ${
options.sopInstanceUID
}`,
Expand Down Expand Up @@ -1287,6 +1312,8 @@ class DICOMwebClient {
'1.2.840.10008.1.2.4.91': ['image/jp2'],
'1.2.840.10008.1.2.4.92': ['image/jpx'],
'1.2.840.10008.1.2.4.93': ['image/jpx'],
'1.2.840.10008.1.2.4.201': ['image/jhc'],
'1.2.840.10008.1.2.4.202': ['image/jhc'],
};

const headers = {
Expand Down Expand Up @@ -1554,7 +1581,7 @@ class DICOMwebClient {
);
}

console.debug(
debugLog(
`retrieve rendered frames ${options.frameNumbers.toString()} of instance ${
options.sopInstanceUID
}`,
Expand Down Expand Up @@ -1881,43 +1908,34 @@ class DICOMwebClient {
return this._httpGet(url, options.headers, 'arraybuffer', null, withCredentials);
}

if (!mediaTypes) {
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
return this._httpGetMultipartApplicationOctetStream(
url,
mediaTypes,
byteRange,
false,
false,
withCredentials,
);
}

const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);

if (commonMediaType === MEDIATYPES.OCTET_STREAM) {
return this._httpGetMultipartApplicationOctetStream(
url,
mediaTypes,
byteRange,
false,
progressCallback,
withCredentials,
);
}
if (commonMediaType.startsWith('image')) {
return this._httpGetMultipartImage(
url,
mediaTypes,
byteRange,
false,
false,
progressCallback,
withCredentials,
);
if (mediaTypes) {
try {
const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes);

if (commonMediaType==='image/') {
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
return this._httpGetMultipartImage(
url,
mediaTypes,
byteRange,
false,
false,
progressCallback,
withCredentials,
);
}
} catch(e) {
// No-op - this happens sometimes if trying to fetch the specific desired type but want to fallback to octet-stream
}
}

throw new Error(
`Media type ${commonMediaType} is not supported for retrieval of bulk data.`,
// Just use the media types provided
return this._httpGetMultipartApplicationOctetStream(
url,
mediaTypes,
byteRange,
false,
progressCallback,
withCredentials,
);
}

Expand Down Expand Up @@ -1954,6 +1972,8 @@ class DICOMwebClient {
options.request,
);
}


}


Expand Down