Skip to content

Enhance external resolver system - #7

Open
simplicbe wants to merge 3 commits into
masterfrom
f_ext_resolver
Open

Enhance external resolver system#7
simplicbe wants to merge 3 commits into
masterfrom
f_ext_resolver

Conversation

@simplicbe

@simplicbe simplicbe commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added cross-service resolution for enriching query results from external OxQL services.
    • Added configurable service endpoints, HTTPS enforcement, service-count limits, and dependency-injection setup.
    • Added resolver support for nested result fields, including batching and per-query caching.
    • Added examples for resolving customer addresses and invoice contacts.
  • Bug Fixes

    • Improved handling of external service failures by logging errors and returning empty results.
  • Documentation

    • Added setup guidance and request examples for cross-service resolution.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The change adds dynamic cross-service resolvers and integrates them with Mongo query execution. Resolve stages batch external keys, cache results per query, populate nested BSON fields, and support dependency-injection registration. Documentation adds configuration and request examples.

Changes

External resolver support

Layer / File(s) Summary
Dynamic cross-service resolver setup
src/docs/cross-service-resolver.md, README.md
Service mappings are parsed from ?services=. The factory validates service limits and HTTP usage, creates resolvers, issues OxQL requests, and documents endpoint wiring and examples.
Mongo resolve execution and result enrichment
src/OxQL.Mongo/MongoQueryAdapter.cs
Aggregation execution is injectable. Resolve stages validate resolvers, batch distinct keys, cache found and missing values, and write converted results into nested BSON paths.
Resolver wiring and cache validation
src/OxQL.Mongo/MongoQueryExecutor.cs, src/OxQL.Mongo/ServiceCollectionExtensions.cs, src/OxQL.Tests/Fakes/FakeExternalResolver.cs, src/OxQL.Tests/Mongo/MongoQueryAdapterResolveTests.cs
Mongo constructors and service registration pass external resolvers to the adapter. Tests track resolver calls and verify cache reuse across resolve stages.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant QueryEndpoint
  participant MongoQueryAdapter
  participant ExternalServiceResolver
  participant DownstreamOxQLService
  Client->>QueryEndpoint: Submit query with resolve stage
  QueryEndpoint->>MongoQueryAdapter: Execute query
  MongoQueryAdapter->>ExternalServiceResolver: Resolve batched keys
  ExternalServiceResolver->>DownstreamOxQLService: POST OxQL key query
  DownstreamOxQLService-->>ExternalServiceResolver: Return matching items
  ExternalServiceResolver-->>MongoQueryAdapter: Return resolved values
  MongoQueryAdapter-->>QueryEndpoint: Return enriched documents
Loading

Possibly related PRs

Poem

A rabbit hops through keys in flight,
Caches each answer, wrong or right.
Mongo fields bloom deep inside,
Cross-service paths now safely glide.
“Resolve!” I cheer, and twitch my nose.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the pull request's main change: enhanced external resolver support across Mongo execution, caching, documentation, and cross-service integration.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch f_ext_resolver

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/docs/cross-service-resolver.md`:
- Around line 83-86: Update the resolver query construction around inList to
serialize each key as a properly escaped OxQL string literal before joining the
values. Use a trusted string serializer or query builder so quotes and escape
characters cannot alter the query grammar; keep URL encoding applied to the
completed query.
- Around line 256-261: The cross-service resolver example must pass
request-specific resolvers into query execution instead of only storing them
locally. In src/docs/cross-service-resolver.md lines 256-261, add or use a
request-scoped execution path that accepts resolvers before
queryService.ExecuteAsync; update README.md line 415 to document that request
only after the execution path is implemented.
- Around line 186-193: Update the service-target handling around the serviceMap
loop and HttpClient.BaseAddress assignment to enforce server-side admission
before creating the client: resolve each source through a server-managed
registry or validate it against a configured allowlist, reject private,
loopback, link-local, and metadata endpoints, and ensure redirects cannot escape
the approved targets. Do not rely solely on the existing HTTP-versus-HTTPS
check.
- Around line 93-94: Update the response deserialization in the cross-service
resolver to use the query response contract returned by the endpoint, rather
than deserializing a top-level List<JsonElement>. Access and enumerate the
contract’s Items collection for result documents, preserving the existing
cancellation token and downstream item-processing flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b61fd2e0-4ee8-4d0b-a670-5e98837c0e9c

📥 Commits

Reviewing files that changed from the base of the PR and between 5a73f4d and 9f4ba42.

📒 Files selected for processing (8)
  • README.md
  • src/OxQL.Mongo/MongoQueryAdapter.cs
  • src/OxQL.Mongo/MongoQueryExecutor.cs
  • src/OxQL.Mongo/ServiceCollectionExtensions.cs
  • src/OxQL.Tests/Fakes/FakeExternalResolver.cs
  • src/OxQL.Tests/Mongo/MongoQueryAdapterResolveTests.cs
  • src/README.md
  • src/docs/cross-service-resolver.md

Comment on lines +83 to +86
// Build: match id in ["key1","key2"]
var inList = string.Join(", ", keys.Select(k => $"\"{k}\""));
var query = $"match id in [{inList}]";
var url = $"/oxql/query?q={HttpUtility.UrlEncode(query)}";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Escape each resolver key before building the OxQL query.

Line 84 inserts raw keys inside quoted OxQL literals. A key containing quotes or escape characters can change the remote query. URL encoding at Line 86 does not protect the OxQL grammar.

Serialize each key as a string literal with a trusted serializer or query builder before joining the list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/docs/cross-service-resolver.md` around lines 83 - 86, Update the resolver
query construction around inList to serialize each key as a properly escaped
OxQL string literal before joining the values. Use a trusted string serializer
or query builder so quotes and escape characters cannot alter the query grammar;
keep URL encoding applied to the completed query.

