Skip to content

fix(directory): drive VMACS endpoint from config, fail closed if unset#262

Merged
rlorenzo merged 1 commit into
mainfrom
fix/vmacs-qa-endpoint
Jul 18, 2026
Merged

fix(directory): drive VMACS endpoint from config, fail closed if unset#262
rlorenzo merged 1 commit into
mainfrom
fix/vmacs-qa-endpoint

Conversation

@rlorenzo

@rlorenzo rlorenzo commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

What & why

The VMACS directory lookup endpoint was hardcoded to the production host (vmacs-vmth), so Development and TEST were querying prod. This makes the endpoint configurable per environment and hardens the failure behavior.

Changes

Endpoint is config-driven, fails closed

  • VMACSService reads the base URL from Vmacs:BaseUrl (no hardcoded fallback). Search() only calls the endpoint when the URL parses as an absolute http(s) URI and Credentials:VmacsAuthToken is configured; otherwise it skips the request, returns no results, and logs once per process. A misconfigured box can never silently hit prod, a malformed/unsupported-scheme URL fails closed instead of throwing out of the lookup, and an environment with no token (e.g. Development) won't fan out one doomed unauthorized request per directory result.
  • A flaky endpoint can't break directory search: network failures (HttpRequestException), timeouts (TaskCanceledException — the shared HttpClient is bounded to a 10s timeout so a hung endpoint can't stall a lookup for the default 100s), and malformed responses all degrade to null + a warning instead of throwing out of the lookup. This matters because the endpoint goes down often and DirectoryController blocks on Search().Result per result — an unhandled throw would 500 the whole search.
  • The health check never echoes exception messages into its /health/detail payload (the probe URI carries the auth token), reporting a generic "unreachable" instead.

Config (each environment declares its own endpoint)

Environment Vmacs:BaseUrl
Development https://vmacs-qa.vetmed.ucdavis.edu
Test https://vmacs-qa.vetmed.ucdavis.edu
Production https://vmacs-vmth.vetmed.ucdavis.edu

The base appsettings.json intentionally has no Vmacs section, so "unset" is a real, detectable state rather than a silent prod default.

Endpoint-specific health check (campus-vmacs-directory)

  • Probes the actual /trust/query.xml directory endpoint (the one Search uses) with an authenticated query, so a failure pinpoints that endpoint being down rather than a bug in VIPER — the VMACS team reports this endpoint goes down often.
  • Probe login: a fixed sentinel login _testdvm. It need not resolve to a record — a valid token returns HTTP 200 with an empty <query> for an unknown id — so the check stays green across environments with different datasets (QA is sparse) and never depends on a real person's data.
  • Severity encodes fault: an outage (unreachable / timeout / 5xx / unparseable) reports Degraded/amber; an unset/invalid URL or rejected token reports Unhealthy/red.
  • Does not affect /health liveness — VMACS being down never pulls VIPER out of rotation.

How often the endpoint is called

  • Health check: at most once per cache window — the first poll after startup (cache starts empty), then ~once/day while healthy (healthyCacheDuration: 1 day) and hourly while down (unhealthyCacheDuration: 1 hour) via AdaptivePollingHealthCheck. These windows are intentionally wider than the shared 1h/5min helper because this endpoint is known to be flaky and directory enrichment is optional — probing sparingly avoids alert noise on a non-critical dependency.
  • Directory search: Search() calls the endpoint once per matched user in a result set (DirectoryController invokes it per person as it builds the response). When the base URL or auth token is unset/invalid it skips entirely — zero calls — logging once per process.

Testing

  • VMACSServiceTest (24, incl. new absolute-http(s)-URL validation cases) and VmacsDirectoryHealthCheckTests (21) pass; full backend suite green.
  • Lint clean; pre-commit lint/test/verify all pass.
  • Verified live: the QA endpoint authenticates with the configured token and returns a parseable response for the _testdvm probe.

Targets Development per the branch/deploy flow (deploys to TEST first).

@rlorenzo
rlorenzo changed the base branch from Development to main July 17, 2026 18:35
@rlorenzo

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

VMACS directory access now uses configured base URLs, safe XML deserialization, and a dedicated health check. Registration adds adaptive polling and environment-specific token handling, while tests cover parsing, security, configuration, HTTP outcomes, cancellation, and request construction.

Changes

VMACS directory integration

