Skip to content

Commit

Permalink
Apply Alireza review
Browse files Browse the repository at this point in the history
  • Loading branch information
Punzo committed Sep 28, 2022
1 parent 6567df0 commit 55628dd
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 182 deletions.
119 changes: 1 addition & 118 deletions src/imageLoader/wadors/getPixelData.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { xhrRequest } from '../internal/index.js';
import findIndexOfString from './findIndexOfString.js';
import buildMultipartAcceptHeaderFieldValue from '../../shared/mediaTypesUtils/buildMultipartAcceptHeaderFieldValue.js';

function findBoundary(header) {
for (let i = 0; i < header.length; i++) {
Expand Down Expand Up @@ -29,124 +30,6 @@ function uint8ArrayToString(data, offset, length) {
return str;
}

/**
* Asserts that a given media type is valid.
*
* @params {String} mediaType media type
*/

function assertMediaTypeIsValid(mediaType) {
if (!mediaType) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
const sepIndex = mediaType.indexOf('/');

if (sepIndex === -1) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
const mediaTypeType = mediaType.slice(0, sepIndex);

const types = ['application', 'image', 'text', 'video'];

if (!types.includes(mediaTypeType)) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
if (mediaType.slice(sepIndex + 1).includes('/')) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
}

/**
* Parses media type and extracts its type and subtype.
*
* @param mediaType e.g. image/jpeg
* @private
*/
function parseMediaType(mediaType) {
assertMediaTypeIsValid(mediaType);

return mediaType.split('/');
}

/**
* Builds an accept header field value for HTTP GET multipart request
messages.
*
* @param {Array} mediaTypes Acceptable media types
* @param {Object} supportedMediaTypes Supported media types
*/

function buildMultipartAcceptHeaderFieldValue(mediaTypes, supportedMediaTypes) {
if (!Array.isArray(mediaTypes)) {
throw new Error('Acceptable media types must be provided as an Array');
}

if (typeof supportedMediaTypes !== 'object') {
throw new Error(
'Supported media types must be provided as an Array or an Object'
);
}

const fieldValueParts = [];

mediaTypes.forEach((item) => {
const { transferSyntaxUID, mediaType } = item;

assertMediaTypeIsValid(mediaType);

let fieldValue = `multipart/related; type="${mediaType}"`;

// SupportedMediaTypes is a lookup table that maps Transfer Syntax UID
// to one or more Media Types
if (!Object.values(supportedMediaTypes).flat(1).includes(mediaType)) {
if (
(!mediaType.endsWith('/*') || !mediaType.endsWith('/')) &&
mediaType !== 'application/octet-stream'
) {
throw new Error(
`Media type ${mediaType} is not supported for requested resource`
);
}
}
if (transferSyntaxUID) {
if (transferSyntaxUID !== '*') {
if (!Object.keys(supportedMediaTypes).includes(transferSyntaxUID)) {
throw new Error(
`Transfer syntax ${transferSyntaxUID} is not supported for requested resource`
);
}
const expectedMediaTypes = supportedMediaTypes[transferSyntaxUID];

if (!expectedMediaTypes.includes(mediaType)) {
const actualType = parseMediaType(mediaType)[0];

expectedMediaTypes.map((expectedMediaType) => {
const expectedType = parseMediaType(expectedMediaType)[0];

const haveSameType = actualType === expectedType;

if (
haveSameType &&
(mediaType.endsWith('/*') || mediaType.endsWith('/'))
) {
return null;
}

throw new Error(
`Transfer syntax ${transferSyntaxUID} is not supported for requested resource`
);
});
}
}
fieldValue += `; transfer-syntax=${transferSyntaxUID}`;
}

fieldValueParts.push(fieldValue);
});

return fieldValueParts.join(', ');
}

function getPixelData(uri, imageId, mediaTypes) {
const supportedMediaTypes = {
'1.2.840.10008.1.2.5': ['image/x-dicom-rle'],
Expand Down
65 changes: 1 addition & 64 deletions src/imageLoader/wadors/loadImage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import external from '../../externalModules.js';
import getPixelData from './getPixelData.js';
import createImage from '../createImage.js';

import { mediaTypes } from '../../shared/mediaTypesUtils/mediaTypes.js';
/**
* Helper method to extract the transfer-syntax from the response of the server.
* @param {string} contentType The value of the content-type header as returned by the WADO-RS server.
Expand Down Expand Up @@ -72,69 +72,6 @@ function loadImage(imageId, options = {}) {
const promise = new Promise((resolve, reject) => {
// TODO: load bulk data items that we might need

const xdicomrleMediaType = 'image/x-dicom-rle';
const xdicomrleTransferSyntaxUID = '1.2.840.10008.1.2.5';
const jpegMediaType = 'image/jpeg';
const jpegTransferSyntaxUID1 = '1.2.840.10008.1.2.4.50';
const jpegTransferSyntaxUID2 = '1.2.840.10008.1.2.4.51';
const jpegTransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.57';
const jllMediaType = 'image/jll';
const jlllTransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.70';
const jlsMediaType = 'image/jls';
const jlsTransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.80';
const jlsTransferSyntaxUID = '1.2.840.10008.1.2.4.81';
const jp2MediaType = 'image/jp2';
const jp2TransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.90';
const jp2TransferSyntaxUID = '1.2.840.10008.1.2.4.91';
const octetStreamMediaType = 'application/octet-stream';
const octetStreamTransferSyntaxUID = '*';
const mediaTypes = [];

mediaTypes.push(
...[
{
mediaType: xdicomrleMediaType,
transferSyntaxUID: xdicomrleTransferSyntaxUID,
},
{
mediaType: jpegMediaType,
transferSyntaxUID: jpegTransferSyntaxUID1,
},
{
mediaType: jpegMediaType,
transferSyntaxUID: jpegTransferSyntaxUID2,
},
{
mediaType: jpegMediaType,
transferSyntaxUID: jpegTransferSyntaxUIDlossless,
},
{
mediaType: jllMediaType,
transferSyntaxUID: jlllTransferSyntaxUIDlossless,
},
{
mediaType: jlsMediaType,
transferSyntaxUID: jlsTransferSyntaxUIDlossless,
},
{
mediaType: jlsMediaType,
transferSyntaxUID: jlsTransferSyntaxUID,
},
{
mediaType: jp2MediaType,
transferSyntaxUID: jp2TransferSyntaxUIDlossless,
},
{
mediaType: jp2MediaType,
transferSyntaxUID: jp2TransferSyntaxUID,
},
{
mediaType: octetStreamMediaType,
transferSyntaxUID: octetStreamTransferSyntaxUID,
},
]
);

function sendXHR(imageURI, imageId, mediaTypes) {
// get the pixel data from the server
return getPixelData(imageURI, imageId, mediaTypes)
Expand Down
26 changes: 26 additions & 0 deletions src/shared/mediaTypesUtils/assertMediaTypeIsValid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Asserts that a given media type is valid.
*
* @params {String} mediaType media type
*/

export default function assertMediaTypeIsValid(mediaType) {
if (!mediaType) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
const sepIndex = mediaType.indexOf('/');

if (sepIndex === -1) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
const mediaTypeType = mediaType.slice(0, sepIndex);

const types = ['application', 'image', 'text', 'video'];

if (!types.includes(mediaTypeType)) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
if (mediaType.slice(sepIndex + 1).includes('/')) {
throw new Error(`Not a valid media type: ${mediaType}`);
}
}
102 changes: 102 additions & 0 deletions src/shared/mediaTypesUtils/buildMultipartAcceptHeaderFieldValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import assertMediaTypeIsValid from './assertMediaTypeIsValid.js';

/**
* Builds an accept header field value for HTTP GET multipart request messages.
*
* Takes in input a media types array of type [{mediaType, transferSyntaxUID}, ... ],
* cross-compares with a map defining the supported media types per transferSyntaxUID
* and finally composes a string for the accept header field value as in example below:
*
* "multipart/related; type="image/x-dicom-rle"; transfer-syntax=1.2.840.10008.1.2.5,
* multipart/related; type="image/jpeg"; transfer-syntax=1.2.840.10008.1.2.4.50,
* multipart/related; type="application/octet-stream"; transfer-syntax=*"
*
* NOTE: the xhr request will try to fetch with all the transfer-syntax syntaxes
* specified in the accept header field value in descending order.
* The first element ("image/x-dicom-rle" in this example) has the highest priority.
*
* @param {Array} mediaTypes Acceptable media types
* @param {Object} supportedMediaTypes Supported media types
*
* @returns {string} accept header field value
*/

export default function buildMultipartAcceptHeaderFieldValue(
mediaTypes,
supportedMediaTypes
) {
if (!Array.isArray(mediaTypes)) {
throw new Error('Acceptable media types must be provided as an Array');
}

if (typeof supportedMediaTypes !== 'object') {
throw new Error(
'Supported media types must be provided as an Array or an Object'
);
}

const supportedMediaTypesArray = Object.values(supportedMediaTypes).flat(1);

supportedMediaTypesArray.forEach((supportedMediaType) => {
assertMediaTypeIsValid(supportedMediaType);
});

const fieldValueParts = [];

mediaTypes.forEach((item) => {
const { transferSyntaxUID, mediaType } = item;

assertMediaTypeIsValid(mediaType);

let fieldValue = `multipart/related; type="${mediaType}"`;

// supportedMediaTypesArray is a lookup table that maps Transfer Syntax UID
// to one or more Media Types
if (!supportedMediaTypesArray.includes(mediaType)) {
if (
(!mediaType.endsWith('/*') || !mediaType.endsWith('/')) &&
mediaType !== 'application/octet-stream'
) {
throw new Error(
`Media type ${mediaType} is not supported for requested resource`
);
}
}
if (transferSyntaxUID) {
if (transferSyntaxUID !== '*') {
if (!Object.keys(supportedMediaTypes).includes(transferSyntaxUID)) {
throw new Error(
`Transfer syntax ${transferSyntaxUID} is not supported for requested resource`
);
}
const expectedMediaTypes = supportedMediaTypes[transferSyntaxUID];

if (!expectedMediaTypes.includes(mediaType)) {
const actualType = mediaType.split('/')[0];

expectedMediaTypes.map((expectedMediaType) => {
const expectedType = expectedMediaType.split('/')[0];

const haveSameType = actualType === expectedType;

if (
haveSameType &&
(mediaType.endsWith('/*') || mediaType.endsWith('/'))
) {
return null;
}

throw new Error(
`Transfer syntax ${transferSyntaxUID} is not supported for requested resource`
);
});
}
}
fieldValue += `; transfer-syntax=${transferSyntaxUID}`;
}

fieldValueParts.push(fieldValue);
});

return fieldValueParts.join(', ');
}
4 changes: 4 additions & 0 deletions src/shared/mediaTypesUtils/mediaTypeJLL.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const jllMediaType = 'image/jll';
const jllTransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.70';

export { jllMediaType, jllTransferSyntaxUIDlossless };
5 changes: 5 additions & 0 deletions src/shared/mediaTypesUtils/mediaTypeJLS.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const jlsMediaType = 'image/jls';
const jlsTransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.80';
const jlsTransferSyntaxUID = '1.2.840.10008.1.2.4.81';

export { jlsMediaType, jlsTransferSyntaxUIDlossless, jlsTransferSyntaxUID };
5 changes: 5 additions & 0 deletions src/shared/mediaTypesUtils/mediaTypeJP2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const jp2MediaType = 'image/jp2';
const jp2TransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.90';
const jp2TransferSyntaxUID = '1.2.840.10008.1.2.4.91';

export { jp2MediaType, jp2TransferSyntaxUIDlossless, jp2TransferSyntaxUID };
11 changes: 11 additions & 0 deletions src/shared/mediaTypesUtils/mediaTypeJPEG.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const jpegMediaType = 'image/jpeg';
const jpegTransferSyntaxUIDlossy1 = '1.2.840.10008.1.2.4.50';
const jpegTransferSyntaxUIDlossy2 = '1.2.840.10008.1.2.4.51';
const jpegTransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.57';

export {
jpegMediaType,
jpegTransferSyntaxUIDlossy1,
jpegTransferSyntaxUIDlossy2,
jpegTransferSyntaxUIDlossless,
};
4 changes: 4 additions & 0 deletions src/shared/mediaTypesUtils/mediaTypeOctetStream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const octetStreamMediaType = 'application/octet-stream';
const octetStreamTransferSyntaxUID = '*';

export { octetStreamMediaType, octetStreamTransferSyntaxUID };
4 changes: 4 additions & 0 deletions src/shared/mediaTypesUtils/mediaTypeXDicomRLE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const xdicomrleMediaType = 'image/x-dicom-rle';
const xdicomrleTransferSyntaxUID = '1.2.840.10008.1.2.5';

export { xdicomrleMediaType, xdicomrleTransferSyntaxUID };
Loading

0 comments on commit 55628dd

Please sign in to comment.