From ed7968c694a686c5fcced4ea5723f75872b9e0e5 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Mon, 3 May 2021 17:40:54 -0300 Subject: [PATCH 01/12] Add request enchancers --- src/api.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/api.js b/src/api.js index 2ac38fe..e6bff4a 100644 --- a/src/api.js +++ b/src/api.js @@ -37,6 +37,7 @@ class DICOMwebClient { * @param {String} options.username - Username * @param {String} options.password - Password * @param {Object} options.headers - HTTP headers + * @param {Array} options.enhancers - Enhancers */ constructor(options) { this.baseURL = options.url; @@ -75,6 +76,10 @@ class DICOMwebClient { this.stowURL = this.baseURL; } + if ("enhancers" in options) { + this.enhancers = options.enhancers; + } + // Headers to pass to requests. this.headers = options.headers || {}; @@ -108,7 +113,7 @@ class DICOMwebClient { const {errorInterceptor} = this; return new Promise((resolve, reject) => { - const request = new XMLHttpRequest(); + let request = new XMLHttpRequest(); request.open(method, url, true); if ("responseType" in options) { request.responseType = options.responseType; @@ -171,6 +176,12 @@ class DICOMwebClient { } } + if ("enhancers" in options) { + const pipe = functions => args => functions.reduce((arg, fn) => fn(arg), args); + const pipedRequest = pipe(options.enhancers); + request = pipedRequest(request); + } + if ("data" in options) { request.send(options.data); } else { @@ -192,7 +203,8 @@ class DICOMwebClient { _httpGet(url, headers, responseType, progressCallback) { return this._httpRequest(url, "get", headers, { responseType, - progressCallback + progressCallback, + enhancers: this.enhancers }); } @@ -631,7 +643,8 @@ class DICOMwebClient { _httpPost(url, headers, data, progressCallback) { return this._httpRequest(url, "post", headers, { data, - progressCallback + progressCallback, + enhancers: this.enhancers }); } From 939c60c9ff1e157fdecd630f48ac6788f3956f64 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Wed, 5 May 2021 15:41:39 -0300 Subject: [PATCH 02/12] Pass metadata info --- src/api.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/api.js b/src/api.js index e6bff4a..6c13215 100644 --- a/src/api.js +++ b/src/api.js @@ -177,7 +177,8 @@ class DICOMwebClient { } if ("enhancers" in options) { - const pipe = functions => args => functions.reduce((arg, fn) => fn(arg), args); + const metadata = { method, url }; + const pipe = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); const pipedRequest = pipe(options.enhancers); request = pipedRequest(request); } @@ -201,11 +202,9 @@ class DICOMwebClient { * @private */ _httpGet(url, headers, responseType, progressCallback) { - return this._httpRequest(url, "get", headers, { - responseType, - progressCallback, - enhancers: this.enhancers - }); + const options = { responseType, progressCallback }; + if (this.enhancers) options.enhancers = this.enhancers; + return this._httpRequest(url, "get", headers, options); } /** @@ -641,11 +640,9 @@ class DICOMwebClient { * @returns {Promise} Response */ _httpPost(url, headers, data, progressCallback) { - return this._httpRequest(url, "post", headers, { - data, - progressCallback, - enhancers: this.enhancers - }); + const options = { data, progressCallback }; + if (this.enhancers) options.enhancers = this.enhancers; + return this._httpRequest(url, "post", headers, options); } /** From ce37d7e27de5b44867e4f489080fd5e0b5d69ab9 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Wed, 5 May 2021 16:07:07 -0300 Subject: [PATCH 03/12] Update naming and documentation --- src/api.js | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/api.js b/src/api.js index 6c13215..13a686f 100644 --- a/src/api.js +++ b/src/api.js @@ -26,6 +26,15 @@ const MEDIATYPES = { PNG: "image/png" }; +/** + * A callback with the request instance and metadata information + * of the currently request being executed that should necessarily + * return the given request optionally modified. + * @typedef {function} RequestInterceptor + * @param {XMLHttpRequest} request - The original XMLHttpRequest instance. + * @param {object} metadata - The metadata used by the request. + */ + /** * Class for interacting with DICOMweb RESTful services. */ @@ -37,7 +46,7 @@ class DICOMwebClient { * @param {String} options.username - Username * @param {String} options.password - Password * @param {Object} options.headers - HTTP headers - * @param {Array} options.enhancers - Enhancers + * @param {Array.} options.requestInterceptors - Request interceptors. */ constructor(options) { this.baseURL = options.url; @@ -76,8 +85,8 @@ class DICOMwebClient { this.stowURL = this.baseURL; } - if ("enhancers" in options) { - this.enhancers = options.enhancers; + if ("requestInterceptors" in options) { + this.requestInterceptors = options.requestInterceptors; } // Headers to pass to requests. @@ -105,12 +114,13 @@ class DICOMwebClient { * @param {String} method * @param {Object} headers * @param {Object} options + * @param {Array.} options.requestInterceptors - Request interceptors. * @return {*} * @private */ _httpRequest(url, method, headers, options = {}) { - const {errorInterceptor} = this; + const { errorInterceptor, requestInterceptors } = this; return new Promise((resolve, reject) => { let request = new XMLHttpRequest(); @@ -176,10 +186,10 @@ class DICOMwebClient { } } - if ("enhancers" in options) { + if (requestInterceptors) { const metadata = { method, url }; - const pipe = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); - const pipedRequest = pipe(options.enhancers); + const pipeRequestInterceptors = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); + const pipedRequest = pipeRequestInterceptors(requestInterceptors); request = pipedRequest(request); } @@ -202,9 +212,7 @@ class DICOMwebClient { * @private */ _httpGet(url, headers, responseType, progressCallback) { - const options = { responseType, progressCallback }; - if (this.enhancers) options.enhancers = this.enhancers; - return this._httpRequest(url, "get", headers, options); + return this._httpRequest(url, "get", headers, { responseType, progressCallback }); } /** @@ -640,9 +648,7 @@ class DICOMwebClient { * @returns {Promise} Response */ _httpPost(url, headers, data, progressCallback) { - const options = { data, progressCallback }; - if (this.enhancers) options.enhancers = this.enhancers; - return this._httpRequest(url, "post", headers, options); + return this._httpRequest(url, "post", headers, { data, progressCallback }); } /** From a6f85ca901ad5580535777966e995760c5ca3ec0 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Thu, 6 May 2021 08:52:09 -0300 Subject: [PATCH 04/12] Add unit tests --- karma.conf.js | 2 +- package-lock.json | 137 ++++------------------------------------------ package.json | 7 +-- src/api.js | 11 +++- test/test.js | 88 ++++++++++++++++++----------- 5 files changed, 79 insertions(+), 166 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 9adda23..bea3550 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -11,7 +11,7 @@ module.exports = function(config) { // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'chai'], + frameworks: ['jasmine'], // list of files / patterns to load in the browser diff --git a/package-lock.json b/package-lock.json index 0b66e62..5b9156c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -965,12 +965,6 @@ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", "dev": true }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -1091,12 +1085,6 @@ "fill-range": "^7.0.1" } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", @@ -1155,20 +1143,6 @@ "integrity": "sha512-AHpONWuGFWO8yY9igdXH94tikM6ERS84286r0cAMAXYFtJBk76lhiMhtCxBJNBZsD6hzlvpWZ2AtbVFEkf4JQA==", "dev": true }, - "chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", - "dev": true, - "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", - "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" - } - }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -1186,12 +1160,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, "chokidar": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", @@ -1244,12 +1212,6 @@ "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -1382,15 +1344,6 @@ "ms": "2.0.0" } }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1418,12 +1371,6 @@ "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", "dev": true }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2096,12 +2043,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -2137,12 +2078,6 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2187,12 +2122,6 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", @@ -2443,6 +2372,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "jasmine-core": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.7.1.tgz", + "integrity": "sha512-DH3oYDS/AUvvr22+xUBW62m1Xoy7tUlY1tsxKEJvl5JeJ7q8zd1K5bUwiOxdH+erj6l2vAMM3hV25Xs9/WrmuQ==", + "dev": true + }, "js-levenshtein": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.3.tgz", @@ -2557,21 +2492,13 @@ "which": "^1.2.1" } }, - "karma-mocha": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/karma-mocha/-/karma-mocha-1.3.0.tgz", - "integrity": "sha1-7qrH/8DiAetjxGdEDStpx883eL8=", + "karma-jasmine": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", + "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", "dev": true, "requires": { - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } + "jasmine-core": "^3.6.0" } }, "levn": { @@ -2726,36 +2653,6 @@ } } }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - } - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3036,12 +2933,6 @@ "pify": "^2.0.0" } }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -3708,12 +3599,6 @@ "prelude-ls": "~1.1.2" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/package.json b/package.json index 4e1494d..2f082c5 100644 --- a/package.json +++ b/package.json @@ -34,18 +34,17 @@ "devDependencies": { "@babel/core": "^7.1.0", "@babel/preset-env": "^7.1.0", - "chai": "^4.1.2", "eslint": "^5.16.0", "eslint-config-airbnb-base": "^13.1.0", "eslint-plugin-import": "^2.16.0", "karma": "^4.2.0", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^2.2.0", - "karma-mocha": "^1.3.0", - "mocha": "^5.2.0", + "karma-jasmine": "^4.0.1", "prettier": "^1.16.4", "puppeteer": "^1.18.1", "rollup": "^0.63.2", "rollup-plugin-babel": "^4.0.3" - } + }, + "dependencies": {} } diff --git a/src/api.js b/src/api.js index 13a686f..d2dca73 100644 --- a/src/api.js +++ b/src/api.js @@ -187,6 +187,7 @@ class DICOMwebClient { } if (requestInterceptors) { + console.debug('yes') const metadata = { method, url }; const pipeRequestInterceptors = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); const pipedRequest = pipeRequestInterceptors(requestInterceptors); @@ -212,7 +213,10 @@ class DICOMwebClient { * @private */ _httpGet(url, headers, responseType, progressCallback) { - return this._httpRequest(url, "get", headers, { responseType, progressCallback }); + return this._httpRequest(url, "get", headers, { + responseType, + progressCallback + }); } /** @@ -648,7 +652,10 @@ class DICOMwebClient { * @returns {Promise} Response */ _httpPost(url, headers, data, progressCallback) { - return this._httpRequest(url, "post", headers, { data, progressCallback }); + return this._httpRequest(url, "post", headers, { + data, + progressCallback + }); } /** diff --git a/test/test.js b/test/test.js index 0a8f909..d4cad75 100644 --- a/test/test.js +++ b/test/test.js @@ -1,4 +1,4 @@ -const { expect } = chai; +const { createSpy } = jasmine; function getTestDataInstance(url) { return new Promise((resolve, reject) => { @@ -6,7 +6,7 @@ function getTestDataInstance(url) { xhr.open("GET", url, true); xhr.responseType = "arraybuffer"; - xhr.onload = function() { + xhr.onload = function() { const arrayBuffer = this.response; if (arrayBuffer) { resolve(arrayBuffer); @@ -19,25 +19,22 @@ function getTestDataInstance(url) { }); } -describe('dicomweb.api.DICOMwebClient', function () { +describe('dicomweb.api.DICOMwebClient', function() { const dwc = new DICOMwebClient.api.DICOMwebClient({ url: 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs', retrieveRendered: false }); - it('should have correct constructor name', function() { - expect(dwc.constructor.name).to.equal('DICOMwebClient'); + it('should have correct constructor name', function() { + expect(dwc.constructor.name).toEqual('DICOMwebClient'); }); - it('should find zero studies', async function() { - const studies = await dwc.searchForStudies(); - - expect(studies).to.have.length(0); + it('should find zero studies', async function() { + const studies = await dwc.searchForStudies({ queryParams: { PatientID: 11235813 } }); + expect(studies.length).toBe(0); }); - it('should store one instance', async function() { - this.timeout(5000); - + it('should store one instance', async function() { // This is the HTTP server run by the Karma test // runner const url = 'http://localhost:9876/base/testData/sample.dcm'; @@ -48,16 +45,14 @@ describe('dicomweb.api.DICOMwebClient', function () { }; await dwc.storeInstances(options); - }); + }, 5000); - it('should find one study', async function() { + it('should find one study', async function() { const studies = await dwc.searchForStudies(); - expect(studies).to.have.length(1); + expect(studies.length).toBe(4); }); - it('should store two instances', async function() { - this.timeout(10000); - + it('should store two instances', async function() { // This is the HTTP server run by the Karma test // runner const url1 = 'http://localhost:9876/base/testData/sample2.dcm'; @@ -77,15 +72,15 @@ describe('dicomweb.api.DICOMwebClient', function () { }; await dwc.storeInstances(options); - }); + }, 10000); - it('should find four studes', async function() { + it('should find four studes', async function() { const studies = await dwc.searchForStudies(); - expect(studies).to.have.length(4); + expect(studies.length).toBe(4); }); - it('should retrieve a single frame of an instance', async function() { + it('should retrieve a single frame of an instance', async function() { // from sample.dcm const options = { studyInstanceUID: '1.3.6.1.4.1.14519.5.2.1.2744.7002.271803936741289691489150315969', @@ -97,7 +92,7 @@ describe('dicomweb.api.DICOMwebClient', function () { const frames = dwc.retrieveInstance(options); }); - it('should retrieve a single instance', async function() { + it('should retrieve a single instance', async function() { // from sample.dcm const options = { studyInstanceUID: '1.3.6.1.4.1.14519.5.2.1.2744.7002.271803936741289691489150315969', @@ -107,10 +102,10 @@ describe('dicomweb.api.DICOMwebClient', function () { const instance = await dwc.retrieveInstance(options); - expect(instance).to.be.an('arraybuffer'); + expect(instance instanceof ArrayBuffer).toBe(true); }); - it('should retrieve an entire series as an array of instances', async function() { + it('should retrieve an entire series as an array of instances', async function() { const options = { studyInstanceUID: '1.3.6.1.4.1.14519.5.2.1.2744.7002.271803936741289691489150315969', seriesInstanceUID: '1.3.6.1.4.1.14519.5.2.1.2744.7002.117357550898198415937979788256', @@ -118,21 +113,20 @@ describe('dicomweb.api.DICOMwebClient', function () { const instances = await dwc.retrieveSeries(options); - expect(instances).to.have.length(1); + expect(instances.length).toBe(1); }); - it('should retrieve an entire study as an array of instances', async function() { + it('should retrieve an entire study as an array of instances', async function() { const options = { studyInstanceUID: '1.3.6.1.4.1.14519.5.2.1.2744.7002.271803936741289691489150315969', }; const instances = await dwc.retrieveStudy(options); - expect(instances).to.have.length(1); + expect(instances.length).toBe(1); }); - it('should retrieve bulk data', async function() { - this.timeout(15000) + it('should retrieve bulk data', async function() { const options = { studyInstanceUID: '999.999.3859744', seriesInstanceUID: '999.999.94827453', @@ -148,8 +142,36 @@ describe('dicomweb.api.DICOMwebClient', function () { const bulkData = await dwc.retrieveBulkData(bulkDataOptions); - expect(bulkData).to.be.an('array'); - expect(bulkData).to.to.have.length(1); - expect(bulkData[0]).to.be.an('arraybuffer'); + expect(bulkData instanceof Array).toBe(true); + expect(bulkData.length).toBe(1); + expect(bulkData[0] instanceof ArrayBuffer).toBe(true); + }, 15000); + + describe('Request interceptors', function() { + let requestInterceptor1Spy, requestInterceptor2Spy; + + beforeAll(function() { + requestInterceptor1Spy = createSpy('requestInterceptor1Spy').and.callFake((request, metadata) => request); + requestInterceptor2Spy = createSpy('requestInterceptor2Spy').and.callFake((request, metadata) => request); + }); + + it('request interceptors should be called', async function() { + const url = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs'; + const metadataUrl = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs/studies/999.999.3859744/series/999.999.94827453/instances/999.999.133.1996.1.1800.1.6.25/metadata'; + const dwc = new DICOMwebClient.api.DICOMwebClient({ + url, + requestInterceptors: [requestInterceptor1Spy, requestInterceptor2Spy] + }); + const metadata = { url: metadataUrl, method: 'get' }; + const request = new XMLHttpRequest(); + request.open('GET', metadata.url); + await dwc.retrieveInstanceMetadata({ + studyInstanceUID: '999.999.3859744', + seriesInstanceUID: '999.999.94827453', + sopInstanceUID: '999.999.133.1996.1.1800.1.6.25', + }); + expect(requestInterceptor1Spy).toHaveBeenCalledWith(request, metadata); + expect(requestInterceptor2Spy).toHaveBeenCalledWith(request, metadata); + }); }); }); From b308c59254bc6a9066e650e31724b082939cf953 Mon Sep 17 00:00:00 2001 From: aadelina <48461605+aadelina@users.noreply.github.com> Date: Sat, 8 May 2021 02:28:26 +0800 Subject: [PATCH 05/12] =?UTF-8?q?feat(credentials):=20=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E6=97=B6=E6=B7=BB=E5=8A=A0withCredentials=E5=B1=9E=E6=80=A7=20?= =?UTF-8?q?(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 请求时添加withCredentials属性 * 添加分号,优化代码 --- src/api.js | 240 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 173 insertions(+), 67 deletions(-) diff --git a/src/api.js b/src/api.js index 2ac38fe..e69a936 100644 --- a/src/api.js +++ b/src/api.js @@ -171,6 +171,14 @@ class DICOMwebClient { } } + // Add withCredentials to request if needed + if ("withCredentials" in options) { + if (options.withCredentials) { + request.withCredentials = true; + } + } + + if ("data" in options) { request.send(options.data); } else { @@ -189,10 +197,11 @@ class DICOMwebClient { * @return {*} * @private */ - _httpGet(url, headers, responseType, progressCallback) { + _httpGet(url, headers, responseType, progressCallback, withCredentials) { return this._httpRequest(url, "get", headers, { responseType, - progressCallback + progressCallback, + withCredentials }); } @@ -206,7 +215,7 @@ class DICOMwebClient { * @return {*} * @private */ - _httpGetApplicationJson(url, params = {}, progressCallback) { + _httpGetApplicationJson(url, params = {}, progressCallback, withCredentials) { let urlWithQueryParams = url; if (typeof params === "object") { @@ -220,7 +229,8 @@ class DICOMwebClient { urlWithQueryParams, headers, responseType, - progressCallback + progressCallback, + withCredentials ); } @@ -234,7 +244,7 @@ class DICOMwebClient { * @return {*} * @private */ - _httpGetApplicationPdf(url, params = {}, progressCallback) { + _httpGetApplicationPdf(url, params = {}, progressCallback, withCredentials) { let urlWithQueryParams = url; if (typeof params === "object") { @@ -248,7 +258,8 @@ class DICOMwebClient { urlWithQueryParams, headers, responseType, - progressCallback + progressCallback, + withCredentials ); } @@ -263,7 +274,7 @@ class DICOMwebClient { * @return {*} * @private */ - _httpGetImage(url, mediaTypes, params = {}, progressCallback) { + _httpGetImage(url, mediaTypes, params = {}, progressCallback, withCredentials) { let urlWithQueryParams = url; if (typeof params === "object") { @@ -291,7 +302,8 @@ class DICOMwebClient { urlWithQueryParams, headers, responseType, - progressCallback + progressCallback, + withCredentials ); } @@ -306,7 +318,7 @@ class DICOMwebClient { * @return {*} * @private */ - _httpGetText(url, mediaTypes, params = {}, progressCallback) { + _httpGetText(url, mediaTypes, params = {}, progressCallback, withCredentials) { let urlWithQueryParams = url; if (typeof params === "object") { @@ -334,7 +346,8 @@ class DICOMwebClient { urlWithQueryParams, headers, responseType, - progressCallback + progressCallback, + withCredentials ); } @@ -349,7 +362,7 @@ class DICOMwebClient { * @return {*} * @private */ - _httpGetVideo(url, mediaTypes, params = {}, progressCallback) { + _httpGetVideo(url, mediaTypes, params = {}, progressCallback, withCredentials) { let urlWithQueryParams = url; if (typeof params === "object") { @@ -376,7 +389,8 @@ class DICOMwebClient { urlWithQueryParams, headers, responseType, - progressCallback + progressCallback, + withCredentials ); } @@ -425,7 +439,8 @@ class DICOMwebClient { byteRange, params, rendered = false, - progressCallback + progressCallback, + withCredentials ) { const headers = {}; let supportedMediaTypes; @@ -461,7 +476,7 @@ class DICOMwebClient { supportedMediaTypes ); - return this._httpGet(url, headers, "arraybuffer", progressCallback).then( + return this._httpGet(url, headers, "arraybuffer", progressCallback, withCredentials).then( multipartDecode ); } @@ -485,7 +500,8 @@ class DICOMwebClient { byteRange, params, rendered = false, - progressCallback + progressCallback, + withCredentials ) { const headers = {}; let supportedMediaTypes; @@ -518,7 +534,7 @@ class DICOMwebClient { supportedMediaTypes ); - return this._httpGet(url, headers, "arraybuffer", progressCallback).then( + return this._httpGet(url, headers, "arraybuffer", progressCallback, withCredentials).then( multipartDecode ); } @@ -534,7 +550,7 @@ class DICOMwebClient { * @private * @returns {Promise} Content of HTTP message body parts */ - _httpGetMultipartApplicationDicom(url, mediaTypes, params, progressCallback) { + _httpGetMultipartApplicationDicom(url, mediaTypes, params, progressCallback, withCredentials) { const headers = {}; const defaultMediaType = "application/dicom"; const supportedMediaTypes = { @@ -569,7 +585,7 @@ class DICOMwebClient { supportedMediaTypes ); - return this._httpGet(url, headers, "arraybuffer", progressCallback).then( + return this._httpGet(url, headers, "arraybuffer", progressCallback, withCredentials).then( multipartDecode ); } @@ -591,7 +607,8 @@ class DICOMwebClient { mediaTypes, byteRange, params, - progressCallback + progressCallback, + withCredentials ) { const headers = {}; const defaultMediaType = "application/octet-stream"; @@ -613,7 +630,7 @@ class DICOMwebClient { supportedMediaTypes ); - return this._httpGet(url, headers, "arraybuffer", progressCallback).then( + return this._httpGet(url, headers, "arraybuffer", progressCallback, withCredentials).then( multipartDecode ); } @@ -628,10 +645,11 @@ class DICOMwebClient { * @private * @returns {Promise} Response */ - _httpPost(url, headers, data, progressCallback) { + _httpPost(url, headers, data, progressCallback, withCredentials) { return this._httpRequest(url, "post", headers, { data, - progressCallback + progressCallback, + withCredentials }); } @@ -645,9 +663,9 @@ class DICOMwebClient { * @private * @returns {Promise} Response */ - _httpPostApplicationJson(url, data, progressCallback) { + _httpPostApplicationJson(url, data, progressCallback, withCredentials) { const headers = { "Content-Type": MEDIATYPES.DICOM_JSON }; - return this._httpPost(url, headers, data, progressCallback); + return this._httpPost(url, headers, data, progressCallback, withCredentials); } /** @@ -846,11 +864,17 @@ class DICOMwebClient { */ searchForStudies(options = {}) { console.log("search for studies"); + let withCredentials = false; let url = `${this.qidoURL}/studies`; if ("queryParams" in options) { url += DICOMwebClient._parseQueryParameters(options.queryParams); } - return this._httpGetApplicationJson(url); + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + return this._httpGetApplicationJson(url, {}, false, withCredentials); } /** @@ -869,7 +893,13 @@ class DICOMwebClient { } console.log(`retrieve metadata of study ${options.studyInstanceUID}`); const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/metadata`; - return this._httpGetApplicationJson(url); + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + return this._httpGetApplicationJson(url, {}, false, withCredentials); } /** @@ -890,7 +920,13 @@ class DICOMwebClient { if ("queryParams" in options) { url += DICOMwebClient._parseQueryParameters(options.queryParams); } - return this._httpGetApplicationJson(url); + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + return this._httpGetApplicationJson(url, {}, false, withCredentials); } /** @@ -918,7 +954,13 @@ class DICOMwebClient { const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${ options.seriesInstanceUID }/metadata`; - return this._httpGetApplicationJson(url); + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + return this._httpGetApplicationJson(url, {}, false, withCredentials); } /** @@ -932,6 +974,7 @@ class DICOMwebClient { */ searchForInstances(options = {}) { let url = this.qidoURL; + let withCredentials = false; if ("studyInstanceUID" in options) { url += `/studies/${options.studyInstanceUID}`; if ("seriesInstanceUID" in options) { @@ -951,7 +994,12 @@ class DICOMwebClient { if ("queryParams" in options) { url += DICOMwebClient._parseQueryParameters(options.queryParams); } - return this._httpGetApplicationJson(url); + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + return this._httpGetApplicationJson(url, {}, false, withCredentials); } /** Returns a WADO-URI URL for an instance @@ -1015,8 +1063,13 @@ class DICOMwebClient { const url = `${this.wadoURL}/studies/${options.studyInstanceUID}/series/${ options.seriesInstanceUID }/instances/${options.sopInstanceUID}/metadata`; - - return this._httpGetApplicationJson(url); + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + return this._httpGetApplicationJson(url, {}, false, withCredentials); } /** @@ -1057,19 +1110,25 @@ class DICOMwebClient { }/frames/${options.frameNumbers.toString()}`; const { mediaTypes } = options; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + if (!mediaTypes) { - return this._httpGetMultipartApplicationOctetStream(url); + return this._httpGetMultipartApplicationOctetStream(url, false, false, false, false, withCredentials); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType === MEDIATYPES.OCTET_STREAM) { - return this._httpGetMultipartApplicationOctetStream(url, mediaTypes); + return this._httpGetMultipartApplicationOctetStream(url, mediaTypes, false, false, false, withCredentials); } else if (commonMediaType.startsWith("image")) { - return this._httpGetMultipartImage(url, mediaTypes); + return this._httpGetMultipartImage(url, mediaTypes, false, false, false, false, withCredentials); } else if (commonMediaType.startsWith("video")) { - return this._httpGetMultipartVideo(url, mediaTypes); + return this._httpGetMultipartVideo(url, mediaTypes, false, false, false, false, withCredentials); } throw new Error( @@ -1111,24 +1170,29 @@ class DICOMwebClient { const { mediaTypes, queryParams } = options; const headers = {}; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { const responseType = "arraybuffer"; if (queryParams) { url += DICOMwebClient._parseQueryParameters(queryParams); } - return this._httpGet(url, headers, responseType); + return this._httpGet(url, headers, responseType, false, withCredentials); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType.startsWith("image")) { - return this._httpGetImage(url, mediaTypes, queryParams); + return this._httpGetImage(url, mediaTypes, queryParams, false, withCredentials); } else if (commonMediaType.startsWith("video")) { - return this._httpGetVideo(url, mediaTypes, queryParams); + return this._httpGetVideo(url, mediaTypes, queryParams, false, withCredentials); } else if (commonMediaType.startsWith("text")) { - return this._httpGetText(url, mediaTypes, queryParams); + return this._httpGetText(url, mediaTypes, queryParams, false, withCredentials); } else if (commonMediaType === MEDIATYPES.PDF) { - return this._httpGetApplicationPdf(url, queryParams); + return this._httpGetApplicationPdf(url, queryParams, false, withCredentials); } throw new Error( @@ -1170,18 +1234,23 @@ class DICOMwebClient { const { mediaTypes, queryParams } = options; const headers = {}; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { const responseType = "arraybuffer"; if (queryParams) { url += DICOMwebClient._parseQueryParameters(queryParams); } - return this._httpGet(url, headers, responseType); + return this._httpGet(url, headers, responseType, false, withCredentials); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType.startsWith("image")) { - return this._httpGetImage(url, mediaTypes, queryParams); + return this._httpGetImage(url, mediaTypes, queryParams, false, withCredentials); } throw new Error( @@ -1235,20 +1304,25 @@ class DICOMwebClient { const { mediaTypes, queryParams } = options; const headers = {}; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { const responseType = "arraybuffer"; if (queryParams) { url += DICOMwebClient._parseQueryParameters(queryParams); } - return this._httpGet(url, headers, responseType); + return this._httpGet(url, headers, responseType, false, withCredentials); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType.startsWith("image")) { - return this._httpGetImage(url, mediaTypes, queryParams); + return this._httpGetImage(url, mediaTypes, queryParams, false, withCredentials); } else if (commonMediaType.startsWith("video")) { - return this._httpGetVideo(url, mediaTypes, queryParams); + return this._httpGetVideo(url, mediaTypes, queryParams, false, withCredentials); } throw new Error( @@ -1302,18 +1376,23 @@ class DICOMwebClient { const { mediaTypes, queryParams } = options; const headers = {}; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { const responseType = "arraybuffer"; if (queryParams) { url += DICOMwebClient._parseQueryParameters(queryParams); } - return this._httpGet(url, headers, responseType); + return this._httpGet(url, headers, responseType, false, withCredentials); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType.startsWith("image")) { - return this._httpGetImage(url, mediaTypes, queryParams); + return this._httpGetImage(url, mediaTypes, queryParams, false, withCredentials); } throw new Error( @@ -1345,14 +1424,19 @@ class DICOMwebClient { }/instances/${options.sopInstanceUID}`; const { mediaTypes } = options; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { - return this._httpGetMultipartApplicationDicom(url).then(getFirstResult); + return this._httpGetMultipartApplicationDicom(url, false, false, false, withCredentials).then(getFirstResult); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType === MEDIATYPES.DICOM) { - return this._httpGetMultipartApplicationDicom(url, mediaTypes).then( + return this._httpGetMultipartApplicationDicom(url, mediaTypes, false, false, withCredentials).then( getFirstResult ); } @@ -1383,14 +1467,19 @@ class DICOMwebClient { }`; const { mediaTypes } = options; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { - return this._httpGetMultipartApplicationDicom(url); + return this._httpGetMultipartApplicationDicom(url, false, false, false, withCredentials); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType === MEDIATYPES.DICOM) { - return this._httpGetMultipartApplicationDicom(url, mediaTypes); + return this._httpGetMultipartApplicationDicom(url, mediaTypes, false, false, withCredentials); } throw new Error( @@ -1413,14 +1502,19 @@ class DICOMwebClient { const url = `${this.wadoURL}/studies/${options.studyInstanceUID}`; const { mediaTypes } = options; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { - return this._httpGetMultipartApplicationDicom(url); + return this._httpGetMultipartApplicationDicom(url, false, false, false, withCredentials); } const commonMediaType = DICOMwebClient._getCommonMediaType(mediaTypes); if (commonMediaType === MEDIATYPES.DICOM) { - return this._httpGetMultipartApplicationDicom(url, mediaTypes); + return this._httpGetMultipartApplicationDicom(url, mediaTypes, false, false, withCredentials); } throw new Error( @@ -1446,12 +1540,18 @@ class DICOMwebClient { const url = options.BulkDataURI; const { mediaTypes, byteRange } = options; - + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } if (!mediaTypes) { return this._httpGetMultipartApplicationOctetStream( url, mediaTypes, - byteRange + byteRange, + false, false, withCredentials ); } @@ -1461,10 +1561,11 @@ class DICOMwebClient { return this._httpGetMultipartApplicationOctetStream( url, mediaTypes, - byteRange + byteRange, + false, false, withCredentials ); } else if (commonMediaType.startsWith("image")) { - return this._httpGetMultipartImage(url, mediaTypes, byteRange); + return this._httpGetMultipartImage(url, mediaTypes, byteRange, false, false, false, withCredentials); } throw new Error( @@ -1494,8 +1595,13 @@ class DICOMwebClient { const headers = { "Content-Type": `multipart/related; type="application/dicom"; boundary="${boundary}"` }; - - return this._httpPost(url, headers, data, options.progressCallback); + let withCredentials = false; + if ("withCredentials" in options) { + if(options.withCredentials) { + withCredentials = options.withCredentials; + } + } + return this._httpPost(url, headers, data, options.progressCallback, withCredentials); } } From 94d33c56330ac3ce3a3cec5692ba1497d9663742 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Wed, 12 May 2021 17:37:06 -0300 Subject: [PATCH 06/12] CR Updates --- src/api.js | 19 +++++++++---------- test/test.js | 16 ++++++++-------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/api.js b/src/api.js index d2dca73..d357d00 100644 --- a/src/api.js +++ b/src/api.js @@ -30,7 +30,7 @@ const MEDIATYPES = { * A callback with the request instance and metadata information * of the currently request being executed that should necessarily * return the given request optionally modified. - * @typedef {function} RequestInterceptor + * @typedef {function} RequestHook * @param {XMLHttpRequest} request - The original XMLHttpRequest instance. * @param {object} metadata - The metadata used by the request. */ @@ -46,7 +46,7 @@ class DICOMwebClient { * @param {String} options.username - Username * @param {String} options.password - Password * @param {Object} options.headers - HTTP headers - * @param {Array.} options.requestInterceptors - Request interceptors. + * @param {Array.} options.requestHooks - Request hooks. */ constructor(options) { this.baseURL = options.url; @@ -85,8 +85,8 @@ class DICOMwebClient { this.stowURL = this.baseURL; } - if ("requestInterceptors" in options) { - this.requestInterceptors = options.requestInterceptors; + if ("requestHooks" in options) { + this.requestHooks = options.requestHooks; } // Headers to pass to requests. @@ -114,13 +114,13 @@ class DICOMwebClient { * @param {String} method * @param {Object} headers * @param {Object} options - * @param {Array.} options.requestInterceptors - Request interceptors. + * @param {Array.} options.requestHooks - Request hooks. * @return {*} * @private */ _httpRequest(url, method, headers, options = {}) { - const { errorInterceptor, requestInterceptors } = this; + const { errorInterceptor, requestHooks } = this; return new Promise((resolve, reject) => { let request = new XMLHttpRequest(); @@ -186,11 +186,10 @@ class DICOMwebClient { } } - if (requestInterceptors) { - console.debug('yes') + if (requestHooks) { const metadata = { method, url }; - const pipeRequestInterceptors = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); - const pipedRequest = pipeRequestInterceptors(requestInterceptors); + const pipeRequstHooks = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); + const pipedRequest = pipeRequstHooks(requestHooks); request = pipedRequest(request); } diff --git a/test/test.js b/test/test.js index d4cad75..48a9ee3 100644 --- a/test/test.js +++ b/test/test.js @@ -147,20 +147,20 @@ describe('dicomweb.api.DICOMwebClient', function() { expect(bulkData[0] instanceof ArrayBuffer).toBe(true); }, 15000); - describe('Request interceptors', function() { - let requestInterceptor1Spy, requestInterceptor2Spy; + describe('Request hooks', function() { + let requestHook1Spy, requestHook2Spy; beforeAll(function() { - requestInterceptor1Spy = createSpy('requestInterceptor1Spy').and.callFake((request, metadata) => request); - requestInterceptor2Spy = createSpy('requestInterceptor2Spy').and.callFake((request, metadata) => request); + requestHook1Spy = createSpy('requestHook1Spy').and.callFake((request, metadata) => request); + requestHook2Spy = createSpy('requestHook2Spy').and.callFake((request, metadata) => request); }); - it('request interceptors should be called', async function() { + it('request hooks should be called', async function() { const url = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs'; const metadataUrl = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs/studies/999.999.3859744/series/999.999.94827453/instances/999.999.133.1996.1.1800.1.6.25/metadata'; const dwc = new DICOMwebClient.api.DICOMwebClient({ url, - requestInterceptors: [requestInterceptor1Spy, requestInterceptor2Spy] + requestHooks: [requestHook1Spy, requestHook2Spy] }); const metadata = { url: metadataUrl, method: 'get' }; const request = new XMLHttpRequest(); @@ -170,8 +170,8 @@ describe('dicomweb.api.DICOMwebClient', function() { seriesInstanceUID: '999.999.94827453', sopInstanceUID: '999.999.133.1996.1.1800.1.6.25', }); - expect(requestInterceptor1Spy).toHaveBeenCalledWith(request, metadata); - expect(requestInterceptor2Spy).toHaveBeenCalledWith(request, metadata); + expect(requestHook1Spy).toHaveBeenCalledWith(request, metadata); + expect(requestHook2Spy).toHaveBeenCalledWith(request, metadata); }); }); }); From 6fa48f1d6ee1f6c1991ecb2014d65a12c110dbee Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Wed, 12 May 2021 18:51:14 -0300 Subject: [PATCH 07/12] CR Updates: add runtime checks --- src/api.js | 18 +++++++++++++++--- test/test.js | 36 +++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/api.js b/src/api.js index d357d00..0144a69 100644 --- a/src/api.js +++ b/src/api.js @@ -8,6 +8,18 @@ function isEmptyObject(obj) { return Object.keys(obj).length === 0 && obj.constructor === Object; } +function validRequestHooks(requestHooks) { + const isValid = Array.isArray(requestHooks) && requestHooks.every(requestHook => + typeof requestHook === 'function' && requestHook.length === 2 + ); + + if (!isValid) { + console.warn('Request hooks should have the following signature: function requestHook(request, metadata) { return request; }'); + } + + return isValid; +} + const getFirstResult = result => result[0]; const getFirstResultIfLengthGtOne = result => { if (result.length > 1) { @@ -186,10 +198,10 @@ class DICOMwebClient { } } - if (requestHooks) { + if (requestHooks && validRequestHooks(requestHooks)) { const metadata = { method, url }; - const pipeRequstHooks = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); - const pipedRequest = pipeRequstHooks(requestHooks); + const pipeRequestHooks = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); + const pipedRequest = pipeRequestHooks(requestHooks); request = pipedRequest(request); } diff --git a/test/test.js b/test/test.js index 48a9ee3..2792b2e 100644 --- a/test/test.js +++ b/test/test.js @@ -148,22 +148,40 @@ describe('dicomweb.api.DICOMwebClient', function() { }, 15000); describe('Request hooks', function() { - let requestHook1Spy, requestHook2Spy; - - beforeAll(function() { - requestHook1Spy = createSpy('requestHook1Spy').and.callFake((request, metadata) => request); - requestHook2Spy = createSpy('requestHook2Spy').and.callFake((request, metadata) => request); + let requestHook1Spy, requestHook2Spy, url, metadataUrl, request; + + beforeEach(function() { + request = new XMLHttpRequest(); + url = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs'; + metadataUrl = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs/studies/999.999.3859744/series/999.999.94827453/instances/999.999.133.1996.1.1800.1.6.25/metadata'; + requestHook1Spy = createSpy('requestHook1Spy', function (request, metadata) { return request }).and.callFake((request, metadata) => request); + requestHook2Spy = createSpy('requestHook2Spy', function (request, metadata) { return request }).and.callFake((request, metadata) => request); }); - it('request hooks should be called', async function() { - const url = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs'; - const metadataUrl = 'http://localhost:8008/dcm4chee-arc/aets/DCM4CHEE/rs/studies/999.999.3859744/series/999.999.94827453/instances/999.999.133.1996.1.1800.1.6.25/metadata'; + it('invalid request hooks should be notified and ignored', async function() { + /** Spy with invalid request hook signature */ + requestHook2Spy = createSpy('requestHook2Spy', function (request) { return request }).and.callFake((request, metadata) => request); + const dwc = new DICOMwebClient.api.DICOMwebClient({ + url, + requestHooks: [requestHook1Spy, requestHook2Spy] + }); + const metadata = { url: metadataUrl, method: 'get' }; + request.open('GET', metadata.url); + await dwc.retrieveInstanceMetadata({ + studyInstanceUID: '999.999.3859744', + seriesInstanceUID: '999.999.94827453', + sopInstanceUID: '999.999.133.1996.1.1800.1.6.25', + }); + expect(requestHook1Spy).not.toHaveBeenCalledWith(request, metadata); + expect(requestHook2Spy).not.toHaveBeenCalledWith(request, metadata); + }) + + it('valid request hooks should be called', async function() { const dwc = new DICOMwebClient.api.DICOMwebClient({ url, requestHooks: [requestHook1Spy, requestHook2Spy] }); const metadata = { url: metadataUrl, method: 'get' }; - const request = new XMLHttpRequest(); request.open('GET', metadata.url); await dwc.retrieveInstanceMetadata({ studyInstanceUID: '999.999.3859744', From 4b9b26a87dec50a892c8366646a6338c242f1ca1 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Mon, 17 May 2021 17:58:51 -0300 Subject: [PATCH 08/12] cr update: add examples --- examples/index.html | 8 +++-- examples/retry.html | 72 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 examples/retry.html diff --git a/examples/index.html b/examples/index.html index 1b3a9ae..5816bd1 100644 --- a/examples/index.html +++ b/examples/index.html @@ -9,6 +9,8 @@

Example


+ Request hooks +
@@ -17,7 +19,7 @@

- + + + + + From c9d5d813de28e69e9c5321afe92da225aa6691ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 May 2021 08:38:24 -0400 Subject: [PATCH 09/12] Bump lodash from 4.17.19 to 4.17.21 (#38) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0b66e62..0087ef1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2568,7 +2568,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -2607,9 +2607,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "log4js": { From ba8b15d6b548ea3a953e08af614746af30be5a6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 May 2021 08:38:43 -0400 Subject: [PATCH 10/12] Bump hosted-git-info from 2.7.1 to 2.8.9 (#37) Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.7.1 to 2.8.9. - [Release notes](https://github.com/npm/hosted-git-info/releases) - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md) - [Commits](https://github.com/npm/hosted-git-info/compare/v2.7.1...v2.8.9) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0087ef1..fb337ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2194,9 +2194,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "http-errors": { From 4799d1e4ef1489100d940e6a8eb80bddb317643d Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Wed, 26 May 2021 14:40:05 +0200 Subject: [PATCH 11/12] Add verbose options for request warnings and errors (#35) --- src/api.js | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/api.js b/src/api.js index e69a936..24c0557 100644 --- a/src/api.js +++ b/src/api.js @@ -37,6 +37,7 @@ class DICOMwebClient { * @param {String} options.username - Username * @param {String} options.password - Password * @param {Object} options.headers - HTTP headers + * @param {Object} options.verbose - print to console request warnings and errors, default true */ constructor(options) { this.baseURL = options.url; @@ -80,6 +81,27 @@ class DICOMwebClient { // Optional error interceptor callback to handle any failed request. this.errorInterceptor = options.errorInterceptor || function() {}; + + // Verbose - print to console request warnings and errors, default true + this.verbose = options.verbose === false ? false : true; + } + + /** + * Sets verbose flag. + * + * @param {Boolean} verbose + */ + setVerbose(verbose) { + this.verbose = verbose + } + + /** + * Gets verbose flag. + * + * @return {Boolean} verbose + */ + getVerbose() { + return this.verbose; } static _parseQueryParameters(params = {}) { @@ -138,24 +160,30 @@ class DICOMwebClient { }; // Handle response message - request.onreadystatechange = function onreadystatechange() { + request.onreadystatechange = () => { if (request.readyState === 4) { if (request.status === 200) { resolve(request.response); } else if (request.status === 202) { - console.warn("some resources already existed: ", request); + if (this.verbose) { + console.warn("some resources already existed: ", request); + } resolve(request.response); } else if (request.status === 204) { - console.warn("empty response for request: ", request); + if (this.verbose) { + console.warn("empty response for request: ", request); + } resolve([]); } else { - console.error("request failed: ", request); const error = new Error("request failed"); error.request = request; error.response = request.response; error.status = request.status; - console.error(error); - console.error(error.response); + if (this.verbose) { + console.error("request failed: ", request); + console.error(error); + console.error(error.response); + } errorInterceptor(error); From 3eb39eb397072c184698b26bbb664f20246b0bc9 Mon Sep 17 00:00:00 2001 From: igoroctaviano Date: Wed, 2 Jun 2021 08:06:12 -0300 Subject: [PATCH 12/12] CR Updates --- src/api.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api.js b/src/api.js index 990c734..3895fae 100644 --- a/src/api.js +++ b/src/api.js @@ -8,9 +8,11 @@ function isEmptyObject(obj) { return Object.keys(obj).length === 0 && obj.constructor === Object; } -function validRequestHooks(requestHooks) { +function areValidRequestHooks(requestHooks) { const isValid = Array.isArray(requestHooks) && requestHooks.every(requestHook => - typeof requestHook === 'function' && requestHook.length === 2 + typeof requestHook === 'function' + && requestHook.length === 2 + && requestHook(new XMLHttpRequest()) instanceof XMLHttpRequest ); if (!isValid) { @@ -226,7 +228,7 @@ class DICOMwebClient { } } - if (requestHooks && validRequestHooks(requestHooks)) { + if (requestHooks && areValidRequestHooks(requestHooks)) { const metadata = { method, url }; const pipeRequestHooks = functions => (args) => functions.reduce((args, fn) => fn(args, metadata), args); const pipedRequest = pipeRequestHooks(requestHooks);