From 6ef7964017101ce7ee028ca3c93e66f023856d76 Mon Sep 17 00:00:00 2001 From: mkuchenbecker Date: Fri, 22 May 2026 13:55:14 -0700 Subject: [PATCH] docs(optimizer): add @ApiResponses to controllers for OpenAPI spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review on PR #531 — each endpoint lists the HTTP response codes it actually returns, in the same "Resource ACTION: STATUS" style used by services/tables. Codes per endpoint: - POST /operations/{id}/update — 201, 400, 404 - GET /operations/{id} — 200, 404 - GET /operations — 200, 400 - POST /operations-history — 201 - GET /operations-history/{u} — 200, 400 - PUT /stats/{u} — 200 - GET /stats/{u} — 200, 404 - GET /stats — 200, 400 - GET /stats/{u}/history — 200, 400 Annotations only — no runtime behavior change, no new tests required. swagger-annotations 2.1.11 is already on the optimizer classpath via openhouse.springboot-conventions. Co-Authored-By: Claude Opus 4.7 --- .../controller/TableOperationsController.java | 18 ++++++++++++++++++ .../TableOperationsHistoryController.java | 11 +++++++++++ .../api/controller/TableStatsController.java | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsController.java b/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsController.java index 2f6f62e4b..2ee40802f 100644 --- a/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsController.java +++ b/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsController.java @@ -6,6 +6,8 @@ import com.linkedin.openhouse.optimizer.api.spec.TableOperationsHistory; import com.linkedin.openhouse.optimizer.api.spec.UpdateOperationRequest; import com.linkedin.openhouse.optimizer.service.OptimizerDataService; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -37,6 +39,12 @@ public class TableOperationsController { * operation row, writes a history entry with the operation's table metadata, and returns 201 * Created with the history row, or 404 if the operation does not exist. */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "201", description = "Operation UPDATE: CREATED"), + @ApiResponse(responseCode = "400", description = "Operation UPDATE: BAD_REQUEST"), + @ApiResponse(responseCode = "404", description = "Operation UPDATE: NOT_FOUND") + }) @PostMapping("/{id}/update") public ResponseEntity updateOperation( @PathVariable String id, @RequestBody UpdateOperationRequest request) { @@ -66,6 +74,11 @@ public ResponseEntity updateOperation( } /** Fetch a single operation row by its ID, regardless of status. Returns 404 if not found. */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "200", description = "Operation GET: OK"), + @ApiResponse(responseCode = "404", description = "Operation GET: NOT_FOUND") + }) @GetMapping("/{id}") public ResponseEntity getTableOperation(@PathVariable String id) { return service @@ -82,6 +95,11 @@ public ResponseEntity getTableOperation(@PathVariable String id * List operations matching the given filters, capped at {@code limit} rows. Every filter is * optional; {@code limit} is required so callers always state how much they want back. */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "200", description = "Operation SEARCH: OK"), + @ApiResponse(responseCode = "400", description = "Operation SEARCH: BAD_REQUEST") + }) @GetMapping public ResponseEntity> listTableOperations( @RequestParam(required = false) OperationType operationType, diff --git a/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsHistoryController.java b/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsHistoryController.java index 9a1b6d303..873d51d2e 100644 --- a/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsHistoryController.java +++ b/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableOperationsHistoryController.java @@ -2,6 +2,8 @@ import com.linkedin.openhouse.optimizer.api.spec.TableOperationsHistory; import com.linkedin.openhouse.optimizer.service.OptimizerDataService; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -24,6 +26,10 @@ public class TableOperationsHistoryController { private final OptimizerDataService service; /** Append a completed-job result. Called by the SparkJob after each run (success or failure). */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "201", description = "OperationsHistory CREATE: CREATED") + }) @PostMapping public ResponseEntity appendHistory( @RequestBody TableOperationsHistory dto) { @@ -35,6 +41,11 @@ public ResponseEntity appendHistory( * Return the most recent history for a table, newest first, capped at {@code limit} rows. {@code * limit} is required. */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "200", description = "OperationsHistory GET: OK"), + @ApiResponse(responseCode = "400", description = "OperationsHistory GET: BAD_REQUEST") + }) @GetMapping("/{tableUuid}") public ResponseEntity> getHistory( @PathVariable String tableUuid, @RequestParam int limit) { diff --git a/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableStatsController.java b/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableStatsController.java index ca8db4d51..b119dd1c7 100644 --- a/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableStatsController.java +++ b/services/optimizer/src/main/java/com/linkedin/openhouse/optimizer/api/controller/TableStatsController.java @@ -4,6 +4,8 @@ import com.linkedin.openhouse.optimizer.api.spec.TableStatsHistory; import com.linkedin.openhouse.optimizer.api.spec.UpsertTableStatsRequest; import com.linkedin.openhouse.optimizer.service.OptimizerDataService; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import java.time.Instant; import java.util.List; import java.util.Optional; @@ -32,6 +34,7 @@ public class TableStatsController { * Create or overwrite the stats row for {@code tableUuid}. Called by the Tables Service on every * Iceberg commit. Idempotent. */ + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Stats PUT: OK")}) @PutMapping("/{tableUuid}") public ResponseEntity upsertTableStats( @PathVariable String tableUuid, @RequestBody UpsertTableStatsRequest request) { @@ -40,6 +43,11 @@ public ResponseEntity upsertTableStats( } /** Fetch the stats row for {@code tableUuid}. Returns 404 if no stats have been written yet. */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "200", description = "Stats GET: OK"), + @ApiResponse(responseCode = "404", description = "Stats GET: NOT_FOUND") + }) @GetMapping("/{tableUuid}") public ResponseEntity getTableStats(@PathVariable String tableUuid) { return service @@ -56,6 +64,11 @@ public ResponseEntity getTableStats(@PathVariable String tableUuid) * List stats rows matching the given filters, capped at {@code limit} rows. Every filter is * optional; {@code limit} is required so callers always state how much they want back. */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "200", description = "Stats SEARCH: OK"), + @ApiResponse(responseCode = "400", description = "Stats SEARCH: BAD_REQUEST") + }) @GetMapping public ResponseEntity> listTableStats( @RequestParam(required = false) String databaseName, @@ -79,6 +92,11 @@ public ResponseEntity> listTableStats( * Return per-commit stats history for {@code tableUuid}, newest first, capped at {@code limit} * rows. Optional {@code since} filter (inclusive). {@code limit} is required. */ + @ApiResponses( + value = { + @ApiResponse(responseCode = "200", description = "StatsHistory GET: OK"), + @ApiResponse(responseCode = "400", description = "StatsHistory GET: BAD_REQUEST") + }) @GetMapping("/{tableUuid}/history") public ResponseEntity> getStatsHistory( @PathVariable String tableUuid,