Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 3, 2026

  • Fix closure bug in SetPreRequestHook (reference s.preRequestHook field)
  • Fix documentation table of contents structure
  • Add tests for multiple SetPreRequestHook calls
  • Remove SetBatchSubRequestHandler logic completely
  • Remove related tests and documentation
  • Update CHANGELOG

Changes Made

Bug Fix

  • Fixed closure that captured hook parameter instead of s.preRequestHook field, so subsequent calls to SetPreRequestHook now correctly update the behavior for batch requests

Breaking Change

  • Removed SetBatchSubRequestHandler method entirely
  • Removed batchSubRequestHandler field from Service struct
  • Removed SetSubRequestHandler method from BatchHandler
  • Removed tests that used SetBatchSubRequestHandler
  • Removed comparison documentation with SetBatchSubRequestHandler

Tests Added

  • TestPreRequestHook_MultipleHookUpdates - verifies hook updates work for single requests
  • TestPreRequestHook_MultipleHookUpdates_BatchChangeset - verifies hook updates work for batch changesets
Original prompt

This section details on the original issue you should resolve

<issue_title>Explore pre-request hook or improved handling to remove manual subrequest logic</issue_title>
<issue_description>### Problem Statement
Implementation of #369 for $batch request parity currently requires library users to do manual work to ensure that $batch subrequests are handled exactly like single requests. This is burdensome and can fail for certain request types, especially where database atomicity is required.

In another project using go-odata, an authentication middleware loads the user object from the JWT and adds it to the HTTP context. This is then used later in OData hooks for authorization. However, this pattern is also difficult to reproduce reliably for $batch, and it highlights the need for a better extensibility mechanism.

Proposal

  • Explore approaches to treat $batch subrequests with the exact same lifecycle and context as single requests, without requiring manual duplication of work for implementers.
  • Investigate the possibility of introducing a pre-request hook (or similar mechanism) in go-odata that would:
    • Provide full request context for each (sub-)request
    • Allow common tasks (e. g., authentication, setting up context objects) to be handled uniformly for single and $batch subrequests
    • Be reusable for scenarios like authentication, authorization, logging, and metadata injection
  • Consider alternative solutions if a hook is not the right pattern
  • Evaluate database atomicity requirements for bulk operations in and how they interact with pre-request logic

Context

  • Current designs make it easy to do authentication/context-loading for single requests via middleware, but $batch subrequests do not benefit from this automatically
  • A uniform mechanism would help reduce mistakes or missed logic across request types

References


If a new hook or mechanism is designed, ensure it is fully tested with both single and $batch requests, and document how implementers should migrate.</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits January 3, 2026 23:16
Introduces a service-level hook that is called before each request is processed,
including batch sub-requests. This provides a unified mechanism for authentication,
context enrichment, logging, and other preprocessing tasks.

Key features:
- Works uniformly for single requests and batch sub-requests (including changesets)
- Hook can return a modified context to pass values to downstream handlers
- Hook can return an error to abort the request with HTTP 403 Forbidden
- Eliminates the need for manual SetBatchSubRequestHandler configuration

Closes #370

Co-authored-by: NLstn <[email protected]>
- Fixed trailing whitespace in test file
- Added clarifying comment about hook wrapper capture

Co-authored-by: NLstn <[email protected]>
Copilot AI changed the title [WIP] Explore pre-request hook for improved batch request handling Add PreRequestHook for unified request preprocessing Jan 3, 2026
Copilot AI requested a review from NLstn January 3, 2026 23:23
@NLstn NLstn marked this pull request as ready for review January 3, 2026 23:23
Copilot AI review requested due to automatic review settings January 3, 2026 23:23
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a PreRequestHook feature that provides a unified mechanism for request preprocessing across all request types, including batch sub-requests and changesets. This addresses the issue where batch sub-requests bypass external middleware, requiring users to manually configure SetBatchSubRequestHandler for authentication and context enrichment.

Key changes:

  • Added PreRequestHook type and SetPreRequestHook method for service-level request preprocessing
  • Integrated hook execution in http_runtime.go for all requests and in batch.go for changeset sub-requests
  • Added comprehensive test coverage with 7 test cases covering single requests, batch operations, changesets, and edge cases

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
odata.go Defines PreRequestHook type and SetPreRequestHook method with comprehensive documentation
http_runtime.go Calls the pre-request hook before processing each request, with proper error handling
internal/handlers/batch.go Integrates hook for changeset sub-requests with context propagation
test/pre_request_hook_test.go Comprehensive test suite with 7 tests covering various scenarios
documentation/advanced-features.md Documents the feature with examples and comparison to SetBatchSubRequestHandler
CHANGELOG.md Documents the new feature in the Added section

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

