Skip to content

🔥 feat: Add All method to Bind #3373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

edvardsanta
Copy link
Contributor

@edvardsanta edvardsanta commented Mar 25, 2025

Description

This pull request introduces a new All method in the Bind struct to unify data binding from multiple sources (URL parameters, body, query, headers, and cookies) into a single struct. It also includes corresponding documentation updates, test cases, and a benchmark for the new method.

New All method in Bind struct:

  • Implementation of All method: Added a new All method in the Bind struct to bind data from multiple sources into a struct pointer. It processes the sources in a predefined precedence order and ensures that only unset fields are updated. Helper functions mergeStruct and isZero were added to support this functionality.

Testing and benchmarking:

  • Test cases for All method: Introduced comprehensive test cases for the All method, including scenarios for invalid output types, partial JSON, precedence handling, and different content types (e.g., JSON, form data). Added a RequestConfig helper struct to simplify test setup.
  • Benchmark for All method: Added a benchmark test to measure the performance of the All method under repeated binding operations.

Documentation updates:

  • API documentation for All method: Updated the API documentation to include the All method, detailing its usage, precedence order, and an example.

These changes enhance the flexibility and usability of the Bind struct by consolidating multiple data sources into a single binding operation while maintaining clear documentation and robust testing.

Fixes #3363

Changes introduced

List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide.

  • Benchmarks: Describe any performance benchmarks and improvements related to the changes.
  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • Changelog/What's New: Include a summary of the additions for the upcoming release notes.
  • Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes.
  • API Alignment with Express: Explain how the changes align with the Express API.
  • API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes.
  • Examples: Provide examples demonstrating the new features or changes in action.

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • Enhancement (improvement to existing features and functionality)
  • Documentation update (changes to documentation)
  • Code consistency (non-breaking change which improves code reliability and robustness)

Checklist

Before you submit your pull request, please make sure you meet these requirements:

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

Commit formatting

Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. Check out the emoji cheatsheet here: CONTRIBUTING.md

This commit introduces a new `All` method to the `Bind` struct, enabling the binding of request data from multiple sources (URI parameters, body, query parameters, headers, and cookies) into a single struct.

The `All` method iterates through the available binding sources, applying them in a predefined precedence order. It merges the values from each source into the output struct, only updating fields that are currently unset.

Changes:

- Added `All` method to `Bind` struct.
- Added `mergeStruct` helper function to merge struct values.
- Added `isZero` helper function to check if a value is zero.
- Added a test case for the `All` method in `bind_test.go` to validate its functionality.
Copy link
Contributor

coderabbitai bot commented Mar 25, 2025

Walkthrough

A new method, All, has been added to the Bind struct to enable binding data from multiple HTTP sources (URI parameters, body, query parameters, headers, and cookies) into a single struct, following a defined precedence. The implementation introduces helper functions for merging struct fields and checking zero values. Comprehensive tests and a benchmark for the new method have been added, including a utility for request configuration and coverage for field precedence and error handling.

Changes

File(s) Change Summary
bind.go Added Bind.All(out any) error method to bind from multiple sources with precedence; added helpers mergeStruct and isZero.
bind_test.go Added RequestConfig struct and ApplyTo method; new tests for Bind.All covering precedence, error handling, and partial data; added benchmark for Bind.All.
docs/api/bind.md Added documentation for Bind.All method including usage example and precedence rules.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Bind
    participant URI
    participant Body
    participant Query
    participant Header
    participant Cookie

    Client->>Bind: All(out)
    Bind->>URI: Bind URI params
    Bind->>Body: Bind body
    Bind->>Query: Bind query params
    Bind->>Header: Bind headers
    Bind->>Cookie: Bind cookies
    Bind-->>Client: Populated struct (with precedence)
Loading

Possibly related issues

Suggested labels

🧹 Updates

Suggested reviewers

  • ReneWerner87
  • gaby

Poem

