Skip to content

feat: add batch endpoint for working days#641

Closed
OneLastChanc3 wants to merge 3 commits into
bobadilla-tech:mainfrom
OneLastChanc3:working-days_batch
Closed

feat: add batch endpoint for working days#641
OneLastChanc3 wants to merge 3 commits into
bobadilla-tech:mainfrom
OneLastChanc3:working-days_batch

Conversation

@OneLastChanc3

@OneLastChanc3 OneLastChanc3 commented May 18, 2026

Copy link
Copy Markdown
Contributor
  • Add POST /v1/places/working-days/batch endpoint
  • Minimum 1 item, maximum 50 items per request
  • Add testing for service and transport_http
  • Add documentation

Summary by CodeRabbit

Release Notes

  • New Features
    • Added batch API endpoint for working days calculations, allowing up to 50 date ranges to be processed in a single request while preserving input order. Each item supports optional country and subdivision parameters for holiday-aware calculations. Results include working days count and echoed date ranges.

Review Change Stack

@OneLastChanc3
OneLastChanc3 requested a review from a team as a code owner May 18, 2026 07:56
@OneLastChanc3
OneLastChanc3 requested a review from crydafan May 18, 2026 07:56
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@OneLastChanc3, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 35 minutes and 8 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 400d6a30-1610-4147-88bf-2e0c9e436f0e

📥 Commits

Reviewing files that changed from the base of the PR and between bc72df2 and d19733b.

📒 Files selected for processing (7)
  • apps/api/services/places/working-days/service.go
  • apps/api/services/places/working-days/service_test.go
  • apps/api/services/places/working-days/transport_http.go
  • apps/api/services/places/working-days/transport_http_test.go
  • apps/dashboard/config/api_catalog.yml
  • apps/dashboard/config/api_docs/working-days.yml
  • docs/apis/places/working-days.md
📝 Walkthrough

Walkthrough

This PR adds a batch endpoint POST /v1/places/working-days/batch to the working-days service. It introduces Date JSON serialization for date strings, batch request/response types, a service method that processes multiple date ranges, an HTTP handler with validation, comprehensive tests, and API documentation.

Changes

Batch Working Days Calculation API

Layer / File(s) Summary
Batch request/response types and JSON marshaling
apps/api/services/places/working-days/transport_http.go
Date type with custom UnmarshalJSON (YYYY-MM-DD and RFC3339) and MarshalJSON (YYYY-MM-DD); BatchItem, BatchRequest (1–50 items), and BatchResponse structures.
Service batch implementation and unit tests
apps/api/services/places/working-days/service.go, service_test.go
GetWorkingDaysBatch loops through requests, calls GetWorkingDays per item, formats dates as YYYY-MM-DD, and returns WorkingDays slice. Tests cover multi-item batches, empty input, and single-item cases with field and count assertions.
HTTP batch endpoint and transport tests
apps/api/services/places/working-days/transport_http.go, transport_http_test.go
POST /working-days/batch route uses httpx.HandleBatch to decode BatchRequest, map items to internal Requests, call GetWorkingDaysBatch, and return BatchResponse. Tests validate happy path with date formatting, JSON parsing errors, empty/missing/oversized items returning 400/422, and 50-item boundary.
API catalog and documentation
apps/dashboard/config/api_catalog.yml, apps/dashboard/config/api_docs/working-days.yml, docs/apis/places/working-days.md
Catalog endpoints_count incremented to 2; OpenAPI spec documents batch endpoint request schema, response schema with ordered results, error variants, and example requests in multiple languages.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • crydafan
  • UltiRequiem

Poem

🐰 A batch of dates now hops in one request,
No more looping—batch API is best!
From dates to working days they fly so neat,
Marshaling JSON, validation complete.
Thump thump! 📅✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: add batch endpoint for working days' accurately reflects the main change: adding a batch endpoint to the working days API.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov

codecov Bot commented May 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.57143% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...api/services/places/working-days/transport_http.go 70.00% 7 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/apis/places/working-days.md (1)

8-8: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Status line is now stale after adding POST batch endpoint.

Line 8 still states availability only at GET /v1/places/working-days, but this PR also introduces POST /v1/places/working-days/batch.

