Skip to content

(wip) feat(optimizer): bound list-API responses with configurable limit#595

Draft
mkuchenbecker wants to merge 1 commit into
mkuchenb/optimizer-2from
mkuchenb/optimizer-2.1
Draft

(wip) feat(optimizer): bound list-API responses with configurable limit#595
mkuchenbecker wants to merge 1 commit into
mkuchenb/optimizer-2from
mkuchenb/optimizer-2.1

Conversation

@mkuchenbecker
Copy link
Copy Markdown
Collaborator

Status

(WIP) — inspection branch on top of mkuchenb/optimizer-2 (PR #531). Open for review of the diff shape before folding into #531.

Summary

All four list-style endpoints on the optimizer REST API now require a bounded limit. Default 10, max 1000, both configurable.

  • GET /v1/optimizer/operations
  • GET /v1/optimizer/stats
  • GET /v1/optimizer/operations-history/{tableUuid}
  • GET /v1/optimizer/stats/{tableUuid}/history

Changes

  • New ApiListLimitProperties (@ConfigurationProperties("optimizer.api.list")) — defaultLimit=10, maxLimit=1000, with a validateAndResolve(Integer) helper that throws ResponseStatusException(BAD_REQUEST) on out-of-range.
  • Controllers accept @RequestParam(required = false) Integer limit, call limits.validateAndResolve(limit) at method entry.
  • OptimizerDataServicelistTableOperations and listTableStats gain int limit (the two history methods already had it).
  • OptimizerDataServiceImpl — drops @Value("${optimizer.repo.default-limit:10000}"); threads the caller-supplied limit straight into PageRequest.of(0, limit), which Spring Data translates to MySQL LIMIT n.
  • application.propertiesoptimizer.repo.default-limit=10000 removed; replaced with optimizer.api.list.default-limit=10 + max-limit=1000.

Design notes

  • No pagination. Filters narrow the scope; the implicit len(result) == limit signal tells callers there may be more. Cursor-based pagination can land later if needed. Offset/limit pagination is deliberately avoided (deep-scan cost, skip/duplicate under concurrent writes).
  • Bounds are configurable, not constants. Tests vary the cap via @TestPropertySource to confirm the bound is config-driven, not hard-coded.
  • 400, not 500, on out-of-range. Uses ResponseStatusException(BAD_REQUEST) rather than IllegalArgumentException because Spring does not auto-map the latter to 400.

Testing Done

  • Added new tests for the changes made.
  • Updated existing tests to reflect the changes made.

./gradlew :services:optimizer:test — all green.

New ListLimitControllerTest (10 MockMvc cases): default applies, explicit limit honored, limit=0 → 400, limit > max → 400, across all four endpoints. @TestPropertySource sets default-limit=3, max-limit=50 to verify config-driven behavior.

New service-layer cases: listTableOperations_respectsLimit and listTableStats_respectsLimit confirm the PageRequest cap reaches the repo.

🤖 Generated with Claude Code

All four list-style endpoints now require a bounded limit.

- New ApiListLimitProperties carries default-limit (10) and max-limit (1000),
  both configurable via optimizer.api.list.*.
- Controllers accept Integer limit (nullable → default), validate via
  ResponseStatusException(BAD_REQUEST) on out-of-range.
- listTableOperations / listTableStats lose their service-level
  @value("${optimizer.repo.default-limit}") fallback and now take limit as a
  required arg. The repo Pageable already cascades limit to LIMIT n in SQL.
- New MockMvc ListLimitControllerTest covers default-applies, explicit limit,
  zero/over-max rejection, with @TestPropertySource confirming bounds are
  config-driven (not hard-coded).

No pagination — implicit len(result) == limit signals "more available." Cursor
pagination can land later if needed; offset/limit deliberately avoided.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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