Comment on lines +93 to +94
var items = await response.Content
.ReadFromJsonAsync<List<JsonElement>>(cancellationToken: cancellationToken) ?? [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Deserialize the query response envelope.

The endpoint at Lines 249-264 returns Results.Ok(result). The Mongo execution path returns a QueryResponse object with result documents in Items. ReadFromJsonAsync<List<JsonElement>> expects a top-level array, so a normal remote OxQL response will throw JsonException.

Deserialize the response contract and enumerate its Items collection.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/docs/cross-service-resolver.md` around lines 93 - 94, Update the response
deserialization in the cross-service resolver to use the query response contract
returned by the endpoint, rather than deserializing a top-level
List<JsonElement>. Access and enumerate the contract’s Items collection for
result documents, preserving the existing cancellation token and downstream
item-processing flow.

Comment on lines +186 to +193
foreach (var (source, baseAddress) in serviceMap)
{
if (!_options.AllowHttp && baseAddress.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException(
$"Plain HTTP is not allowed for '{source}'. Use HTTPS.");

var client = _httpClientFactory.CreateClient($"oxql-external-{source}");
client.BaseAddress = baseAddress;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Apply a server-side target admission policy.

services is caller-controlled, and this factory assigns each supplied URI to HttpClient.BaseAddress. HTTPS does not prevent requests to internal HTTPS services, loopback endpoints, or cloud metadata endpoints. This creates an SSRF path.

Resolve service names through a server-managed registry, or enforce an allowlist and block private, loopback, link-local, and redirect targets.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/docs/cross-service-resolver.md` around lines 186 - 193, Update the
service-target handling around the serviceMap loop and HttpClient.BaseAddress
assignment to enforce server-side admission before creating the client: resolve
each source through a server-managed registry or validate it against a
configured allowlist, reject private, loopback, link-local, and metadata
endpoints, and ensure redirects cannot escape the approved targets. Do not rely
solely on the existing HTTP-versus-HTTPS check.

Comment on lines +256 to +261
var serviceMap = ServiceRegistryParser.Parse(services);
var resolvers = factory.CreateResolvers(serviceMap);

// Register resolvers into your execution context, then run the query
var request = BuildQueryRequest(q);
var result = await queryService.ExecuteAsync(request, ct);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Integrate request-specific resolvers with query execution.

DynamicResolverFactory.CreateResolvers returns resolvers only into a local variable. The Mongo adapter receives its resolver set during DI construction, so it cannot use these request-specific resolvers.

  • src/docs/cross-service-resolver.md#L256-L261: create a request-scoped execution path that receives resolvers before queryService.ExecuteAsync.
  • README.md#L415-L415: document the request only after that execution path is implemented.
📍 Affects 2 files
  • src/docs/cross-service-resolver.md#L256-L261 (this comment)
  • README.md#L415-L415
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/docs/cross-service-resolver.md` around lines 256 - 261, The cross-service
resolver example must pass request-specific resolvers into query execution
instead of only storing them locally. In src/docs/cross-service-resolver.md
lines 256-261, add or use a request-scoped execution path that accepts resolvers
before queryService.ExecuteAsync; update README.md line 415 to document that
request only after the execution path is implemented.

@simplicbe simplicbe changed the title F ext resolver Enhance external resolver system Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant