Repository navigation
feat: add inclusive date ranges to habit logs - #822
Merged
Merged
Conversation
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Reviewed the complete PR diff, the binding and validation pipeline, persistence filters, and the sibling client's route and response schema.
- Optional date ranges: Adds strict ISO
from/tobinding and validates paired bounds, ordering, and the 366-date inclusive limit. - Historical reads: Adds an inclusive SQL-filtered range read without the legacy row cap, retaining ownership checks, soft-delete filtering, and newest-first ordering.
- Existing clients: Preserves the default user-local lookback, 1,000-row cap, and response fields; both OpenAPI documents add only optional parameters.
- Verification: All 57 focused handler, validator, controller, MVC-binding, and persistence tests passed. Query-plan coverage uses SQLite; PostgreSQL execution plans were not independently verified.
gpt-6.1-sol | 𝕏
|
thomasluizon
added a commit
that referenced
this pull request
Oct 20, 2026
… and bulk reparenting (#746) (#827) * fix: expose persisted habit recurrence origins (#820) (cherry picked from commit 6e4a3a5) * feat: support bounded habit log date ranges (#822) (cherry picked from commit 906f9e4) * Add bulk habit reparenting (#821) * refactor: share habit hierarchy validation helpers * feat: move selected habits under a parent atomically * fix: register bulk parent action in the capability catalog (cherry picked from commit f67708f)
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 thomasluizon/orbit-tickets#387
GET /api/habits/{id}/logsaccepts optional ISOfromandtodates. Supplying both returns every visible log in that inclusive range, including history older than a year and ranges containing more than 1,000 rows. Omitting both preserves the existing user-local 365-day lookback, 1,000-row cap, ordering, and response fields.Implementation
HabitsController.csforwards the dates.ModelBinding/IsoDateQueryModelBinder.csstrictly binds ISO calendar dates and routes malformed values through the existingValidationExceptionHandlerenvelope.GetHabitLogsQuery.csand its FluentValidation validator select and validate the explicit range.IHabitLogReader.csandHabitLogReader.csadd a separate SQL-filtered range read so the legacy recent-read implementation stays intact. Authorization remains ahead of either read. This is a read operation with no domain entity mutation.Assumptions
ReadRangeLogsAsyncrather than broaden the existing method, preserving the legacy query shape and callers.Test evidence
Before changing behavior, the existing tests passed with the defect present:
Results: 7 handler tests and 19 reader tests passed. The reader run had only the optional query fields declared; the handler and reader behavior remained unchanged.
Added
HabitLogReaderTests.Handle_ExplicitPastRange_ReturnsOnlyInclusiveRangeWithoutLegacyCap, composing the actual handler, repository, and reader over EF InMemory. Before fixing the implementation:dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~Handle_ExplicitPastRange_ReturnsOnlyInclusiveRangeWithoutLegacyCap --verbosity quietResult: exit 1, one failure. January 2025 returned an empty collection instead of the expected 1,002 rows with user-today in July 2026. The same command passed after the fix. The strengthened regression also excludes both adjacent dates, another habit's rows, and a soft-deleted row.
Post-commit verification:
dotnet build Orbit.slnx --verbosity quiet: exit 0, zero errors.dotnet test tests/Orbit.Application.Tests --no-build --filter FullyQualifiedName~GetHabitLogsQuery --verbosity quiet: 19 passed.dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter 'FullyQualifiedName~HabitLogReaderTests|FullyQualifiedName~HabitLogRangeContractTests|FullyQualifiedName~HabitsControllerTests.GetLogs' --verbosity quiet: 38 passed.dotnet test tests/Orbit.Domain.Tests --no-build --verbosity quiet: 767 passed.dotnet test tests/Orbit.Application.Tests --no-build --verbosity quiet: 5,236 passed.dotnet test tests/Orbit.Infrastructure.Tests --no-build --verbosity quiet: 3,788 passed.dotnet test tests/Orbit.Analyzers.Tests --no-build --verbosity quiet: 40 passed.oasdiffv1.11.7 breaking comparison against the committedorigin/mainv1 document with--fail-on ERR: exit 0, no breaking changes.node tools/arch-map.mjs: exit 0. Pre-commit formatting, dash, timeless, root allowlist, and suppression allowlist checks passed.Query plan
HabitLogReaderTests.ReadRangeLogsAsync_UsesExistingDateIndexexecutes the actual range read over the shared SQLite in-memory context and runsEXPLAIN QUERY PLANon the emitted command with its actual parameters. Reproduce with:The emitted query applies
HabitId = @habitId AND Date >= @from AND Date <= @to, the existing soft-delete filter, andORDER BY Date DESC, CreatedAtUtc DESC. Its complete returned plan rows were:The existing index serves the range filter; the secondary timestamp ordering uses a temporary B-tree. No index or migration was added. This is SQLite evidence, not a PostgreSQL production plan. A PostgreSQL
EXPLAINwas not collected under the work order's prohibition on starting servers.External interface evidence
HabitLogRangeContractTestsuses the installed ASP.NET MVC action descriptors, real model-binder factory,ParameterBinder, andQueryStringValueProvider, rather than mocked binding results. Realfrom=2025-01-01&to=2025-01-31values bind to the matchingDateOnlyvalues, omitted dates bind to null, and malformed or empty values reach the existing validation envelope. The passing tests exercise the binder'sValueProviderResult.LengthandFirstValuereads against the actual provider. The query-plan test copies the actual EF command parameters and enumerates every returned column without assuming named plan fields; the complete returned rows are shown above.