🤖 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 `@docs/apis/places/working-days.md` at line 8, Update the status line to
reflect both endpoints by mentioning that the feature is live at GET
/v1/places/working-days and also available as a batch endpoint POST
/v1/places/working-days/batch; edit the status text (the line currently reading
"✅ **Live** - Available now at `GET /v1/places/working-days`") to include the
POST path and/or rephrase to "Available now at GET /v1/places/working-days and
POST /v1/places/working-days/batch" so the documentation accurately lists both
endpoints.
🤖 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 `@apps/api/services/places/working-days/transport_http.go`:
- Around line 43-55: The batch handler currently only checks item count and then
calls svc.GetWorkingDaysBatch; add per-item validation of each req.Items element
(check presence/format of from and to, enforce to >= from, and validate
country/subdivision combinations) before calling svc.GetWorkingDaysBatch. Reuse
the same validation logic used by the single-item endpoint (or extract it into a
helper like validateWorkingDayItem) and return httpx.Error(w,
http.StatusBadRequest, "invalid_request", ...) for the first invalid item
(include item index in the message) so invalid entries never reach
svc.GetWorkingDaysBatch; keep the final call to svc.GetWorkingDaysBatch and
httpx.JSON unchanged once all items are valid.
- Around line 38-41: The batch endpoint fails to accept documented "YYYY-MM-DD"
dates because json.Decoder uses time.Time's RFC3339-only unmarshaler; fix by
adding a custom date type with UnmarshalJSON that accepts both "2006-01-02" and
RFC3339 (or parses fallback layouts) and use that type for the Request's
items[].from and items[].to fields (or implement UnmarshalJSON on the Request
struct to parse those fields manually), then keep the same
json.NewDecoder(r.Body).Decode(&req) call so clients can send "YYYY-MM-DD" as
documented.

---

Outside diff comments:
In `@docs/apis/places/working-days.md`:
- Line 8: Update the status line to reflect both endpoints by mentioning that
the feature is live at GET /v1/places/working-days and also available as a batch
endpoint POST /v1/places/working-days/batch; edit the status text (the line
currently reading "✅ **Live** - Available now at `GET /v1/places/working-days`")
to include the POST path and/or rephrase to "Available now at GET
/v1/places/working-days and POST /v1/places/working-days/batch" so the
documentation accurately lists both endpoints.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0809aa5-9efa-47a8-8957-491828e7f665

📥 Commits

Reviewing files that changed from the base of the PR and between ffac5a6 and 97d8c2f.

📒 Files selected for processing (8)
  • apps/api/services/places/working-days/service.go
  • apps/api/services/places/working-days/service_test.go
  • apps/api/services/places/working-days/transport_http.go
  • apps/api/services/places/working-days/transport_http_test.go
  • apps/api/services/places/working-days/type.go
  • apps/dashboard/config/api_catalog.yml
  • apps/dashboard/config/api_docs/working-days.yml
  • docs/apis/places/working-days.md

Comment thread apps/api/services/places/working-days/transport_http.go Outdated
Comment thread apps/api/services/places/working-days/transport_http.go Outdated

