Skip to content

Commit e8dc14f

Browse files
committed
fix(DicomWebDataSource): set correct cross-service URLs on DICOMweb clients
The dicomweb-client library sets qidoURL, wadoURL, and stowURL all to the same base url when no prefixes are provided. This causes requests to be routed to the wrong endpoint when qidoRoot and wadoRoot differ (e.g. /qidors/ vs /wadors/). After constructing the clients, assign the correct cross-service URLs so each client can reach all three DICOMweb services. Also adds an optional stowRoot config field for deployments with a separate STOW endpoint. Closes #5820 Signed-off-by: Agustin Bereciartua <bereciartua.agustin@gmail.com>
1 parent d792bf7 commit e8dc14f

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Tests for the DICOMweb client URL construction fix.
3+
*
4+
* The dicomweb-client library sets qidoURL, wadoURL, stowURL all to the base
5+
* `url` when no prefixes are provided. After construction, we must set the
6+
* correct cross-service URLs on each client so that QIDO/WADO/STOW operations
7+
* use the right endpoint.
8+
*
9+
* These tests validate the fix logic in isolation since the full
10+
* createDicomWebApi function has deep transitive dependencies.
11+
*/
12+
13+
class MockDICOMwebClient {
14+
qidoURL: string;
15+
wadoURL: string;
16+
stowURL: string;
17+
18+
constructor(config: { url: string }) {
19+
// Replicates dicomweb-client v0.10.4 behavior: all URLs default to base url
20+
this.qidoURL = config.url;
21+
this.wadoURL = config.url;
22+
this.stowURL = config.url;
23+
}
24+
}
25+
26+
/**
27+
* Applies the URL fix from index.ts lines ~211-219.
28+
* Extracted here to test in isolation.
29+
*/
30+
function applyServiceUrls(
31+
qidoClient: MockDICOMwebClient,
32+
wadoClient: MockDICOMwebClient,
33+
config: { qidoRoot: string; wadoRoot: string; stowRoot?: string }
34+
) {
35+
const effectiveStowRoot = config.stowRoot || config.wadoRoot;
36+
37+
qidoClient.wadoURL = config.wadoRoot;
38+
qidoClient.stowURL = effectiveStowRoot;
39+
40+
wadoClient.qidoURL = config.qidoRoot;
41+
wadoClient.stowURL = effectiveStowRoot;
42+
}
43+
44+
describe('DICOMweb client URL construction', () => {
45+
it('should fix cross-service URLs when roots differ', () => {
46+
const qidoClient = new MockDICOMwebClient({ url: 'https://server.com/qidors/org1' });
47+
const wadoClient = new MockDICOMwebClient({ url: 'https://server.com/wadors/org1' });
48+
49+
// Before fix: each client has all URLs pointing to its own root
50+
expect(qidoClient.wadoURL).toBe('https://server.com/qidors/org1');
51+
expect(wadoClient.qidoURL).toBe('https://server.com/wadors/org1');
52+
53+
applyServiceUrls(qidoClient, wadoClient, {
54+
qidoRoot: 'https://server.com/qidors/org1',
55+
wadoRoot: 'https://server.com/wadors/org1',
56+
});
57+
58+
// After fix: each client has correct cross-service URLs
59+
expect(qidoClient.qidoURL).toBe('https://server.com/qidors/org1');
60+
expect(qidoClient.wadoURL).toBe('https://server.com/wadors/org1');
61+
expect(qidoClient.stowURL).toBe('https://server.com/wadors/org1');
62+
63+
expect(wadoClient.qidoURL).toBe('https://server.com/qidors/org1');
64+
expect(wadoClient.wadoURL).toBe('https://server.com/wadors/org1');
65+
expect(wadoClient.stowURL).toBe('https://server.com/wadors/org1');
66+
});
67+
68+
it('should use explicit stowRoot when provided', () => {
69+
const qidoClient = new MockDICOMwebClient({ url: 'https://server.com/qidors/org1' });
70+
const wadoClient = new MockDICOMwebClient({ url: 'https://server.com/wadors/org1' });
71+
72+
applyServiceUrls(qidoClient, wadoClient, {
73+
qidoRoot: 'https://server.com/qidors/org1',
74+
wadoRoot: 'https://server.com/wadors/org1',
75+
stowRoot: 'https://server.com/stowrs/org1',
76+
});
77+
78+
expect(qidoClient.stowURL).toBe('https://server.com/stowrs/org1');
79+
expect(wadoClient.stowURL).toBe('https://server.com/stowrs/org1');
80+
});
81+
82+
it('should be a no-op when qidoRoot and wadoRoot are the same', () => {
83+
const qidoClient = new MockDICOMwebClient({ url: 'https://server.com/dicomweb' });
84+
const wadoClient = new MockDICOMwebClient({ url: 'https://server.com/dicomweb' });
85+
86+
applyServiceUrls(qidoClient, wadoClient, {
87+
qidoRoot: 'https://server.com/dicomweb',
88+
wadoRoot: 'https://server.com/dicomweb',
89+
});
90+
91+
// All URLs should be the same (backward-compatible)
92+
expect(qidoClient.qidoURL).toBe('https://server.com/dicomweb');
93+
expect(qidoClient.wadoURL).toBe('https://server.com/dicomweb');
94+
expect(qidoClient.stowURL).toBe('https://server.com/dicomweb');
95+
expect(wadoClient.qidoURL).toBe('https://server.com/dicomweb');
96+
expect(wadoClient.wadoURL).toBe('https://server.com/dicomweb');
97+
expect(wadoClient.stowURL).toBe('https://server.com/dicomweb');
98+
});
99+
100+
it('should default stowURL to wadoRoot when stowRoot is not provided', () => {
101+
const qidoClient = new MockDICOMwebClient({ url: 'https://server.com/qidors' });
102+
const wadoClient = new MockDICOMwebClient({ url: 'https://server.com/wadors' });
103+
104+
applyServiceUrls(qidoClient, wadoClient, {
105+
qidoRoot: 'https://server.com/qidors',
106+
wadoRoot: 'https://server.com/wadors',
107+
});
108+
109+
expect(qidoClient.stowURL).toBe('https://server.com/wadors');
110+
expect(wadoClient.stowURL).toBe('https://server.com/wadors');
111+
});
112+
});