Layer / File(s) Summary
Configured lookup and XML deserialization
web/Areas/Directory/Services/VMACSService.cs, test/Areas/Directory/VMACSServiceTest.cs
VMACSService reads Vmacs:BaseUrl, builds requests from it, safely parses XML, and handles invalid or missing responses.
Directory health probe
web/Classes/HealthChecks/VmacsDirectoryHealthCheck.cs, test/HealthChecks/VmacsDirectoryHealthCheckTests.cs
The health check validates configuration, probes VMACS, parses responses, and maps authentication, server, parsing, network, timeout, and cancellation results to health statuses.
Registration and environment configuration
web/Classes/HealthChecks/HealthCheckExtensions.cs, web/appsettings.*.json
Health-check registration uses the directory probe, adaptive polling, configured credentials, and environment-specific VMACS base URLs.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HealthCheck as VmacsDirectoryHealthCheck
  participant HttpClient
  participant VMACS as VMACS endpoint
  participant Parser as VMACSService.Deserialize
  HealthCheck->>HttpClient: Send directory probe with login and AUTH token
  HttpClient->>VMACS: HTTP GET request
  VMACS-->>HttpClient: HTTP response and XML body
  HttpClient-->>HealthCheck: Response status and body
  HealthCheck->>Parser: Deserialize XML body
  Parser-->>HealthCheck: VMACSQuery or InvalidOperationException
  HealthCheck-->>HealthCheck: Return mapped health status
Loading

Possibly related PRs

  • ucdavis/VIPER#159: Introduced earlier VMACS adaptive-polling health-check plumbing replaced by this directory health check.
  • ucdavis/VIPER#226: Related VMACSService request construction and BuildSearchPath behavior.

