feat(idempotency): ActionAwareIdempotencyMiddleware — auto-wire from resolved Action (#182)#183
Merged
Merged
Conversation
…resolved Action (#182) Closes the idempotency circle from #171. Host applications no longer hand-wire IdempotencyKeyMiddleware per route — a single global middleware reads each request's resolved Action via the altair:http:action attribute, looks for the static idempotency() accessor the scaffolder emits (#174), and configures a per-request IdempotencyKeyMiddleware (#173) from that policy. Pieces - Altair\Idempotency\Middleware\ActionAwareIdempotencyMiddleware - Reads attribute (configurable; default matches Altair\Http\Contracts\MiddlewareInterface::ATTRIBUTE_ACTION). - Passes through when no Action on request, when Action lacks idempotency(), or when the returned policy is malformed. - Delegates to a fresh IdempotencyKeyMiddleware per request, parametrised by spec ttl/mode. - Altair\Idempotency\Hash\TtlParser - Pure utility: '24h' / '500ms' / '7d' → seconds. Rejects malformed strings with IdempotencyException. ms < 1000 round up to 1 second (storage TTLs are second-granular on the wire). Host wiring is now two lines: register IdempotencyConfiguration, add ActionAwareIdempotencyMiddleware after DispatcherMiddleware and before ActionMiddleware. docs/packages/idempotency.md quick-start is rewritten around this pattern; the manual IdempotencyKeyMiddleware recipe stays as the escape hatch for hosts that need to override the spec-driven policy. No coupling added to univeros/http. The attribute key is a constructor-injectable string defaulting to the value univeros/http ships, so the package stays portable. Tests (+17) - TtlParserTest: ms/s/m/h/d units, ms rounding, malformed rejection, zero handling. - ActionAwareIdempotencyMiddlewareTest: pass-through paths (no attribute, no idempotency(), malformed policy), end-to-end replay via InMemoryStore, required-mode-from-action 400 path, custom attribute name configuration. Closes #182. Closes the idempotency circle from #171.
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.
Closes #182. Closes the idempotency circle opened by #171.
Summary
Eliminates the manual per-route wiring step the idempotency epic left as host responsibility. A single global middleware reads each request's resolved Action via the
altair:http:actionrequest attribute, looks for the staticidempotency()accessor the scaffolder emits (#174), and configures a per-requestIdempotencyKeyMiddleware(#173) from that policy.Pieces
ActionAwareIdempotencyMiddleware\xe2\x80\x94 PSR-15. Reads the attribute (configurable; default matchesAltair\\Http\\Contracts\\MiddlewareInterface::ATTRIBUTE_ACTION). Passes through when no Action on request, when Action lacksidempotency(), or when the policy is malformed. Delegates to a freshIdempotencyKeyMiddlewareper request, parametrised by the spec's TTL + mode.Altair\\Idempotency\\Hash\\TtlParser\xe2\x80\x94 pure utility.'24h'/'500ms'/'7d'→ seconds. Rejects malformed strings withIdempotencyException. Sub-second TTLs round up to 1 second (storage TTLs are second-granular on the wire).Host wiring
Two lines. Register
IdempotencyConfiguration(bindsIdempotencyStoreInterface→InMemoryStoreby default); addActionAwareIdempotencyMiddlewareto the middleware pipeline afterDispatcherMiddlewareand beforeActionMiddleware:That's the full host wiring. Endpoints whose specs carry
idempotency:get the runtime policy; endpoints without the block pass through unchanged.The manual
IdempotencyKeyMiddlewarerecipe stays in the docs as an escape hatch for hosts that need to override the spec-driven policy (e.g. forcemode: requiredglobally).Composer note
No coupling added to
univeros/http. The attribute key is a constructor-injectable string defaulting to the valueuniveros/httpships, so the package stays portable.Tests (+17)
TtlParserTest(11) \xe2\x80\x94 ms/s/m/h/d units, ms rounding behaviour, malformed rejection, zero handling.ActionAwareIdempotencyMiddlewareTest(6) \xe2\x80\x94 pass-through paths (no attribute / no accessor / malformed policy), end-to-end replay viaInMemoryStore, required-mode-from-action 400 path, custom attribute name configuration.Test plan
composer cs\xe2\x80\x94 greencomposer stan\xe2\x80\x94 greencomposer rector(full tree, no cache) \xe2\x80\x94 greencomposer test\xe2\x80\x94 6309 tests (+17 new), 0 new failuresbin/altair manifest:generate\xe2\x80\x94 cleandocs/packages/idempotency.mdquick-start rewritten around the auto-wiring pattern