extensions/default/src/DicomWebDataSource/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type DicomWebConfig = {
3535
/** Base URL to use for QIDO requests */
3636
qidoRoot?: string;
3737
wadoRoot?: string; // - Base URL to use for WADO requests
38+
stowRoot?: string; // - Base URL to use for STOW requests (defaults to wadoRoot)
3839
wadoUri?: string; // - Base URL to use for WADO URI requests
3940
qidoSupportsIncludeField?: boolean; // - Whether QIDO supports the "Include" option to request additional fields in response
4041
imageRendering?: string; // - wadors | ? (unsure of where/how this is used)
@@ -207,6 +208,17 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) {
207208
wadoDicomWebClient = dicomWebConfig.staticWado
208209
? new StaticWadoClient(wadoConfig)
209210
: new api.DICOMwebClient(wadoConfig);
211+
212+
// Ensure each client has correct URLs for all DICOMweb services.
213+
// Without this, each client uses its own base URL for ALL services,
214+
// which breaks when QIDO/WADO/STOW have different endpoint paths.
215+
const effectiveStowRoot = dicomWebConfig.stowRoot || dicomWebConfig.wadoRoot;
216+
217+
qidoDicomWebClient.wadoURL = dicomWebConfig.wadoRoot;
218+
qidoDicomWebClient.stowURL = effectiveStowRoot;
219+
220+
wadoDicomWebClient.qidoURL = dicomWebConfig.qidoRoot;
221+
wadoDicomWebClient.stowURL = effectiveStowRoot;
210222
},
211223
query: {
212224
studies: {

0 commit comments

Comments
 (0)