httpx.JSON(w, http.StatusOK, response)
})
r.Post("/working-days/batch", func(w http.ResponseWriter, r *http.Request) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If I remember correctly we have a new function in the httpx library called HandleBatch exactly for this

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah pal, use HandleBatch

@UltiRequiem UltiRequiem left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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


httpx.JSON(w, http.StatusOK, response)
})
r.Post("/working-days/batch", func(w http.ResponseWriter, r *http.Request) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah pal, use HandleBatch

Comment thread apps/api/services/places/working-days/transport_http.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
apps/api/services/places/working-days/type.go (1)

57-57: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add to >= from validation for batch items.

Line 57 is missing gtfield=From, so batch accepts inverted ranges unlike the single-item contract and docs.

Suggested fix
 type BatchItem struct {
 	From        Date   `json:"from" validate:"required"`
-	To          Date   `json:"to" validate:"required"`
+	To          Date   `json:"to" validate:"required,gtfield=From"`
 	Country     string `json:"country" validate:"omitempty,iso3166_1_alpha2"`
 	Subdivision string `json:"subdivision" validate:"omitempty,iso3166_2,required_with=Country"`
 }
🤖 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 `@apps/api/services/places/working-days/type.go` at line 57, The batch-item
struct's To field (declared as To Date) lacks a cross-field validator, so
inverted ranges slip through; update the struct tag on To to include the
cross-field rule (e.g., validate:"required,gtefield=From") so To is validated as
greater-than-or-equal-to From (reference the To Date field and the From field
name when making the change).
🧹 Nitpick comments (1)
apps/api/services/places/working-days/transport_http_test.go (1)

103-219: ⚡ Quick win

Add a regression test for to < from in batch items.

Current cases cover count/malformed-body bounds, but not invalid date ordering. Add one case expecting validation failure (422) when an item has to before from.

🤖 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 `@apps/api/services/places/working-days/transport_http_test.go` around lines
103 - 219, Add a regression test function (e.g.,
TestWorkingDaysBatch_ToBeforeFrom) that constructs a BatchRequest with one
BatchItem whose To date is earlier than its From date (use Date{Time:
mustParseDate(...)} and mustMarshal for the body), POSTs it to
"/working-days/batch" via newBatchRouter() and asserts the response code is
http.StatusUnprocessableEntity (422); reuse the existing test setup patterns
(httptest.NewRequest, req.Header.Set("Content-Type","application/json"),
httptest.NewRecorder, and newBatchRouter().ServeHTTP) and mirror the other
tests' style and parallelization (t.Parallel()).
🤖 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.

Duplicate comments:
In `@apps/api/services/places/working-days/type.go`:
- Line 57: The batch-item struct's To field (declared as To Date) lacks a
cross-field validator, so inverted ranges slip through; update the struct tag on
To to include the cross-field rule (e.g., validate:"required,gtefield=From") so
To is validated as greater-than-or-equal-to From (reference the To Date field
and the From field name when making the change).

---

Nitpick comments:
In `@apps/api/services/places/working-days/transport_http_test.go`:
- Around line 103-219: Add a regression test function (e.g.,
TestWorkingDaysBatch_ToBeforeFrom) that constructs a BatchRequest with one
BatchItem whose To date is earlier than its From date (use Date{Time:
mustParseDate(...)} and mustMarshal for the body), POSTs it to
"/working-days/batch" via newBatchRouter() and asserts the response code is
http.StatusUnprocessableEntity (422); reuse the existing test setup patterns
(httptest.NewRequest, req.Header.Set("Content-Type","application/json"),
httptest.NewRecorder, and newBatchRouter().ServeHTTP) and mirror the other
tests' style and parallelization (t.Parallel()).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b2d9a1d6-bd80-44d0-a843-cda49dc5240e

📥 Commits

Reviewing files that changed from the base of the PR and between 97d8c2f and d1cb840.

📒 Files selected for processing (5)
  • apps/api/services/places/working-days/transport_http.go
  • apps/api/services/places/working-days/transport_http_test.go
  • apps/api/services/places/working-days/type.go
  • apps/dashboard/config/api_docs/working-days.yml
  • docs/apis/places/working-days.md
✅ Files skipped from review due to trivial changes (1)
  • apps/dashboard/config/api_docs/working-days.yml

@UltiRequiem

Copy link
Copy Markdown
Member

@copilot resolve the merge conflicts in this pull request

1 similar comment
@UltiRequiem

Copy link
Copy Markdown
Member

@copilot resolve the merge conflicts in this pull request

@UltiRequiem UltiRequiem left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

httpx.JSON(w, http.StatusOK, response)
})

