CELDEV-1334 Add navigation REST API - #309
Conversation
| @RestControllerAdvice(assignableTypes = NavigationController.class) | ||
| public final class NavigationExceptionHandler { |
There was a problem hiding this comment.
Move these @ExceptionHandler methods into NavigationController. NavigationExceptionHandler applies only to NavigationController. Separate advice adds another component and cross-file control flow without reuse. extract shared handlers when multiple controllers use the same error contract.
| @GetMapping(path = "/{nodeSpace}", produces = MediaType.APPLICATION_JSON_VALUE) | ||
| @PreAuthorize("permitAll()") | ||
| @Operation(summary = "Get the current wiki's navigation tree", description = """ | ||
| Public endpoint returning a caller-relative, rights-filtered navigation tree. | ||
| Input and output references are canonical and local to the wiki handling the request. | ||
| Responses are private and not cacheable by shared or browser caches. | ||
| """) | ||
| @ApiResponses({ | ||
| @ApiResponse(responseCode = "200", | ||
| description = "The rights-filtered segmented navigation tree", | ||
| content = @Content(schema = @Schema(implementation = NavigationTreeResponse.class), | ||
| examples = @ExampleObject(value = """ | ||
| { | ||
| "nodeSpace": "Content", | ||
| "currentNode": "Content.MyPage", | ||
| "language": "de", | ||
| "partName": null, | ||
| "showInactiveToLevel": 2, | ||
| "segments": [] | ||
| } | ||
| """))), | ||
| @ApiResponse(responseCode = "400", | ||
| description = "Invalid reference or parameter, or unsupported language", | ||
| content = @Content(schema = @Schema(implementation = NavigationErrorResponse.class), | ||
| examples = @ExampleObject(value = """ | ||
| {"code":"invalid_reference","message":"The reference is invalid."} | ||
| """))), | ||
| @ApiResponse(responseCode = "404", | ||
| description = "The active node is absent, inaccessible, out of root, or part-excluded", | ||
| content = @Content(schema = @Schema(implementation = NavigationErrorResponse.class), | ||
| examples = @ExampleObject(value = """ | ||
| { | ||
| "code": "navigation_node_not_found", | ||
| "message": "The navigation node was not found." | ||
| } | ||
| """))), | ||
| @ApiResponse(responseCode = "500", description = "Navigation infrastructure is unavailable", | ||
| content = @Content(schema = @Schema(implementation = NavigationErrorResponse.class), | ||
| examples = @ExampleObject(value = """ | ||
| { | ||
| "code": "navigation_unavailable", | ||
| "message": "Navigation is currently unavailable." | ||
| } | ||
| """))) }) | ||
| public ResponseEntity<NavigationTreeResponse> getNavigation( | ||
| @Parameter(description = "Canonical local space reference identifying the navigation root", | ||
| example = "Content", required = true) @PathVariable String nodeSpace, | ||
| @Parameter(description = "Canonical local document reference identifying the active node", | ||
| example = "Content.MyPage") @RequestParam(required = false) String currentNode, | ||
| @Parameter(description = "Allowed wiki language; defaults to the current request language", | ||
| example = "de") @RequestParam(required = false) String language, | ||
| @Parameter( | ||
| description = "Case-sensitive root part filter; missing or blank returns all parts", | ||
| example = "main") @RequestParam(required = false) String partName, | ||
| @Parameter(description = "Inactive expansion threshold from 0 through 100", example = "2", | ||
| schema = @Schema(defaultValue = "0", minimum = "0", maximum = "100")) @RequestParam( | ||
| name = "show_inactive_to_level", defaultValue = "0") int showInactiveToLevel) { |
There was a problem hiding this comment.
Annotation soup.
Springdoc already infers request parameters, success responses, and record schemas. Remove inline JSON examples and annotations that merely restate names or types. Keep only a short operation summary, non-obvious constraints, and explicit error responses. Simplify OpenAPI tests to verify structural contract instead of prose and examples. Do not move duplication into interfaces, wrappers, or custom annotations.
Applies to:
- NavigationController.java
- NavigationErrorResponse.java
- NavigationNodeDto.java
- NavigationSegmentDto.java
- NavigationTreeResponse.java
- NavigationOpenApiContractTest.java
| return groupedRoots.entrySet().stream().sorted(Map.Entry.comparingByKey(PART_NAME_COMPARATOR)) | ||
| .map(entry -> new NavigationSegmentDto(emptyToNull(entry.getKey()), | ||
| entry.getValue().stream().map(root -> toDto(root, 1, activePath, request)).toList())) |
There was a problem hiding this comment.
one chain call per line if a full chain doesn't fit in one line. please apply this readability concern for the entire file.
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
|
||
| public class NavigationControllerTest { |
There was a problem hiding this comment.
tests in this PR do not follow celements-testing conventions: they manually construct components and mocks instead of using AbstractComponentTest, registerComponentMocks(...), and getBeanFactory(). This bypasses real Spring/XWiki component wiring.
Please install relevant skills globally https://github.com/celements/synventis-tools/blob/dev/skills/README.md
https://synjira.atlassian.net/browse/CELDEV-1334
Summary
celements-navigation-restwith the public/api/v1/navigation/{nodeSpace}contractCache-Control: private, no-storeto successful and error responsesCoordinated delivery
Draft 2 of 4 for CELDEV-1334:
Validation