Add velocity-dropwizard: the service tier (first increment)#11
Merged
Conversation
The Dropwizard 5 service serving the velocity-api HTTP contract over the velocity-core
engine, wired with Dagger 2, behind namespace-scoped API-key auth. First increment:
record + query + capabilities endpoints, proven end-to-end over real HTTP.
- VelocityApplication (Dropwizard 5) builds a Dagger graph (VelocityModule/VelocityComponent
provide the VelocityEngine = BackendRegistry + MutableFeatureDefinitionProvider +
DimensionHasher + CapabilityValidator) and registers the JAX-RS resource behind the
auth filter + a problem exception mapper. The backend is supplied via createBackend()
(overridable) so a deployment wires a velocity-backend-*; tests wire the in-memory one.
- VelocityResource: POST /v1/{ns}/record, POST /v1/{ns}/query, GET /v1/{ns}/capabilities
(AR-2). Money and every value are JSON STRINGS of a decimal integer (OQ-D) — BigDecimal
cents <-> String, no float.
- ApiKeyFilter (P9/NFR-21): X-API-Key -> allowed-namespace set; missing/blank/unknown -> 401,
a key outside the path {namespace} -> 403 (fail-closed, cross-tenant access blocked).
RFC 9457 problem responses (AR-5).
- Hand-written wire DTOs faithful to the OpenAPI (FeatureResult -> Success|Failure with a
"kind" discriminator, ADR 0009 — never a silent success).
- No Jackson/Dagger leak concerns: Jackson is legitimate here (JSON service); Dagger-generated
code excluded from the coverage gate.
15 tests (a DropwizardAppExtension HTTP integration test proving record->query round-trip,
fan-out, read-your-write, and the 401/403 auth paths; plus WireMapper/ExceptionMapper/
ApiKeyFilter unit tests), 0 skipped; coverage 96.9% line / 86.4% branch (gate 80/70).
Hand-implemented (implementer subagents stalled). Validated by the change-validator agent:
VERDICT APPROVE.
Deferred (follow-ups): features + purge resources; openapi-generator server DTOs (replacing
the hand-written ones); real backend selection/config; finer AR-5 status mapping (unknown-
namespace 404 / unsupported-window 422).
FOLLOW-UP TO RECONCILE (flagged by review): the query request/response shape DIVERGES between
velocity-api (tuple-based: {tuples:[{subject,aggregation,window}]} -> positional results) and
velocity-core (feature-name-based: query(ns, subject, [featureNames]) -> map). The module
faithfully wraps the core engine; the velocity-api yaml and velocity-core query model need to
be reconciled before the openapi-generator cutover (AR-3).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The velocity-api OpenAPI query was tuple-based ({tuples:[{subject,aggregation,
window}]} -> positional results) while velocity-core (and the velocity-dropwizard
service that wraps it) is feature-name-based. Align the contract to the engine:
- QueryRequest is now {subject, features:[name]}; QueryResponse maps each feature
name to its per-window FeatureResult list — matching VelocityEngine.query and the
service's wire DTOs (which were already feature-name-based).
- Remove the now-unused QueryTuple schema; update the OpenApiSpecTest schema list.
Resolves the query request/response divergence flagged in this PR's review, unblocking
velocity-client-java generation from a contract that agrees with the engine.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Update: the query-contract divergence flagged in review is now resolved in this PR. Aligned the |
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.
Summary
velocity-dropwizard— the Dropwizard 5 service tier, serving thevelocity-apiHTTP contractover the
velocity-coreengine, wired with Dagger 2, behind namespace-scoped API-key auth.First increment: record + query + capabilities, proven end-to-end over real HTTP.
What's in it
VelocityApplicationbuilds a Dagger graph (VelocityModule/VelocityComponentprovide theVelocityEngine=BackendRegistry+MutableFeatureDefinitionProvider+DimensionHasher+CapabilityValidator) and registers the JAX-RS resource behind the auth filter + a problemexception mapper. The backend is supplied via
createBackend()(overridable) so a deployment wiresa
velocity-backend-*; tests wire the in-memory one.VelocityResource:POST /v1/{ns}/record,POST /v1/{ns}/query,GET /v1/{ns}/capabilities(AR-2). Money and every value are JSON strings of a decimal integer (OQ-D) —
BigDecimalcents↔
String, no float.ApiKeyFilter(P9 / NFR-21):X-API-Key→ allowed-namespace set; missing/blank/unknown → 401,a key outside the path
{namespace}→ 403 (fail-closed — a credential cannot touch anothertenant). RFC 9457 problem responses (AR-5).
FeatureResult→Success | Failurewith akinddiscriminator,ADR 0009 — never a silent success).
Tests
15 tests, 0 skipped, coverage 96.9% line / 86.4% branch (gate 80/70):
DropwizardAppExtensionHTTP integration test that boots the real app and proves record→queryround-trip, fan-out (one record updates
card.countANDcard.sum), read-your-write, and the401/403 auth paths — money round-trips as
"14950";WireMapper(incl. theUNSUPPORTED_WINDOWfailure mapping),ExceptionMapperstatus branches, and all
ApiKeyFilterbranches.Review
Hand-implemented (the implementer subagents stalled again — a delegation-tooling issue). Reviewed by
the
change-validatoragent: VERDICT APPROVE — verified the end-to-end HTTP round-trip, thatnamespace-scoped auth is fail-closed and blocks cross-tenant access, the faithful money-as-string
encoding, real Dagger wiring, and meaningful (non-padding) tests.
The query shape diverges between
velocity-api(yaml:{tuples:[{subject,aggregation,window}]}→ positional results) and
velocity-core(engine:query(ns, subject, [featureNames])→ map). Thismodule faithfully wraps the core engine, so the OpenAPI contract and the core query model need to
be reconciled before the openapi-generator cutover (AR-3) — either update the yaml to feature-name
query, or add tuple-based query to the engine. Worth an architect decision.
Other deferrals (scoped-out, not defects)
features + purge resources; openapi-generator server DTOs (replacing the hand-written ones); real
backend selection/config; finer AR-5 status mapping (unknown-namespace 404 / unsupported-window 422);
per-namespace backend selection in
/capabilities../gradlew :velocity-dropwizard:build→ BUILD SUCCESSFUL (in-JVM Jetty).🤖 Generated with Claude Code