chore: further optimizations to batch address validation - #290
Conversation
Signed-off-by: Alan Brault <alan.brault@visus.io>
There was a problem hiding this comment.
Pull request overview
This PR further optimizes the batch address-validation pipeline by extracting response-level custom data once per batch and reusing it across per-item mappings, reducing repeated work and keeping provider mappers consistent.
Changes:
- Extend
IBatchApiResponseMapper<TResponse>withGetSharedCustomResponseData(...)and pass shared data intoMap(...)for each item. - Optimize
AbstractBatchAddressValidationServiceby avoiding LINQ materializations when splitting valid requests/indexes. - Update FedEx mappers/tests plus docs and architecture diagrams to reflect the shared custom-response-data flow.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Visus.AddressValidation.Tests/Services/AbstractBatchAddressValidationServiceTests.cs | Adds a unit test asserting shared custom response data is computed once per batch; updates mapper stubs for new signature. |
| tests/Visus.AddressValidation.Tests/ApiTests.PublicApi_HasNoBreakingChanges_Async.verified.txt | Updates the public API snapshot for the mapper interface change and removed dictionary merge overload. |
| tests/Visus.AddressValidation.Integration.FedEx.Tests/BatchAddressValidationServiceTests.cs | Verifies shared response-level custom data (e.g., customerTransactionId) is present on multiple batch results. |
| src/Visus.AddressValidation/Services/AbstractBatchAddressValidationService.cs | Splits valid requests/indexes without LINQ and computes shared custom response data for mapping. |
| src/Visus.AddressValidation/Mappers/IBatchApiResponseMapper.cs | Adds GetSharedCustomResponseData and updates Map to accept shared custom data. |
| src/Visus.AddressValidation/Extensions/DictionaryExtensions.cs | Simplifies merge logic and removes the IDictionary overload (API change). |
| src/Visus.AddressValidation.Integration.FedEx/Mappers/ResolvedAddressResponseMapper.cs | Threads shared custom data into per-item custom-response-data construction. |
| src/Visus.AddressValidation.Integration.FedEx/Mappers/BatchAddressValidationResponseMapper.cs | Implements GetSharedCustomResponseData and uses shared data in mapping. |
| src/Visus.AddressValidation.Integration.FedEx/Mappers/AddressValidationResponseMapper.cs | Updates singular mapping helper call to supply response-level custom data to the shared mapper. |
| docs/docs/integrations/custom/batch-mappers.md | Documents the two-method batch response mapper pattern and shared custom data handling. |
| Directory.Packages.props | Trivial formatting change only. |
| ARCHITECTURE.md | Updates pipeline diagram and key points to include shared custom response data computed once per batch. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Signed-off-by: Alan Brault <alan.brault@visus.io>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
src/Visus.AddressValidation/Extensions/DictionaryExtensions.cs:28
- This change removes the IDictionary<TKey,TValue> overload of DictionaryExtensions.Merge from the public API (see the public API snapshot update). Even if it is unused internally, it can be a breaking change for external consumers that only have an IDictionary-typed input dictionary. If the library is intended to remain backward compatible, consider keeping the overload as a shim or call out the breaking change in release/versioning.
foreach ( KeyValuePair<TKey, TValue> kvp in dictionary )
{
if ( !source.ContainsKey(kvp.Key) )
{
source.Add(kvp.Key, kvp.Value);
src/Visus.AddressValidation/Mappers/IBatchApiResponseMapper.cs:24
- The XML docs for GetSharedCustomResponseData do not document two important contract details that the batch service relies on: it must return a non-null dictionary (return an empty dictionary when there is no shared data), and it may not be called if every item in the batch has validation errors (lazy first-use behavior). Clarifying this in the interface docs helps custom integration authors implement the mapper correctly without needing to read the service internals.
/// <remarks>
/// Call this once per batch, not once per item. <paramref name="response" /> is the same instance for
/// every item in the batch, so any response-level data it exposes is invariant across the whole call.
/// </remarks>
/// <param name="response">The batch API response from the provider.</param>
Signed-off-by: Alan Brault <alan.brault@visus.io>
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (3)
src/Visus.AddressValidation/Mappers/IBatchApiResponseMapper.cs:22
- The text does not follow ASD-STE100 sentence-length guidance (keep sentences short; avoid chaining multiple facts into one long sentence). Please split the second sentence into shorter sentences for readability and consistency with the documentation standard used in this repo.
/// <remarks>
/// Call this once per batch, not once per item. <paramref name="response" /> is the same instance for
/// every item in the batch, so any response-level data it exposes is invariant across the whole call.
/// The batch service does not call this method when every item in the batch has validation errors.
/// </remarks>
docs/docs/integrations/custom/batch-mappers.md:89
- This NOTE paragraph is a single long sentence chain. To follow ASD-STE100 (short sentences, one fact per sentence), split it into multiple shorter sentences/lines.
> `response` is the same instance for every item in a batch call, so any response-level data it exposes is invariant across the whole batch. The [batch validation service](xref:Visus.AddressValidation.Services.AbstractBatchAddressValidationService`2) calls `GetSharedCustomResponseData` exactly once per batch — not once per item — and passes the result into every `Map` call as `sharedCustomResponseData`. If the response type has no response-level custom data (see [Data Models](xref:custom-models)), return an empty dictionary.
ARCHITECTURE.md:203
- This bullet contains multiple long, compound sentences. It is hard to scan and does not follow ASD-STE100 guidance (short sentences; one fact per sentence). Consider splitting the explanation into several short sentences on wrapped lines under the same bullet.
- **Response-level custom data is computed at most once per batch, not once per item.** `IBatchApiResponseMapper<T>.GetSharedCustomResponseData(response)` is called lazily, the first time an item in the batch needs `Map(...)` (i.e. the first item without validation errors), and the memoized result is passed into every subsequent `Map(...)` call as `sharedCustomResponseData`. If every item in the batch has validation errors, `GetSharedCustomResponseData` is never called. This exists because `response` is the same `TApiResponse` instance for every item in the batch, so any response-level data it exposes (e.g. FedEx's `customerTransactionId`) is invariant across the whole call — recomputing it per item would repeat identical work up to `MaxBatchSize` times for no benefit, and computing it at all is wasted work when no item will use it. `AbstractBatchAddressValidationService` throws `InvalidImplementationException` if `GetSharedCustomResponseData` returns `null`, since the interface contract requires a non-null dictionary.



No description provided.