Suggested reviewers: bsedwards

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the main change: VMACS endpoint config-driven behavior with fail-closed handling when unset.
Description check ✅ Passed The description is directly related and accurately summarizes the config-driven endpoint, fail-closed behavior, health check, and tests.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/vmacs-qa-endpoint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/Areas/Directory/Services/VMACSService.cs`:
- Around line 31-42: Require VmacsBaseUrl to parse as an absolute HTTP or HTTPS
URI before VMACSService calls sharedClient.GetAsync; return null and preserve
the existing missing/invalid configuration logging behavior otherwise. Add or
reuse a shared helper for this validation, and update VmacsDirectoryHealthCheck
to use it and report Unhealthy for invalid HTTP/S configuration. Apply the
service change in web/Areas/Directory/Services/VMACSService.cs lines 31-42 and
the health-check change in web/Classes/HealthChecks/VmacsDirectoryHealthCheck.cs
lines 49-67.

In `@web/Classes/HealthChecks/HealthCheckExtensions.cs`:
- Around line 197-198: Update the healthyCacheDuration and
unhealthyCacheDuration arguments in the health-check registration to use the
existing bounded policy of one hour for successful probes and five minutes for
failed probes, or equivalent shorter intervals; leave the surrounding
health-check configuration unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5e684d1a-13f8-4343-a550-7f45c8afff2c

📥 Commits

Reviewing files that changed from the base of the PR and between 0c5ab2b and 8493473.

📒 Files selected for processing (8)
  • test/Areas/Directory/VMACSServiceTest.cs
  • test/HealthChecks/VmacsDirectoryHealthCheckTests.cs
  • web/Areas/Directory/Services/VMACSService.cs
  • web/Classes/HealthChecks/HealthCheckExtensions.cs
  • web/Classes/HealthChecks/VmacsDirectoryHealthCheck.cs
  • web/appsettings.Development.json
  • web/appsettings.Production.json
  • web/appsettings.Test.json

Comment thread web/Areas/Directory/Services/VMACSService.cs Outdated
Comment thread web/Classes/HealthChecks/HealthCheckExtensions.cs
@rlorenzo
rlorenzo force-pushed the fix/vmacs-qa-endpoint branch from 8493473 to 30ebb6e Compare July 17, 2026 19:46
@codecov-commenter

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

Comment thread test/HealthChecks/VmacsDirectoryHealthCheckTests.cs
Comment thread test/HealthChecks/VmacsDirectoryHealthCheckTests.cs
Comment thread test/HealthChecks/VmacsDirectoryHealthCheckTests.cs Dismissed
Comment thread test/HealthChecks/VmacsDirectoryHealthCheckTests.cs Dismissed
@codecov-commenter

codecov-commenter commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 54.83871% with 56 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.39%. Comparing base (efa132b) to head (4a71b4d).

Files with missing lines Patch % Lines
web/Areas/Directory/Services/VMACSService.cs 25.00% 42 Missing ⚠️
web/Classes/HealthChecks/HealthCheckExtensions.cs 0.00% 13 Missing ⚠️
.../Classes/HealthChecks/VmacsDirectoryHealthCheck.cs 98.18% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #262      +/-   ##
==========================================
+ Coverage   45.31%   45.39%   +0.07%     
==========================================
  Files         915      916       +1     
  Lines       52690    52784      +94     
  Branches     4992     5005      +13     
==========================================
+ Hits        23877    23960      +83     
- Misses      28186    28196      +10     
- Partials      627      628       +1     
Flag Coverage Δ
backend 45.42% <54.83%> (+0.08%) ⬆️
frontend 44.76% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@rlorenzo
rlorenzo force-pushed the fix/vmacs-qa-endpoint branch 2 times, most recently from 9635358 to e6f59ae Compare July 17, 2026 21:25
@rlorenzo
rlorenzo requested a review from Copilot July 17, 2026 21:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR removes the hardcoded VMACS directory lookup host and makes the directory query endpoint environment-configurable via Vmacs:BaseUrl, adding a targeted health check for the /trust/query.xml endpoint and hardening failure behavior so misconfiguration can’t silently hit production.

Changes:

  • Make VMACSService.Search() drive the VMACS base URL from configuration and fail closed when unset/invalid; move XML parsing into a reusable Deserialize() helper with safer degradation on malformed responses.
  • Add campus-vmacs-directory health check that probes the real authenticated directory query endpoint with adaptive polling and severity mapping.
  • Declare Vmacs:BaseUrl per environment in appsettings.{Development,Test,Production}.json.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
web/Classes/HealthChecks/VmacsDirectoryHealthCheck.cs Adds an endpoint-specific health check that authenticates and validates response shape for /trust/query.xml.
web/Classes/HealthChecks/HealthCheckExtensions.cs Registers the new adaptive-polling campus-vmacs-directory health check and wires config values into it.
web/Areas/Directory/Services/VMACSService.cs Removes hardcoded prod host, validates config-driven base URL, and hardens XML deserialization behavior.
web/appsettings.Development.json Adds Vmacs:BaseUrl pointing to QA for Development.
web/appsettings.Test.json Adds Vmacs:BaseUrl pointing to QA for Test.
web/appsettings.Production.json Adds Vmacs:BaseUrl pointing to prod for Production.
test/HealthChecks/VmacsDirectoryHealthCheckTests.cs Adds coverage for the new health check’s status mapping and request construction.
test/Areas/Directory/VMACSServiceTest.cs Adds tests for base URL validation and XML deserialization behavior.

Comment thread web/Areas/Directory/Services/VMACSService.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread web/Areas/Directory/Services/VMACSService.cs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

web/Areas/Directory/Services/VMACSService.cs:135

  • BuildSearchPath interpolates the VMACS auth token directly into the query string without URL-encoding. If the token contains characters like '+', '/', or '=' (common in tokens), the request URI can be malformed or interpreted incorrectly. URL-encode the token the same way loginID is encoded.
            string encodedLoginId = Uri.EscapeDataString(loginID ?? string.Empty);
            return $"/trust/query.xml?dbfile=3&index=CampusLoginId&find={encodedLoginId}&format=CHRIS4&AUTH={authToken}";
        }

Comment thread web/Areas/Directory/Services/VMACSService.cs Outdated
Comment thread web/Classes/HealthChecks/VmacsDirectoryHealthCheck.cs Outdated
- Read base URL from Vmacs:BaseUrl (vmacs-qa in dev/test, vmacs-vmth in
  prod); remove the hardcoded prod fallback
- Skip the lookup and log once when unset, so a misconfigured box returns
  no results instead of silently hitting prod
- Degrade to null (log a warning) on a malformed VMACS response instead of
  failing the directory search
- Add campus-vmacs-directory health check: an authenticated /trust/query.xml
  probe reporting an outage as Degraded and a missing URL or rejected token
  as Unhealthy, checked ~daily while healthy and hourly while down
@rlorenzo
rlorenzo force-pushed the fix/vmacs-qa-endpoint branch from 442b383 to 4a71b4d Compare July 17, 2026 23:28
@rlorenzo
rlorenzo requested review from bniedzie and bsedwards July 18, 2026 00:06
@rlorenzo

Copy link
Copy Markdown
Contributor Author

@bsedwards Alex complained that hitting the production URL for the VMACS Trust URL was causing too many emails to be sent to them. So this PR changes our code to use the QA URL for Development and TEST environments.

@rlorenzo
rlorenzo merged commit 3bc4b99 into main Jul 18, 2026
13 of 14 checks passed
@rlorenzo
rlorenzo deleted the fix/vmacs-qa-endpoint branch July 18, 2026 00:12
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.

5 participants