Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,9 @@ public static void applyPageableAnnotations(
Map<String, PageableDefaultsData> pageableDefaultsRegistry,
AnnotationSyntax syntax) {

String operationId = codegenOperation.operationId;
String operationId = codegenOperation.operationIdOriginal != null
? codegenOperation.operationIdOriginal
: codegenOperation.operationId;
List<String> pageableAnnotations = new ArrayList<>();

if (generatePageableConstraintValidation && useBeanValidation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7888,6 +7888,36 @@ public void sortDefaultAndPageableDefaultBothApplied() throws IOException {
.fileContains("@SortDefault.SortDefaults({@SortDefault(sort = {\"name\"}, direction = Sort.Direction.DESC), @SortDefault(sort = {\"id\"}, direction = Sort.Direction.ASC)})");
}

@Test
public void pageableAnnotationsUseOriginalOperationId_issue24721() throws IOException {
Map<String, Object> props = new HashMap<>();
props.put(INTERFACE_ONLY, "true");
props.put(SpringCodegen.SKIP_DEFAULT_INTERFACE, "true");
props.put(SpringCodegen.USE_TAGS, "true");
props.put(SpringCodegen.USE_SPRING_BOOT3, "true");
props.put(SpringCodegen.AUTO_X_SPRING_PAGINATED, "true");
props.put(SpringCodegen.GENERATE_PAGEABLE_CONSTRAINT_VALIDATION, "true");
props.put(SpringCodegen.GENERATE_SORT_VALIDATION, "true");

Map<String, File> files = generateFromContract(
"src/test/resources/3_0/spring/issue_24721.yaml", SPRING_BOOT, props);

JavaFileAssert.assertThat(files.get("ItemsApi.java"))
.assertMethod("listItems")
.assertParameter("pageable")
.hasType("Pageable")
.assertParameterAnnotations()
.containsWithName("ValidPageable")
.containsWithName("ValidSort")
.containsWithName("PageableDefault");

JavaFileAssert.assertThat(files.get("ItemsApi.java"))
.fileContains("@ValidPageable(maxSize = 100, maxPage = 50)")
.fileContains("@ValidSort(allowedValues = {\"id,asc\", \"id,desc\", \"name,asc\", \"name,desc\"})")
.fileContains("@PageableDefault(page = 0, size = 25)")
.fileContains("@SortDefault.SortDefaults({@SortDefault(sort = {\"name\"}, direction = Sort.Direction.DESC)})");
}

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.

thanks for the PR and adding the test

should the test look for the original operationId list-items in the output to confirm it's preserved?

@wassuh0520 wassuh0520 Aug 21, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for the suggestion.

I added a focused unit test which explicitly sets the generated operation ID to listItems, keeps operationIdOriginal as list-items, and uses pageable registries keyed only by list-items

// -------------------------------------------------------------------------
// substituteGenericPagedModel tests
// -------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,31 @@ public void applyPageableAnnotations_sortDefault_kotlinSyntax() {
.isEqualTo("@SortDefault.SortDefaults(SortDefault(sort = [\"name\"], direction = Sort.Direction.ASC))");
}

@Test
public void applyPageableAnnotations_usesOriginalOperationIdForRegistryLookup() {
CodegenOperation op = minimalOp("listItems");
op.operationIdOriginal = "list-items";

Map<String, SpringPageableScanUtils.PageableConstraintsData> constraintsRegistry =
Collections.singletonMap("list-items", new SpringPageableScanUtils.PageableConstraintsData(50, 100, -1, -1));
Map<String, List<String>> sortValidationRegistry =
Collections.singletonMap("list-items", List.of("id,asc", "name,desc"));
Map<String, SpringPageableScanUtils.PageableDefaultsData> defaultsRegistry =
Collections.singletonMap("list-items", new SpringPageableScanUtils.PageableDefaultsData(
0, 25, List.of(new SpringPageableScanUtils.SortFieldDefault("name", "DESC"))));

SpringPageableScanUtils.applyPageableAnnotations(op, true, true, constraintsRegistry,
true, sortValidationRegistry, defaultsRegistry,
SpringPageableScanUtils.AnnotationSyntax.JAVA);

List<String> annotations = (List<String>) op.vendorExtensions.get("x-pageable-extra-annotation");
assertThat(annotations).containsExactly(
"@ValidPageable(maxSize = 100, maxPage = 50)",
"@ValidSort(allowedValues = {\"id,asc\", \"name,desc\"})",
"@PageableDefault(page = 0, size = 25)",
"@SortDefault.SortDefaults({@SortDefault(sort = {\"name\"}, direction = Sort.Direction.DESC)})");
}

@Test
public void applyPageableAnnotations_noMatchingRegistryEntries_noAnnotationsAdded() {
CodegenOperation op = minimalOp("someOtherOp");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
openapi: 3.0.1
info:
title: Issue 24721 Pageable Operation ID Sanitization
version: 1.0.0
paths:
/items:
get:
tags:
- items
operationId: list-items
parameters:
- name: page
in: query
schema:
type: integer
default: 0
maximum: 50
- name: size
in: query
schema:
type: integer
default: 25
maximum: 100
- name: sort
in: query
style: form
explode: true
schema:
type: array
items:
type: string
enum:
- id,asc
- id,desc
- name,asc
- name,desc
default:
- name,desc
responses:
'200':
description: Successful response
Loading