From 31bedf8f8754def7b9ae401f55b043ed68b26bca Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Tue, 23 May 2023 16:43:32 -0400 Subject: [PATCH] Add a DICOMweb proxy capability to the webserver --- .../lib/mkdicomwebConfig.js | 10 ++++ .../lib/query/retrieveJson.js | 23 ++++++++ packages/static-wado-creator/lib/queryMain.js | 57 +++++++++++++++++++ .../lib/writer/HashDataWriter.js | 3 +- .../static-wado-deploy/lib/clientMain.mjs | 2 +- 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 packages/static-wado-creator/lib/query/retrieveJson.js create mode 100644 packages/static-wado-creator/lib/queryMain.js diff --git a/packages/static-wado-creator/lib/mkdicomwebConfig.js b/packages/static-wado-creator/lib/mkdicomwebConfig.js index 3f92dd1..862e342 100644 --- a/packages/static-wado-creator/lib/mkdicomwebConfig.js +++ b/packages/static-wado-creator/lib/mkdicomwebConfig.js @@ -3,6 +3,7 @@ const { staticWadoConfig } = require("@radicalimaging/static-wado-util"); const createMain = require("./createMain"); const deleteMain = require("./deleteMain"); const rejectMain = require("./rejectMain"); +const queryMain = require("./queryMain"); const instanceMain = require("./instanceMain"); const indexMain = require("./indexMain"); const groupMain = require("./groupMain"); @@ -58,6 +59,10 @@ const { mkdicomwebConfig } = ConfigPoint.register({ key: "-e, --no-encapsulated-image", description: "Avoid encapsulating the image frame. Writes with the extension and without multipart", }, + { + key: "--singlepart-bulkdata", + description: "Store bulkdata as single part data", + }, { key: "--single-part-image", description: "Writes single part image data", @@ -184,6 +189,11 @@ const { mkdicomwebConfig } = ConfigPoint.register({ main: rejectMain, helpDescription: "Reject the specified series", }, + { + command: "query ", + main: queryMain, + helpDescription: "Performs DICOMweb query operations to update local deduplicated/metadata", + }, ], }, }); diff --git a/packages/static-wado-creator/lib/query/retrieveJson.js b/packages/static-wado-creator/lib/query/retrieveJson.js new file mode 100644 index 0000000..f2157dd --- /dev/null +++ b/packages/static-wado-creator/lib/query/retrieveJson.js @@ -0,0 +1,23 @@ +const https = require("https"); + +/** Retrieve the given url fromm the back end, returning a JSON representation of the data */ +module.exports = function retrieve(url, options) { + console.log("Requesting to retrieve", url); + const parsedUrl = new URL(url); + const req = https.request(parsedUrl, (res) => { + console.log(`STATUS: ${res.statusCode}`); + console.log(`HEADERS: ${JSON.stringify(res.headers)}`); + res.setEncoding('utf8'); + res.on('data', (chunk) => { + console.log(`BODY: ${chunk}`); + }); + res.on('end', () => { + console.log('No more data in response.'); + }); + }); + req.on('error', (e) => { + console.error(`problem with request: ${e.message}`); + }); + + req.end(); +} \ No newline at end of file diff --git a/packages/static-wado-creator/lib/queryMain.js b/packages/static-wado-creator/lib/queryMain.js new file mode 100644 index 0000000..cf30759 --- /dev/null +++ b/packages/static-wado-creator/lib/queryMain.js @@ -0,0 +1,57 @@ +const StaticWado = require("./StaticWado"); +const adaptProgramOpts = require("./util/adaptProgramOpts"); +const retrieveJson = require("./query/retrieveJson"); + +/** + * Queries the given URL instance. + * Options: + * * metadata + * Appends /series to the URL and tries to retrieve + * Stores the data in the `series` key + * Iterates over all series in the list and checks if the series is up to date + * For each series not up to date: + * * `series//metadata` stored to a key of the same name + * * Iterates over all instances in above and adds to `StudyData` + * Writes updated study data files (recreated from new deduplciated data) + * * cache - will cache all queried files + * * stream - will print the response directly to the standard output. + * * deduplicated + * Reads the /deduplicated path first + * If no object, and the `metadata` option is set, then runs that path + * If the object is found, then writes the deduplicated data to the deduplicated path + * Runs the regular metadata create path + * + * Retrieving individual instances for proxying cached data. + * Data can be read from disk once this is complete. + * `mkdicomweb query 1.2.3/series --cache` + * `mkdicomweb query 1.2.3/series/2.3.4/metadata --cache` + * + * Retrieving a study query for direct proxy: + * Data can be streamed directly from the command line. + * `mkdicomweb query 1.2.3?ModalitiesInStudy=CR --stream` + * + * Creates or Updates Deduplicated Metadata: + * This command should be used before receiving data against remote proxies NOT supporting deduplicated + * `mkdicomweb query 1.2.3 --metadata` + * + * Creates or Updates Deduplicated Metadata from Deduplicated Remote + * This command should be used before receiving data against remote proxies supporting deduplicated + * `mkdicomweb query 1.2.3 --deduplicated` + * + * @param {*} url to fetch + * @param {*} options + * @param {*} program + */ +module.exports = function queryMain(url, options) { + const finalOptions = adaptProgramOpts(options, { + ...this, + isGroup: true, + isStudyData: true, + }); + const importer = new StaticWado(finalOptions); + const useUrl = url.indexOf('http')===-1 ? `${this.dicomwebUrl}/studies/${url}` : url; + console.log("Import from", useUrl); + retrieveJson(url, options).then(json => { + console.log("Retrieved:", JSON.stringify(json,null,2)); + }); +}; diff --git a/packages/static-wado-creator/lib/writer/HashDataWriter.js b/packages/static-wado-creator/lib/writer/HashDataWriter.js index 9178aa6..4d7364d 100644 --- a/packages/static-wado-creator/lib/writer/HashDataWriter.js +++ b/packages/static-wado-creator/lib/writer/HashDataWriter.js @@ -20,6 +20,7 @@ const HashDataWriter = (options) => async (id, key, data, additionalOptions = {}) => { const isRaw = ArrayBuffer.isView(data); + const { singlepartBulkdata } = options; const { mimeType } = additionalOptions; // If the file has an extension, it should be directly accessible as that file type. const gzip = !isRaw || (data.length > 1024 && !mimeType); @@ -30,7 +31,7 @@ const HashDataWriter = mkdir: true, gzip, }); - if (isRaw) { + if (isRaw && !singlepartBulkdata) { await WriteMultipart(writeStream, [new MultipartHeader("Content-Type", "application/octet-stream")], rawData); } else { await writeStream.write(rawData); diff --git a/packages/static-wado-deploy/lib/clientMain.mjs b/packages/static-wado-deploy/lib/clientMain.mjs index 1651fef..b9caa00 100644 --- a/packages/static-wado-deploy/lib/clientMain.mjs +++ b/packages/static-wado-deploy/lib/clientMain.mjs @@ -3,5 +3,5 @@ import uploadDeploy from "./uploadDeploy.mjs"; export default async function (options) { if (options.retrieve) throw new Error("Retrieve unsupported for client files"); - await commonMain(this, "client", options, uploadDeploy.bind(null, undefined)); + await commonMain(this, "client", options, uploadDeploy.bind(null, "")); }