Repository: springdoc/springdoc-openapi
Pull request #1855 (2022-09-22 13:34:21)

Fixed a bug that duplicate field were get for record classes
## Describe the bug
When the argument of the MethodParameterPojoExtractor.extractFrom method is a record class, duplicate fields gets.

1. when the argument is a class (expected)

```java
// The ClassObject class has email, firstName, lastName fields and getters 
Stream<MethodParameter> actual = MethodParameterPojoExtractor.extractFrom(ClassObject.class);
actual.forEach(method -> System.out.println(method.getMethod().getName()));

// Below is the output result.
// getEmail
// getFirstName
// getLastName
```

2. when the argument is a record (unexpected)

```java
record RecordObject(String email, String firstName, String lastName) {}
Stream<MethodParameter> actual = MethodParameterPojoExtractor.extractFrom(RecordObject.class);
actual.forEach(method -> System.out.println(method.getMethod().getName()));

// Below is the output result.
// email
// firstName
// lastName
// email
// firstName
// lastName
// email
// firstName
// lastName
```

## Fixes
Added filter as well as Class.



Patch:
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java
index cbc93d1b2..7bfde64d7 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java
@@ -181,7 +181,9 @@ public class MethodParameterPojoExtractor {
 			boolean isNotRequired = parameter == null || !parameter.required();
 			Annotation[] finalFieldAnnotations = fieldAnnotations;
 			if (paramClass.isRecord()) {
-				return Stream.of( paramClass.getRecordComponents()).map(recordComponent -> recordComponent.getAccessor())
+				return Stream.of(paramClass.getRecordComponents())
+						.filter(d -> d.getName().equals(field.getName()))
+						.map(recordComponent -> recordComponent.getAccessor())
 						.map(method -> new MethodParameter(method, -1))
 						.map(methodParameter -> DelegatingMethodParameter.changeContainingClass(methodParameter, paramClass))
 						.map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), finalFieldAnnotations, true, isNotRequired));


Test patch:
diff --git a/springdoc-openapi-starter-common/src/test/java/org/springdoc/core/extractor/MethodParameterPojoExtractorTest.java b/springdoc-openapi-starter-common/src/test/java/org/springdoc/core/extractor/MethodParameterPojoExtractorTest.java
new file mode 100644
index 000000000..088192515
--- /dev/null
+++ b/springdoc-openapi-starter-common/src/test/java/org/springdoc/core/extractor/MethodParameterPojoExtractorTest.java
@@ -0,0 +1,91 @@
+/*
+ *
+ *  *
+ *  *  *
+ *  *  *  *
+ *  *  *  *  * Copyright 2019-2022 the original author or authors.
+ *  *  *  *  *
+ *  *  *  *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  *  *  *  * you may not use this file except in compliance with the License.
+ *  *  *  *  * You may obtain a copy of the License at
+ *  *  *  *  *
+ *  *  *  *  *      https://www.apache.org/licenses/LICENSE-2.0
+ *  *  *  *  *
+ *  *  *  *  * Unless required by applicable law or agreed to in writing, software
+ *  *  *  *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  *  *  *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  *  *  *  * See the License for the specific language governing permissions and
+ *  *  *  *  * limitations under the License.
+ *  *  *  *
+ *  *  *
+ *  *
+ *
+ */
+package org.springdoc.core.extractor;
+
+import java.lang.reflect.Method;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.core.MethodParameter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link MethodParameterPojoExtractor}.
+ */
+class MethodParameterPojoExtractorTest {
+	/**
+	 * Tests for {@link MethodParameterPojoExtractor#extractFrom(Class<?>)}.
+	 */
+	@Nested
+	class extractFrom {
+		@Test
+		void ifRecordObjectShouldGetField() {
+			Stream<MethodParameter> actual = MethodParameterPojoExtractor.extractFrom(RecordObject.class);
+			assertThat(actual)
+					.extracting(MethodParameter::getMethod)
+					.extracting(Method::getName)
+					.containsOnlyOnce("email", "firstName", "lastName");
+		}
+
+		@Test
+		void ifClassObjectShouldGetMethod() {
+			Stream<MethodParameter> actual = MethodParameterPojoExtractor.extractFrom(ClassObject.class);
+			assertThat(actual)
+					.extracting(MethodParameter::getMethod)
+					.extracting(Method::getName)
+					.containsOnlyOnce("getEmail", "getFirstName", "getLastName");
+		}
+
+		public record RecordObject(String email, String firstName, String lastName) {}
+
+		public class ClassObject {
+			private String email;
+
+			private String firstName;
+
+			private String lastName;
+
+			public ClassObject(String email, String firstName, String lastName) {
+				this.email = email;
+				this.firstName = firstName;
+				this.lastName = lastName;
+			}
+
+			public String getEmail() {
+				return email;
+			}
+
+			public String getFirstName() {
+				return firstName;
+			}
+
+			public String getLastName() {
+				return lastName;
+			}
+		}
+	}
+}