In fields where structs and values play,
The binder hops in, merging away.
From URI, body, query, and more,
It gathers data—never a bore!
With tests and benchmarks, swift and neat,
This rabbit’s work is quite the feat.
🐇✨

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@edvardsanta edvardsanta changed the title feat: Add All method to Bind 🔥 feat: Add All method to Bind Mar 25, 2025
@ReneWerner87 ReneWerner87 added this to v3 Mar 25, 2025
@ReneWerner87 ReneWerner87 added this to the v3 milestone Mar 25, 2025
@edvardsanta
Copy link
Contributor Author

@ReneWerner87 please let me know if you have any feedback, suggestions, or improvements for the code. I’ll make any necessary adjustments and work on improving the tests when I have some time.

bind_test.go Outdated
@@ -1885,3 +1885,52 @@ func Test_Bind_RepeatParserWithSameStruct(t *testing.T) {
testDecodeParser(MIMEApplicationForm, "body_param=body_param")
testDecodeParser(MIMEMultipartForm+`;boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"body_param\"\r\n\r\nbody_param\r\n--b--")
}

func TestBind_All(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you create some benchmarks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure

@gaby gaby moved this to In Progress in v3 Mar 26, 2025
The changes include:

- Added `RequestConfig` struct to encapsulate request configuration (ContentType, Body, Headers, Cookies, Query).
- Implemented `ApplyTo` method on `RequestConfig` to apply the configuration to the context.
- Created multiple test cases for `Bind.All` covering successful binding, missing fields, overriding query parameters, and form binding.
- Added a test case `Test_Bind_All_Uri_Precedence` to validate the precedence of URI parameters.
- Added benchmark test `BenchmarkBind_All` to measure the performance of the `Bind.All` method.
- Refactored the `TestBind_All` to use the new `RequestConfig` and assertion libraries.
@edvardsanta edvardsanta marked this pull request as ready for review April 21, 2025 03:24
@edvardsanta edvardsanta requested a review from a team as a code owner April 21, 2025 03:24
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
bind.go (2)

299-309: Remove the initial blank line inside the for block to satisfy revive / empty-lines lint rule

golangci‑lint fails with extra empty line at the start of a block.
A one‑line diff fixes it:

 for _, bindFunc := range sources {
-
+	// attempt bind from the current source
 	if err := bindFunc(tempStruct); err != nil {
 		return err
 	}
🧰 Tools
🪛 GitHub Check: lint

[failure] 301-301:
empty-lines: extra empty line at the start of a block (revive)

🪛 GitHub Actions: golangci-lint

[error] 301-301: golangci-lint (revive): extra empty line at the start of a block (empty-lines)


314-327: mergeStruct copies only top‑level zero fields – nested structs remain partially empty

Because the function checks isZero on the destination field as a whole,
a nested struct such as Address{City, Zip} will never receive the City
coming from a later source once any field inside Address has a
non‑zero value.
If deep merging is important, consider recursing into nested structs or
requiring pointer fields.

Not blocking, but worth documenting in godoc to avoid surprises.

bind_test.go (2)

1919-2041: Run each table‑driven sub‑test in parallel and use require for fatal assertions

  1. Add t.Parallel() inside the sub‑test to comply with the tparallel
    lint suggestion and speed up the suite.
  2. Replace assert.NoError with require.NoError so the test aborts
    immediately on failure, keeping subsequent checks safe.
 		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
 			bind := newBind(app)
@@
-			err := bind.All(tt.out)
-			if tt.wantErr {
-				assert.Error(t, err)
+			err := bind.All(tt.out)
+			if tt.wantErr {
+				require.Error(t, err)
 				return
 			}
-			assert.NoError(t, err)
+			require.NoError(t, err)
🧰 Tools
🪛 GitHub Check: lint

[failure] 2027-2027:
require-error: for error assertions use require (testifylint)


[failure] 1949-1949:
fieldalignment: struct with 48 pointer bytes could be 40 (govet)


[failure] 1921-1921:
fieldalignment: struct with 72 pointer bytes could be 64 (govet)


[failure] 1919-1919:
Test_Bind_All's subtests should call t.Parallel (tparallel)


1891-1917: RequestConfig.ApplyTo leaves previous request state untouched – add a reset

Because the same ctx is reused, fields that are not overridden by the
current RequestConfig (query string, cookies, headers, …) survive from
earlier test cases, leading to flakiness (see wrong ID above).

Insert a reset at the top of ApplyTo:

 func (rc *RequestConfig) ApplyTo(ctx Ctx) {
+	ctx.Request().Reset()
 	if rc.Body != nil {

(This doesn’t affect production code – it’s only a test helper.)

🧰 Tools
🪛 GitHub Check: lint

[failure] 1891-1891:
fieldalignment: struct with 64 pointer bytes could be 56 (govet)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 85cce3c and 32dd82b.

📒 Files selected for processing (2)
  • bind.go (2 hunks)
  • bind_test.go (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
bind_test.go (2)
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-07-26T21:00:12.902Z
Learning: The project uses the testify/assert package for assertions in unit tests.
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The project uses the testify/assert package for assertions in unit tests.
🪛 GitHub Check: lint
bind.go

[failure] 301-301:
empty-lines: extra empty line at the start of a block (revive)

bind_test.go

[failure] 2076-2076:
fieldalignment: struct with 72 pointer bytes could be 64 (govet)


[failure] 2046-2046:
fieldalignment: struct with 32 pointer bytes could be 24 (govet)


[failure] 2027-2027:
require-error: for error assertions use require (testifylint)


[failure] 1949-1949:
fieldalignment: struct with 48 pointer bytes could be 40 (govet)


[failure] 1921-1921:
fieldalignment: struct with 72 pointer bytes could be 64 (govet)


[failure] 1919-1919:
Test_Bind_All's subtests should call t.Parallel (tparallel)


[failure] 1891-1891:
fieldalignment: struct with 64 pointer bytes could be 56 (govet)

🪛 GitHub Actions: golangci-lint
bind.go

[error] 301-301: golangci-lint (revive): extra empty line at the start of a block (empty-lines)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Compare
  • GitHub Check: repeated
  • GitHub Check: Analyse

bind.go Outdated
Comment on lines 279 to 312
func (b *Bind) All(out any) error {
outVal := reflect.ValueOf(out)
if outVal.Kind() != reflect.Ptr || outVal.Elem().Kind() != reflect.Struct {
return ErrUnprocessableEntity
}

outElem := outVal.Elem()

// Precedence: URL Params -> Body -> Query -> Headers -> Cookies
sources := []func(any) error{
b.URI,
b.Body,
b.Query,
b.Header,
b.Cookie,
}

tempStruct := reflect.New(outElem.Type()).Interface()

// TODO: Support custom precedence with an optional binding_source tag
// TODO: Create WithOverrideEmptyValues
// Bind from each source, but only update unset fields
for _, bindFunc := range sources {

if err := bindFunc(tempStruct); err != nil {
return err
}

tempStructVal := reflect.ValueOf(tempStruct).Elem()
mergeStruct(outElem, tempStructVal)
}

return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Handle “no body” and similar benign errors so All doesn’t abort early

b.Body() (and the other single‑source helpers) can legitimately return
ErrUnprocessableEntity, ErrEmptyBody, etc. when the incoming request
simply doesn’t contain data for that source.
All should treat those as non‑fatal and continue with the next source;
otherwise a perfectly valid request that, say, only has query params will
fail the whole bind.

-		if err := bindFunc(tempStruct); err != nil {
-			return err
-		}
+		if err := bindFunc(tempStruct); err != nil {
+			// Skip “nothing to bind”‑style errors and continue.
+			if errors.Is(err, ErrUnprocessableEntity) ||
+				errors.Is(err, binder.ErrEmptyBody) {
+				continue
+			}
+			return err
+		}

(You may need to import errors and, if available, the concrete “empty body”
error from the binder package.)

🧰 Tools
🪛 GitHub Check: lint

[failure] 301-301:
empty-lines: extra empty line at the start of a block (revive)

🪛 GitHub Actions: golangci-lint

[error] 301-301: golangci-lint (revive): extra empty line at the start of a block (empty-lines)

Copy link

codecov bot commented Apr 21, 2025

Codecov Report

Attention: Patch coverage is 92.85714% with 3 lines in your changes missing coverage. Please review.

Project coverage is 84.03%. Comparing base (85cce3c) to head (87a8dea).

Files with missing lines Patch % Lines
bind.go 92.85% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3373      +/-   ##
==========================================
+ Coverage   83.87%   84.03%   +0.16%     
==========================================
  Files         119      119              
  Lines       11892    11934      +42     
==========================================
+ Hits         9974    10029      +55     
+ Misses       1488     1475      -13     
  Partials      430      430              
Flag Coverage Δ
unittests 84.03% <92.85%> (+0.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
bind_test.go (1)

2008-2008: ⚠️ Potential issue

Test expectation for Form binding needs correction.

When testing Form binding, with id=2 in the body, the expected ID should be 2, not 1. This appears to be a carryover from the previous test that had different data.

-				ID:    1,
+				ID:    2,

This also highlights a potential issue with request context reuse between tests. It would be clearer to explicitly reset or create a new context for each test case.

🧹 Nitpick comments (4)
bind_test.go (4)

1891-1897: Well-designed RequestConfig struct for test configuration.

This utility struct provides a clean way to configure request parameters for tests. The design is comprehensive, covering all the request components needed for the binding tests.

However, the struct could be optimized for memory efficiency:

type RequestConfig struct {
-	ContentType string
-	Body        []byte
-	Headers     map[string]string
-	Cookies     map[string]string
-	Query       string
+	Body        []byte
+	Headers     map[string]string
+	Cookies     map[string]string
+	ContentType string
+	Query       string
}

The static analysis indicates field alignment issues - reordering fields from largest to smallest can improve memory efficiency.

🧰 Tools
🪛 GitHub Check: lint

[failure] 1891-1891:
fieldalignment: struct with 64 pointer bytes could be 56 (govet)


2074-2112: Good benchmark implementation for the All method.

The benchmark properly measures the performance of the All method with realistic data and all binding sources. This addresses the previous request for benchmarks.

Similar to the User struct in the tests, this one also has field alignment issues:

type User struct {
-	ID        int                   `param:"id" query:"id" json:"id" form:"id"`
-	Avatar    *multipart.FileHeader `form:"avatar"`
-	Name      string                `query:"name" json:"name" form:"name"`
-	Email     string                `json:"email" form:"email"`
-	Role      string                `header:"x-user-role"`
-	SessionID string                `json:"session_id" cookie:"session_id"`
+	Avatar    *multipart.FileHeader `form:"avatar"`
+	ID        int                   `param:"id" query:"id" json:"id" form:"id"`
+	Name      string                `query:"name" json:"name" form:"name"`
+	Email     string                `json:"email" form:"email"`
+	Role      string                `header:"x-user-role"`
+	SessionID string                `json:"session_id" cookie:"session_id"`
}
🧰 Tools
🪛 GitHub Check: lint

[failure] 2076-2076:
fieldalignment: struct with 72 pointer bytes could be 64 (govet)


1921-1928: User struct efficiently captures binding from multiple sources.

The struct correctly uses tags for different binding sources (param, query, json, form, header, cookie) which demonstrates the capability of the new All method.

The field ordering could be optimized for memory efficiency (pointer fields first):

type User struct {
-	ID        int                   `param:"id" query:"id" json:"id" form:"id"`
-	Avatar    *multipart.FileHeader `form:"avatar"`
-	Name      string                `query:"name" json:"name" form:"name"`
-	Email     string                `json:"email" form:"email"`
-	Role      string                `header:"x-user-role"`
-	SessionID string                `json:"session_id" cookie:"session_id"`
+	Avatar    *multipart.FileHeader `form:"avatar"`
+	ID        int                   `param:"id" query:"id" json:"id" form:"id"`
+	Name      string                `query:"name" json:"name" form:"name"`
+	Email     string                `json:"email" form:"email"`
+	Role      string                `header:"x-user-role"`
+	SessionID string                `json:"session_id" cookie:"session_id"`
}
🧰 Tools
🪛 GitHub Check: lint

[failure] 1921-1921:
fieldalignment: struct with 72 pointer bytes could be 64 (govet)


2046-2050: User struct in URI precedence test is appropriately designed.

The struct correctly includes tags for different binding sources to test precedence rules.

Similar to the other structs, field alignment could be optimized:

type User struct {
-	ID    int    `param:"id" json:"id" query:"id" form:"id"`
-	Name  string `json:"name"`
-	Email string `json:"email"`
+	ID    int    `param:"id" json:"id" query:"id" form:"id"`
+	Name  string `json:"name"`
+	Email string `json:"email"`
}
🧰 Tools
🪛 GitHub Check: lint

[failure] 2046-2046:
fieldalignment: struct with 32 pointer bytes could be 24 (govet)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32dd82b and 4799c50.

📒 Files selected for processing (1)
  • bind_test.go (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
bind_test.go (2)
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-07-26T21:00:12.902Z
Learning: The project uses the testify/assert package for assertions in unit tests.
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The project uses the testify/assert package for assertions in unit tests.
🪛 GitHub Check: lint
bind_test.go

[failure] 2076-2076:
fieldalignment: struct with 72 pointer bytes could be 64 (govet)


[failure] 2046-2046:
fieldalignment: struct with 32 pointer bytes could be 24 (govet)


[failure] 2027-2027:
require-error: for error assertions use require (testifylint)


[failure] 1949-1949:
fieldalignment: struct with 48 pointer bytes could be 40 (govet)


[failure] 1921-1921:
fieldalignment: struct with 72 pointer bytes could be 64 (govet)


[failure] 1919-1919:
Test_Bind_All's subtests should call t.Parallel (tparallel)


[failure] 1891-1891:
fieldalignment: struct with 64 pointer bytes could be 56 (govet)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: unit (1.24.x, windows-latest)
  • GitHub Check: unit (1.23.x, windows-latest)
  • GitHub Check: repeated
  • GitHub Check: Compare
🔇 Additional comments (5)
bind_test.go (5)

13-13: Good addition of the required import.

Adding the strings package is necessary for the new test functionality, particularly for the request body creation in Test_Bind_All_Uri_Precedence.


19-19: Appropriate use of testify/assert package.

Using the assert package from testify aligns with the project's testing conventions as indicated in the retrieved learnings.


1899-1916: Clean implementation of ApplyTo method.

The method correctly applies all configuration properties to the request context in a logical order. The null checks prevent errors when optional fields aren't provided.


2043-2072: Good test for URI parameter precedence.

This test properly verifies that URI parameters take precedence over query parameters and JSON body when binding data, which is an important behavior of the new All method.

The test correctly sets up the scenario:

  1. URI parameter: id=111
  2. Query parameter: id=888
  3. JSON body: {"id": 999, ...}

And it verifies that the URI parameter value (111) is chosen.

🧰 Tools
🪛 GitHub Check: lint

[failure] 2046-2046:
fieldalignment: struct with 32 pointer bytes could be 24 (govet)


1946-1946: Test query parameter setup could be improved.

In the defaultConfig function, the query ID duplicates the ID from the JSON body.

Since the test is verifying binding behavior, consider making the IDs different to better validate the precedence rules:

-			Query: "id=1&name=john",
+			Query: "id=2&name=john",

This would make it clearer which source is being used when checking the bound value.

bind_test.go Outdated
assert.Error(t, err)
return
}
assert.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use require for error assertions instead of assert.

The codebase consistently uses require for error assertions to stop test execution on failure.

-			assert.Error(t, err)
+			require.Error(t, err)

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Check: lint

[failure] 2027-2027:
require-error: for error assertions use require (testifylint)

- Reordered fields in `RequestConfig` and `User` structs for field alignment
- Updated `Test_Bind_All` to use `require.NoError` for more robust error checking.
- Corrected header key casing in `Test_Bind_All` to `X-User-Role` to match the struct tag.
- Added `t.Parallel()` to the test function to enable parallel test execution.
"testing"
"time"

"github.com/fxamacker/cbor/v2"
"github.com/gofiber/fiber/v3/binder"
"github.com/stretchr/testify/assert"
Copy link
Member

Choose a reason for hiding this comment

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

Use testify.require() instead of assert()

@@ -273,3 +275,57 @@ func (b *Bind) Body(out any) error {
// No suitable content type found
return ErrUnprocessableEntity
}

func (b *Bind) All(out any) error {
Copy link
Member

Choose a reason for hiding this comment

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

Add function to docs

@gaby gaby requested a review from Copilot April 21, 2025 03:45
Copy link

@Copilot 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 an All method for the Bind struct to enable the merging of request data from multiple sources (URI, body, query, headers, and cookies), following a defined precedence order. Key changes include adding a new All method and mergeStruct helper in bind.go and comprehensive unit tests in bind_test.go covering various binding scenarios.

Reviewed Changes

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

File Description
bind_test.go Adds tests for the new All method with various binding cases.
bind.go Implements the All method and mergeStruct helper to combine data from multiple sources.
Comments suppressed due to low confidence (1)

bind_test.go:1945

  • [nitpick] Consider adding test cases where fields with valid zero values (such as numeric fields intentionally set to 0) are bound, to verify that the merge logic handles them correctly.
Query: "id=1&name=john",

b.Cookie,
}

tempStruct := reflect.New(outElem.Type()).Interface()
Copy link
Preview

Copilot AI Apr 21, 2025

Choose a reason for hiding this comment

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

The temporary struct (tempStruct) is created once outside the loop and then reused for each binding source. This may cause previous binding values to carry over into subsequent bindings. Consider reinitializing tempStruct inside the loop for each source to ensure isolated binding.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The purpose of tempStruct is to act as a temporary holder for each source binding (such as URI, Body, Query, etc.) and merge it into the final outElem struct.

srcField := src.Field(i)

// Skip if the destination field is already set
if isZero(dstField.Interface()) {
Copy link
Preview

Copilot AI Apr 21, 2025

Choose a reason for hiding this comment

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

Using isZero to decide whether to update a field might unintentionally overwrite valid zero values (e.g., 0 for an int). Reevaluate the logic for what constitutes an 'unset' field, possibly by using pointer types or an explicit flag.

Suggested change
if isZero(dstField.Interface()) {
if (dstField.Kind() == reflect.Ptr && dstField.IsNil()) || (dstField.Kind() != reflect.Ptr && isZero(dstField.Interface())) {

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

This commit adds documentation for the `Bind.All` function to the API documentation.

The documentation includes:

- A description of the function's purpose and precedence order for binding data from different sources (URI, body, query, headers, cookies).
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
docs/api/bind.md (2)

446-451: Clarify merging semantics
The description and precedence list explain the order of sources but don’t explicitly state that All only sets zero‑valued fields and preserves values from higher‑precedence binders. Consider adding a note, for example:

“By design, All will not override non‑zero fields set by earlier binders; it only populates fields that remain at their zero value.”


462-462: Fix code fence formatting
The example block is opened with go (with a space). For consistency, it should be go (no space) to correctly highlight as Go code.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6ff948 and 87a8dea.

📒 Files selected for processing (1)
  • docs/api/bind.md (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: unit (1.24.x, windows-latest)
  • GitHub Check: repeated
  • GitHub Check: unit (1.23.x, windows-latest)
  • GitHub Check: Compare
🔇 Additional comments (1)
docs/api/bind.md (1)

28-28: Binder list updated to include All
The new All binder is now listed under the Binders section. Placing it after the URI binder aligns with its broad, catch‑all scope.

Comment on lines +464 to +469
Name string `query:"name" json:"name" form:"name"`
Email string `json:"email" form:"email"`
Role string `header:"X-User-Role"`
SessionID string `json:"session_id" cookie:"session_id"`
ID int `param:"id" query:"id" json:"id" form:"id"`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct struct tag for URI parameters
The example struct uses param:"id", but the URI binder expects the uri tag (as shown in earlier sections). Update it to uri:"id" so that All will bind path parameters correctly.

@edvardsanta edvardsanta marked this pull request as draft April 21, 2025 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

Feature Proposal: Universal Request Binding with ctx.Bind().All()
4 participants