odata.go Outdated
Comment on lines 846 to 854
// Wrap the hook for the batch handler. The wrapper is created once and captures
// the hook variable, so subsequent calls to SetPreRequestHook will update the
// service's preRequestHook but the batch handler will use its own copy.
// To update the batch handler's hook, call SetPreRequestHook again.
s.batchHandler.SetPreRequestHook(func(r *http.Request) (context.Context, error) {
if hook == nil {
return nil, nil
}
return hook(r)
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

The closure captures the hook parameter value at the time of the call, not the s.preRequestHook field. This means if SetPreRequestHook is called multiple times, the batch handler will keep using the hook from the first call, not subsequent ones.

The comment on lines 846-849 acknowledges this issue but doesn't fix it. To fix this, the closure should reference s.preRequestHook instead of the hook parameter, like this:

s.batchHandler.SetPreRequestHook(func(r *http.Request) (context.Context, error) {
    if s.preRequestHook == nil {
        return nil, nil
    }
    return s.preRequestHook(r)
})

This way, the batch handler always calls the current hook value.

Suggested change
// Wrap the hook for the batch handler. The wrapper is created once and captures
// the hook variable, so subsequent calls to SetPreRequestHook will update the
// service's preRequestHook but the batch handler will use its own copy.
// To update the batch handler's hook, call SetPreRequestHook again.
s.batchHandler.SetPreRequestHook(func(r *http.Request) (context.Context, error) {
if hook == nil {
return nil, nil
}
return hook(r)
// Wrap the hook for the batch handler. The wrapper reads s.preRequestHook on each
// call so that subsequent calls to SetPreRequestHook are honored for batch requests.
s.batchHandler.SetPreRequestHook(func(r *http.Request) (context.Context, error) {
if s.preRequestHook == nil {
return nil, nil
}
return s.preRequestHook(r)

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 080360a. The closure now references s.preRequestHook instead of the hook parameter, so subsequent calls to SetPreRequestHook are honored for batch requests.

Comment on lines 11 to 15
- [Pre-Request Hook](#pre-request-hook)
- [Change Tracking and Delta Tokens](#change-tracking-and-delta-tokens)
- [Read Hooks](#read-hooks)
- [Tenant Filtering Example](#tenant-filtering-example)
- [Redacting Sensitive Data](#redacting-sensitive-data)
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

The table of contents structure is incorrect. The items "Read Hooks", "Tenant Filtering Example", and "Redacting Sensitive Data" (lines 13-15) are indented as sub-items of "Change Tracking and Delta Tokens", but they should be sub-items of "Lifecycle Hooks" based on the typical structure of this documentation.

This creates a confusing navigation structure where lifecycle hook examples appear under the wrong parent section.

Suggested change
- [Pre-Request Hook](#pre-request-hook)
- [Change Tracking and Delta Tokens](#change-tracking-and-delta-tokens)
- [Read Hooks](#read-hooks)
- [Tenant Filtering Example](#tenant-filtering-example)
- [Redacting Sensitive Data](#redacting-sensitive-data)
- [Pre-Request Hook](#pre-request-hook)
- [Read Hooks](#read-hooks)
- [Tenant Filtering Example](#tenant-filtering-example)
- [Redacting Sensitive Data](#redacting-sensitive-data)
- [Change Tracking and Delta Tokens](#change-tracking-and-delta-tokens)

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 080360a. The TOC now correctly shows Read Hooks, Tenant Filtering Example, and Redacting Sensitive Data as sub-items under the appropriate section.

if hookCallCount < 3 {
t.Errorf("Expected hook to be called at least 3 times, got %d", hookCallCount)
}
}
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

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

Missing test coverage for calling SetPreRequestHook multiple times. The current tests only call SetPreRequestHook once per test. There should be a test that:

  1. Calls SetPreRequestHook with a first hook
  2. Makes a batch request to verify the first hook is called
  3. Calls SetPreRequestHook with a different hook
  4. Makes another batch request to verify the second hook is now being called

This would help catch the bug in lines 850-855 of odata.go where the closure captures the parameter instead of the field.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added tests in 080360a: TestPreRequestHook_MultipleHookUpdates for single requests and TestPreRequestHook_MultipleHookUpdates_BatchChangeset for batch changesets.

@NLstn
Copy link
Owner

NLstn commented Jan 4, 2026

@copilot remove the SetBatchSubRequestHandler logic without caring about backwards compatibility

- Fix closure bug: reference s.preRequestHook instead of captured hook parameter
- Fix documentation TOC structure
- Add tests for calling SetPreRequestHook multiple times
- Remove SetBatchSubRequestHandler entirely (breaking change)
- Remove batchSubRequestHandler field from Service struct
- Remove SetSubRequestHandler from BatchHandler
- Update CHANGELOG with removed feature

Co-authored-by: NLstn <[email protected]>
Copy link
Contributor Author

Copilot AI commented Jan 4, 2026

@copilot remove the SetBatchSubRequestHandler logic without caring about backwards compatibility

Done in 080360a. Removed SetBatchSubRequestHandler, batchSubRequestHandler field, and SetSubRequestHandler from BatchHandler entirely, along with related tests and documentation.

@NLstn NLstn merged commit f6af58d into main Jan 4, 2026
10 checks passed
@NLstn NLstn deleted the copilot/explore-batch-request-handling branch January 4, 2026 10:54
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.

Explore pre-request hook or improved handling to remove manual subrequest logic

2 participants