r.Post("/working-days/batch", httpx.HandleBatch(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh you still got a type.go file....

Subdivision string `json:"subdivision,omitempty"`
}

func (WorkingDays) IsData() {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We dont support IsData anymore....

businessdayscalculator "github.com/bobadilla-tech/business-days-calculator"
)

// WorkingDays represents the response for working days calculation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/api/services/places/working-days/transport_http.go (1)

60-63: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add to >= from validation for each batch item.

BatchItem currently validates presence/format but not ordering. On Line 61, To should enforce gtefield=From (or equivalent) to match the documented contract and prevent invalid ranges from reaching service logic.

Suggested fix
 type BatchItem struct {
 	From        Date   `json:"from" validate:"required"`
-	To          Date   `json:"to" validate:"required"`
+	To          Date   `json:"to" validate:"required,gtefield=From"`
 	Country     string `json:"country" validate:"omitempty,iso3166_1_alpha2"`
 	Subdivision string `json:"subdivision" validate:"omitempty,iso3166_2,required_with=Country"`
 }
🤖 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 `@apps/api/services/places/working-days/transport_http.go` around lines 60 -
63, The BatchItem struct's date range isn't validated for ordering: add a
validation rule to the To field so it enforces To >= From (e.g., add
`validate:"required,gtefield=From"` or append `gtefield=From` to the existing
tag on `To`) in the BatchItem definition in transport_http.go so each batch item
fails validation when the end date precedes the start date; ensure the tag uses
the same validator framework used elsewhere for `Date` fields.
🤖 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 `@apps/api/services/places/working-days/transport_http.go`:
- Around line 3-11: Run goimports on the file to fix the import formatting
issue: reformat the import block in transport_http.go so imports are grouped and
ordered correctly (standard libs first, then third-party, then local packages)
and remove any unnecessary blank lines; target the import block that contains
symbols like "context", "fmt", "github.com/go-chi/chi/v5", "strings", "time",
and "requiems-api/platform/httpx" so the linter warning on the import formatting
(around the current import list) is resolved.
- Line 85: The transport maps decode failures for the batch endpoint to the
wrong error code; update the HandleBatch error mapping used in the
r.Post("/working-days/batch", httpx.HandleBatch(...)) call so that
decode/malformed-JSON errors return "invalid_request" (instead of
"bad_request"), or alternatively update the batch docs/tests to expect
"bad_request" to keep the contract consistent; modify the argument or options
passed into httpx.HandleBatch to produce the "invalid_request" error code for
JSON decode failures, ensuring any relevant error-to-code mapping logic in
httpx.HandleBatch is updated accordingly.

---

Duplicate comments:
In `@apps/api/services/places/working-days/transport_http.go`:
- Around line 60-63: The BatchItem struct's date range isn't validated for
ordering: add a validation rule to the To field so it enforces To >= From (e.g.,
add `validate:"required,gtefield=From"` or append `gtefield=From` to the
existing tag on `To`) in the BatchItem definition in transport_http.go so each
batch item fails validation when the end date precedes the start date; ensure
the tag uses the same validator framework used elsewhere for `Date` fields.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b39cfe0d-b530-46a4-b56b-ce297689eca3

📥 Commits

Reviewing files that changed from the base of the PR and between d1cb840 and bc72df2.

📒 Files selected for processing (4)
  • apps/api/services/places/working-days/service.go
  • apps/api/services/places/working-days/transport_http.go
  • apps/api/services/places/working-days/transport_http_test.go
  • apps/dashboard/config/api_catalog.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/dashboard/config/api_catalog.yml
  • apps/api/services/places/working-days/service.go

Comment thread apps/api/services/places/working-days/transport_http.go
}, nil
}))

r.Post("/working-days/batch", httpx.HandleBatch(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Malformed-JSON error code appears inconsistent with the endpoint docs.

At Line 85, httpx.HandleBatch maps decode failures to bad_request, while the batch docs specify invalid_request for malformed JSON. Please align either transport behavior or docs/tests so clients get a stable error contract.

🤖 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 `@apps/api/services/places/working-days/transport_http.go` at line 85, The
transport maps decode failures for the batch endpoint to the wrong error code;
update the HandleBatch error mapping used in the r.Post("/working-days/batch",
httpx.HandleBatch(...)) call so that decode/malformed-JSON errors return
"invalid_request" (instead of "bad_request"), or alternatively update the batch
docs/tests to expect "bad_request" to keep the contract consistent; modify the
argument or options passed into httpx.HandleBatch to produce the
"invalid_request" error code for JSON decode failures, ensuring any relevant
error-to-code mapping logic in httpx.HandleBatch is updated accordingly.

UltiRequiem added a commit that referenced this pull request May 31, 2026
Batch working-days calculation for up to 50 date ranges. Pure in-memory
computation uses sequential loop per batch-apis.md.

Fixes from review:
- Remove unused BatchResponse type from transport_http.go
- Remove GetWorkingDaysBatch from service (took HTTP-binding Request type);
  inline loop directly in the transport handler
- Date custom type supports both YYYY-MM-DD and RFC3339 input formats

Co-Authored-By: OneLastChanc3 <OneLastChanc3@users.noreply.github.com>
UltiRequiem added a commit that referenced this pull request May 31, 2026
Co-Authored-By: OneLastChanc3 <OneLastChanc3@users.noreply.github.com>
@UltiRequiem

Copy link
Copy Markdown
Member
image Closed because it landed directly on main! 🚀

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.

3 participants