Skip to content

fix(DicomWebDataSource): set correct cross-service URLs on DICOMweb clients#6009

Open
galenzo17 wants to merge 2 commits into
OHIF:masterfrom
galenzo17:fix/dicomweb-cross-service-urls
Open

fix(DicomWebDataSource): set correct cross-service URLs on DICOMweb clients#6009
galenzo17 wants to merge 2 commits into
OHIF:masterfrom
galenzo17:fix/dicomweb-cross-service-urls

Conversation

@galenzo17
Copy link
Copy Markdown

@galenzo17 galenzo17 commented May 9, 2026

Summary

  • After client construction, assigns the correct cross-service URLs (wadoURL, qidoURL, stowURL) on each DICOMweb client so requests are routed to the right endpoint when qidoRoot and wadoRoot differ (e.g. /qidors/ vs /wadors/)
  • Adds optional stowRoot config field for deployments with a separate STOW endpoint
  • Adds unit tests covering cross-service URL assignment, explicit stowRoot, same-root (no-op), and stowRoot fallback scenarios

Closes #5820

Test plan

  • Cross-service URLs are correctly assigned when qidoRoot and wadoRoot differ
  • Explicit stowRoot is used when provided
  • Backward-compatible: no-op when qidoRoot === wadoRoot
  • stowURL defaults to wadoRoot when stowRoot is omitted

Greptile Summary

This PR fixes cross-service URL routing in the DicomWebDataSource by introducing applyServiceUrls, a utility that post-construction assigns the correct wadoURL, qidoURL, and stowURL on each DICOMweb client when qidoRoot and wadoRoot differ. It also adds an optional stowRoot config field and a dedicated test suite for the new utility.

  • applyServiceUrls.ts guards assignments with !== undefined checks and uses ?? for stowRoot fallback — both are improvements over naive falsy checks.
  • index.ts wires the utility into the initialize block immediately after client construction and extends DicomWebConfig with stowRoot.
  • The new test file covers four representative scenarios but misses the edge case where qidoRoot is provided while both wadoRoot and stowRoot are absent, which would cause wadoClient.stowURL to be set to undefined at runtime.

Confidence Score: 4/5

Safe to merge after addressing the effectiveStowRoot undefined-assignment edge case in applyServiceUrls.ts

When config.qidoRoot is defined but both config.wadoRoot and config.stowRoot are absent, effectiveStowRoot is undefined and wadoClient.stowURL is overwritten with it — silently corrupting the client for any STOW operation. The guard on qidoRoot only protects wadoClient.qidoURL, not the stowURL assignment that follows it.

extensions/default/src/DicomWebDataSource/utils/applyServiceUrls.ts — the effectiveStowRoot computation needs to move inside each if guard

Important Files Changed

Filename Overview
extensions/default/src/DicomWebDataSource/utils/applyServiceUrls.ts New utility that cross-assigns service URLs; has a type-unsafe effectiveStowRoot that can be undefined inside the qidoRoot guard block, corrupting wadoClient.stowURL
extensions/default/src/DicomWebDataSource/utils/applyServiceUrls.test.ts Unit tests covering four scenarios; no test for the edge case where qidoRoot is defined but wadoRoot and stowRoot are both absent
extensions/default/src/DicomWebDataSource/index.ts Wires applyServiceUrls into the initialize hook and adds stowRoot to DicomWebConfig; the integration point itself is straightforward
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
extensions/default/src/DicomWebDataSource/utils/applyServiceUrls.ts:14-24
When `config.qidoRoot` is defined but both `config.wadoRoot` and `config.stowRoot` are undefined, `effectiveStowRoot` resolves to `undefined`. The second `if` block guard only checks `config.qidoRoot !== undefined`, so `wadoClient.stowURL` is overwritten with `undefined`, corrupting the client state. TypeScript strict mode also flags the assignment since `effectiveStowRoot: string | undefined` is not assignable to `stowURL: string`. Moving `effectiveStowRoot` inside each guard (where the relevant root is guaranteed defined) eliminates both the runtime risk and the type error.

```suggestion
  if (config.wadoRoot !== undefined) {
    const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;
    qidoClient.wadoURL = config.wadoRoot;
    qidoClient.stowURL = effectiveStowRoot;
  }

  if (config.qidoRoot !== undefined) {
    const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;
    wadoClient.qidoURL = config.qidoRoot;
    wadoClient.stowURL = effectiveStowRoot;
  }
```

Reviews (3): Last reviewed commit: "fix(DicomWebDataSource): guard against u..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 9, 2026

Deploy Preview for ohif-dev canceled.

Name Link
🔨 Latest commit bfcb2d1
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/69ff667dc791540008b822f7

