Microsoft.Extensions.Configuration integration#35
Merged
Conversation
Signed-off-by: Kenny Pflug <[email protected]>
… from Validator<TSource, TValidated> Signed-off-by: Kenny Pflug <[email protected]>
Signed-off-by: Kenny Pflug <[email protected]>
Signed-off-by: Kenny Pflug <[email protected]>
Signed-off-by: Kenny Pflug <[email protected]>
Signed-off-by: Kenny Pflug <[email protected]>
7 tasks
There was a problem hiding this comment.
Pull request overview
This PR adds first-class integration between Light.PortableResults.Validation and Microsoft.Extensions.Options so Validator<TOptions> can participate in the IValidateOptions<TOptions> pipeline when options are resolved from DI (including named options), alongside a version bump and related documentation/test updates.
Changes:
- Add an
IValidateOptions<TOptions>adapter (PortableResultsValidateOptions<TOptions>) + a fluentOptionsBuilder<TOptions>.ValidateWithPortableResults<TOptions, TValidator>()registration extension. - Refactor validator public APIs away from caller-expression string overloads and remove
TryValidatewrappers, updating tests accordingly. - Update docs/release notes/versioning (0.3.0) and extend
ErrorCategorywith HTTP 418.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Light.PortableResults.Validation.Tests/packages.lock.json | Updates locked dependencies for validation tests (including Options). |
| tests/Light.PortableResults.Validation.Tests/ValidatorOverloadCoverageTests.cs | Removes coverage tests for removed TryValidate / transforming wrappers. |
| tests/Light.PortableResults.Validation.Tests/ValidationConfigurationTests.cs | Updates tests to match new validator overload shapes. |
| tests/Light.PortableResults.Validation.Tests/ValidateWithPortableResultsTests.cs | Adds DI/Options pipeline tests for ValidateWithPortableResults. |
| tests/Light.PortableResults.Validation.Tests/SynchronousValidatorWorkflowTests.cs | Adjusts workflow tests to use Validate instead of removed wrappers/targets. |
| tests/Light.PortableResults.Validation.Tests/ConfigurationIntegration/PortableResultsValidateOptionsTests.cs | Adds unit tests for the adapter behavior (skip/success/fail/name forwarding). |
| tests/Light.PortableResults.Validation.Tests/AsyncValidatorWorkflowTests.cs | Updates async workflow tests to align with new overloads/targets. |
| tests/Light.PortableResults.Tests/ErrorCategoryTests.cs | Adds test coverage for the new HTTP 418 category. |
| src/Light.PortableResults/Light.PortableResults.csproj | Bumps package release notes to 0.3.0 and adjusts wording. |
| src/Light.PortableResults/ErrorCategory.cs | Expands docs for HTTP 410 and adds HTTP 418 ImATeapot. |
| src/Light.PortableResults.Validation/Validator.cs | Removes caller-expression string overloads and TryValidate; changes default target semantics. |
| src/Light.PortableResults.Validation/Module.cs | Makes DI registration idempotent and adds ValidateWithPortableResults extension. |
| src/Light.PortableResults.Validation/Light.PortableResults.Validation.csproj | Updates release notes and mentions options validation integration. |
| src/Light.PortableResults.Validation/ConfigurationIntegration/PortableResultsValidateOptions.cs | Introduces IValidateOptions<TOptions> adapter over Validator<TOptions>. |
| src/Light.PortableResults.Validation/ConfigurationIntegration/ConfigurationConstants.cs | Introduces a well-known ValidationContextKey<string?> for options name. |
| src/Light.PortableResults.Validation/AsyncValidator.cs | Aligns async validator overloads with new default target semantics (no caller-expression). |
| src/Light.PortableResults.AspNetCore.Shared/Light.PortableResults.AspNetCore.Shared.csproj | Updates release notes/version to 0.3.0 and punctuation. |
| src/Light.PortableResults.AspNetCore.Mvc/Light.PortableResults.AspNetCore.Mvc.csproj | Updates release notes/version to 0.3.0. |
| src/Light.PortableResults.AspNetCore.MinimalApis/Light.PortableResults.AspNetCore.MinimalApis.csproj | Updates release notes/version to 0.3.0. |
| ai-plans/0034-plan-deviations.md | Documents deviations between the implementation and plan 0034. |
| ai-plans/0034-configuration-validation.md | Adds the original plan document for configuration/options validation. |
| README.md | Adds documentation for options validation integration and bumps badge version. |
| Light.PortableResults.slnx | Adds the plan documents to the solution. |
| Directory.Build.props | Bumps repository version to 0.3.0. |
Signed-off-by: Kenny Pflug <[email protected]>
…imes Signed-off-by: Kenny Pflug <[email protected]>
…Attribute Signed-off-by: Kenny Pflug <[email protected]>
Signed-off-by: Kenny Pflug <[email protected]>
…iles Signed-off-by: Kenny Pflug <[email protected]>
Minimum allowed line rate is |
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 #34
Summary
The main feature from plan 0034 was implemented successfully:
Validator<TOptions>can participate in theIValidateOptions<TOptions>pipeline, named options are filtered correctly, DI registration is idempotent, and automated tests cover the core scenarios.The remaining differences are mostly about API organization and two behavioral details where the implementation chose a leaner shape than the original plan text described.
Deviations From The Original Plan
1. Configuration integration was grouped into a dedicated sub-namespace instead of staying in the root validation namespace
Original plan:
The plan described adding
PortableResultsValidateOptions<TOptions>"inside the existingLight.PortableResults.Validationproject and namespace" and suggested a dedicated static extension class such asOptionsBuilderExtensions.Implemented:
The configuration-related types were grouped under
Light.PortableResults.Validation.ConfigurationIntegration:PortableResultsValidateOptions<TOptions>ConfigurationConstantsThe
ValidateWithPortableResults<TOptions, TValidator>()extension method was added to the existingModuleclass in the root namespace instead of a separateOptionsBuilderExtensionsclass.Impact:
This is an organizational deviation, not a capability gap. The public API is still available, but the code is structured around a dedicated configuration-integration area rather than keeping every type directly in the root namespace.
2. Unnamed options are represented by an absent context item instead of storing
OptionsNameKeywith anullvalueOriginal plan:
The plan explicitly specified forwarding the incoming options name via:
context.SetItem(OptionsNameKey, name)This implies that validators could read the key for both named and unnamed options, with unnamed options represented by a stored
nullvalue.Implemented:
PortableResultsValidateOptions<TOptions>.Validate(...)only stores the item whennameis notnull:ConfigurationConstants.OptionsNameKeyis presentThis means validators must interpret unnamed options via
TryGetItem(...) == falserather than via a present key whose value isnull.Impact:
This is a real behavioral deviation from the plan wording. The current implementation still lets validators distinguish named options, but it does not preserve the exact "forward the incoming
nullname" semantics that the plan described.3. The adapter relies on a fresh root
ValidationContextinstead of explicitly passingValidationTarget.Absolute("")Original plan:
The bridge adapter was supposed to call:
_validator.CheckForErrors(options, context, out var failure, ValidationTarget.Absolute(""))The plan used this explicit target to guarantee clean property paths such as
ConnectionString.Implemented:
The adapter creates a fresh root context from
ValidationContextFactoryand then calls:_validator.CheckForErrors(options, context, out var failure)No explicit
ValidationTarget.Absolute("")is passed.Impact:
With the current
DefaultValidationContextFactory, this is functionally equivalent because a newly created rootValidationContextalready starts with an empty target prefix. As a result, property paths remain clean today.The deviation is architectural: the root-target behavior now depends on root-context semantics rather than being made explicit at the adapter call site.
4. The final API only supports
Validator<TOptions>, not transformingValidator<TSource, TValidated>Original plan:
The plan text is internally inconsistent:
Validator<T>andValidator<TSource, TValidated>should be supported whenTSource == TOptionsThe git history shows why this inconsistency surfaced in the final implementation. Shortly before the configuration-validation work landed, commit
acec552(chore!: remove TryValidate from all validators, remove CheckForErrors from Validator<TSource, TValidated>) simplified the validator surface and removed the non-generic failure wrapper from transforming validators. The actual configuration-validation feature then landed in commit3f73fae(feat: add support for IValidateOptions<T>).Implemented:
The implementation resolved this in favor of the narrower scope:
PortableResultsValidateOptions<TOptions>acceptsValidator<TOptions>onlyValidateWithPortableResults<TOptions, TValidator>()is constrained towhere TValidator : Validator<TOptions>Validator<TOptions, TValidated>Impact:
This is the most important functional deviation from the acceptance-criteria wording. The branch supports only non-transforming synchronous validators for options validation.
Given the later scope section in the original plan and the preceding API simplification in
acec552, this looks like an intentional narrowing rather than an accidental omission. In other words, the implementation did not merely skip a planned overload; it aligned the feature with the validator API that existed by the timeIValidateOptions<T>support was introduced.Notes On Items Implemented As Planned
The following parts of plan 0034 match the current implementation:
PortableResultsValidateOptions<TOptions>implementsIValidateOptions<TOptions>ValidateOptionsResult.Fail(IEnumerable<string>)ValidateOptionsResult.SkipAddValidationForPortableResults()uses idempotent registration patternsValidateWithPortableResults<TOptions, TValidator>()returns the originalOptionsBuilder<TOptions>for chaining