feat(#119 F1): resource-server scope foundation + dev XML capture/sample#167
Merged
Merged
Conversation
Foundation for the canonical DC ESPI resource surface (#119): the security-critical scope core every resource endpoint will enforce, so subscription scoping / the data-leakage fix lives in one tested component rather than per controller. Scope core (web/api/scope): - ResourceScope: resolved per-request visibility — admin/unfiltered, or a granted energy UsagePoint-id set + PII RetailCustomer-id set; fail-closed denied(). UUID resources, Long RetailCustomer correlation key. - ScopeResolver: token -> ResourceScope. DataCustodian-admin authority or Batch/Bulk -> admin; Batch/Subscription resourceURI -> the subscription's granted UsagePoint ids (read from the DC's own store via the new lazy-safe SubscriptionRepository.findUsagePointIdsBySubscriptionId, never from the token body); Batch/RetailCustomer customerResourceURI -> the PII RetailCustomer id; otherwise denied. URIs parsed via the canonical EspiBatchUri. - EspiQueryOptions: REQ.21.6.2.8 feed query params — ALL optional. published/updated min/max are required-to-support timestamp filters; max-results/start-index/start-after/depth are optional best-effort DB knobs. No limit/offset pagination (ESPI CMD/DMD are machine-to-machine). Dev tooling (web/api/devtools): - EspiXmlCaptureFilter: dev-only (@Profile dev-*) servlet filter that tees ESPI XML responses under /espi/1_1/resource/** to per-run sub-dirs under repo-root dev-xml-capture/ (git-ignored) so a developer can view/diff the marshalled XML before committing. Zero impact on test/prod/CI. - DevXmlSampleGenerator: on-demand generator (not run by CI) that marshals a sample resource via the real UsageExportService into dev-xml-capture/sample/ — view ESPI XML without a full server. Reuses the existing tested Atom/DTO/IdentifiedObject marshalling — F1 adds none. The resource engine + entry-assembly (self/up/related FB-gated atom:links, per REQ.21.6.2) lands in E1 with the first real resource (UsagePoint/MeterReading/ReadingType), exercised end-to-end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First foundation slice of the canonical DC ESPI resource surface (#119) — the security-critical scope core every resource endpoint will enforce, so subscription scoping (the data-leakage fix) lives in one tested component instead of per controller. Reuses the existing tested Atom/DTO/
IdentifiedObjectmarshalling — F1 adds none.Scope core (
web/api/scope)ResourceScope— resolved per-request visibility:unfiltered()(admin/Bulk), or a granted energy UsagePoint-id set + PII RetailCustomer-id set; fail-closeddenied(). UUID resources;LongRetailCustomer correlation key (RetailCustomer is not an ESPI resource).ScopeResolver— token →ResourceScope: DataCustodian-admin authority orBatch/Bulk→ admin;Batch/SubscriptionresourceURI→ the subscription's granted UsagePoint ids (read from the DC's own store, never the token body) via the new lazy-safeSubscriptionRepository.findUsagePointIdsBySubscriptionId;Batch/RetailCustomercustomerResourceURI→ the PII RetailCustomer id; otherwisedenied(). URIs parsed via the canonicalEspiBatchUri.EspiQueryOptions— REQ.21.6.2.8 feed query params, all optional:published/updated min/maxare the required-to-support timestamp filters;max-results/start-index/start-after/depthare optional best-effort DB knobs. Nolimit/offsetpagination (ESPI CMD/DMD are machine-to-machine).Dev tooling (
web/api/devtools)EspiXmlCaptureFilter— dev-only (@Profile dev-mysql/dev-postgresql/local/dev) servlet filter that tees ESPI XML responses under/espi/1_1/resource/**into per-run sub-dirs under repo-rootdev-xml-capture/(git-ignored) so a developer can view/diffthe marshalled XML before committing. Zero impact on test/prod/CI; body passes through unchanged.DevXmlSampleGenerator— on-demand (not run by CI;-Dtest=DevXmlSampleGenerator) generator that marshals a sample resource via the realUsageExportServiceintodev-xml-capture/sample/— view ESPI XML without standing up the full server.Tests
ScopeResolverTest(admin / Bulk / subscription→usage-points / PII / fail-closed denied),ResourceScopeTest,EspiQueryOptionsTest,EspiXmlCaptureFilterTest. The new repo@Queryis validated by the existingSubscriptionRepositoryTest(JPA/H2 bootstrap).Next (E1, not this PR)
The delegation-based resource engine + entry assembly —
self/up/relatedatom:links (related FB-gated, per REQ.21.6.2;type=espi-feed/…for collection hrefs,espi-entry/…for id hrefs; links on entries only) — lands in E1 with the first real resource (UsagePoint/MeterReading/ReadingType), exercised end-to-end via MockMvc. (One open decision for E1: default vsatom:-prefixed Atom namespace.)🤖 Generated with Claude Code