Comment on lines +27 to +42
* Applies the URL fix from index.ts lines ~211-219.
* Extracted here to test in isolation.
*/
function applyServiceUrls(
qidoClient: MockDICOMwebClient,
wadoClient: MockDICOMwebClient,
config: { qidoRoot: string; wadoRoot: string; stowRoot?: string }
) {
const effectiveStowRoot = config.stowRoot || config.wadoRoot;

qidoClient.wadoURL = config.wadoRoot;
qidoClient.stowURL = effectiveStowRoot;

wadoClient.qidoURL = config.qidoRoot;
wadoClient.stowURL = effectiveStowRoot;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Test logic decoupled from production code

applyServiceUrls is a hand-copied extract of the assignment block in index.ts. If the production logic changes (e.g., a guard is added, a URL property is renamed, or a new client is introduced), these tests will continue to pass while the real behavior regresses silently. The tests should import and exercise the actual createDicomWebApi initializer (or at minimum import a shared utility), not duplicate its logic inline.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/default/src/DicomWebDataSource/index.test.ts
Line: 27-42

Comment:
**Test logic decoupled from production code**

`applyServiceUrls` is a hand-copied extract of the assignment block in `index.ts`. If the production logic changes (e.g., a guard is added, a URL property is renamed, or a new client is introduced), these tests will continue to pass while the real behavior regresses silently. The tests should import and exercise the actual `createDicomWebApi` initializer (or at minimum import a shared utility), not duplicate its logic inline.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread extensions/default/src/DicomWebDataSource/index.ts Outdated
…lients

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/).

Extract applyServiceUrls into a shared utility so the production code
and tests exercise the same function. After constructing the clients,
call applyServiceUrls to assign the correct cross-service URLs. Also
adds an optional stowRoot config field for deployments with a separate
STOW endpoint.

Closes OHIF#5820

Signed-off-by: Agustin Bereciartua <bereciartua.agustin@gmail.com>
@galenzo17 galenzo17 force-pushed the fix/dicomweb-cross-service-urls branch from e8dc14f to 37ef346 Compare May 9, 2026 16:22
Comment thread extensions/default/src/DicomWebDataSource/utils/applyServiceUrls.ts Outdated
…h coalescing

Use ?? instead of || for stowRoot fallback to respect explicit empty
strings. Wrap assignments in undefined checks so client URLs are not
overwritten when a root is not provided.

Signed-off-by: Agustin Bereciartua <bereciartua.agustin@gmail.com>
Comment on lines +14 to +24
const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;

if (config.wadoRoot !== undefined) {
qidoClient.wadoURL = config.wadoRoot;
qidoClient.stowURL = effectiveStowRoot;
}

if (config.qidoRoot !== undefined) {
wadoClient.qidoURL = config.qidoRoot;
wadoClient.stowURL = effectiveStowRoot;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 When config.qidoRoot is defined but both config.wadoRoot and config.stowRoot are undefined, effectiveStowRoot resolves to undefined. The second if block guard only checks config.qidoRoot !== undefined, so wadoClient.stowURL is overwritten with undefined, corrupting the client state. TypeScript strict mode also flags the assignment since effectiveStowRoot: string | undefined is not assignable to stowURL: string. Moving effectiveStowRoot inside each guard (where the relevant root is guaranteed defined) eliminates both the runtime risk and the type error.

Suggested change
const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;
if (config.wadoRoot !== undefined) {
qidoClient.wadoURL = config.wadoRoot;
qidoClient.stowURL = effectiveStowRoot;
}
if (config.qidoRoot !== undefined) {
wadoClient.qidoURL = config.qidoRoot;
wadoClient.stowURL = effectiveStowRoot;
}
if (config.wadoRoot !== undefined) {
const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;
qidoClient.wadoURL = config.wadoRoot;
qidoClient.stowURL = effectiveStowRoot;
}
if (config.qidoRoot !== undefined) {
const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;
wadoClient.qidoURL = config.qidoRoot;
wadoClient.stowURL = effectiveStowRoot;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/default/src/DicomWebDataSource/utils/applyServiceUrls.ts
Line: 14-24

Comment:
When `config.qidoRoot` is defined but both `config.wadoRoot` and `config.stowRoot` are undefined, `effectiveStowRoot` resolves to `undefined`. The second `if` block guard only checks `config.qidoRoot !== undefined`, so `wadoClient.stowURL` is overwritten with `undefined`, corrupting the client state. TypeScript strict mode also flags the assignment since `effectiveStowRoot: string | undefined` is not assignable to `stowURL: string`. Moving `effectiveStowRoot` inside each guard (where the relevant root is guaranteed defined) eliminates both the runtime risk and the type error.

```suggestion
  if (config.wadoRoot !== undefined) {
    const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;
    qidoClient.wadoURL = config.wadoRoot;
    qidoClient.stowURL = effectiveStowRoot;
  }

  if (config.qidoRoot !== undefined) {
    const effectiveStowRoot = config.stowRoot ?? config.wadoRoot;
    wadoClient.qidoURL = config.qidoRoot;
    wadoClient.stowURL = effectiveStowRoot;
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Incorrect URL Construction for DICOMweb QIDO/WADO Requests

1 participant