diff --git a/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java b/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java index 675eda5b4..23524ed61 100644 --- a/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java +++ b/core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AnnotationScanner.java @@ -305,9 +305,23 @@ default Optional processOperation(final AnnotationScannerContext cont // validate operationId String operationId = operation.getOperationId(); + if (operationId != null) { - final MethodInfo conflictingMethod = context.getOperationIdMap().putIfAbsent(operationId, method); - if (conflictingMethod != null) { + saveOperationId(context, resourceClass, method, operationId); + } + + return Optional.of(operation); + } + + private void saveOperationId(AnnotationScannerContext context, ClassInfo resourceClass, MethodInfo method, + String operationId) { + final MethodInfo conflictingMethod = context.getOperationIdMap().putIfAbsent(operationId, method); + + if (conflictingMethod != null) { + if (context.getAugmentedIndex().ancestry(method).values().contains(conflictingMethod)) { + // The conflict was a method from a parent class, replace it + context.getOperationIdMap().put(operationId, method); + } else { final ClassInfo conflictingClass = conflictingMethod.declaringClass(); final String className = resourceClass.name().toString(); final String methodName = method.toString(); @@ -322,8 +336,6 @@ default Optional processOperation(final AnnotationScannerContext cont } } } - - return Optional.of(operation); } default void setJsonViewContext(AnnotationScannerContext context, Type[] views) { diff --git a/core/src/test/java/io/smallrye/openapi/runtime/scanner/LogCapture.java b/core/src/test/java/io/smallrye/openapi/runtime/scanner/LogCapture.java index 92886f687..31eb81b62 100644 --- a/core/src/test/java/io/smallrye/openapi/runtime/scanner/LogCapture.java +++ b/core/src/test/java/io/smallrye/openapi/runtime/scanner/LogCapture.java @@ -90,6 +90,25 @@ public LogRecord assertLogContaining(String substring) { } } + public void assertNoLogContaining(String substring) { + synchronized (handler.records) { + for (LogRecord rec : handler.records) { + if (rec.getMessage().contains(substring)) { + StringBuilder sb = new StringBuilder(); + sb.append("Log containing \"").append(substring).append("\" was found."); + sb.append("\n"); + sb.append("Log records recorded:\n"); + for (LogRecord r : handler.records) { + sb.append("[").append(r.getLevel()).append("] "); + sb.append(r.getMessage()).append("\n"); + } + + throw new AssertionError(sb.toString()); + } + } + } + } + private static class TestHandler extends Handler { private List records = Collections.synchronizedList(new ArrayList<>()); diff --git a/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/OperationIdTest.java b/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/OperationIdTest.java index ffbcec4ce..dfe54bb67 100644 --- a/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/OperationIdTest.java +++ b/extension-jaxrs/src/test/java/io/smallrye/openapi/runtime/scanner/OperationIdTest.java @@ -5,6 +5,7 @@ import org.eclipse.microprofile.openapi.models.OpenAPI; import org.jboss.jandex.Index; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -23,6 +24,9 @@ */ class OperationIdTest extends JaxRsDataObjectScannerTestBase { + @RegisterExtension + public LogCapture logs = new LogCapture(ScannerLogging.class.getPackage().getName()); + @ParameterizedTest @CsvSource({ "METHOD, test.io.smallrye.openapi.runtime.scanner.resources.javax.GreetingGetResource, resource.testOperationIdMethod.json", @@ -56,6 +60,7 @@ void testInheritedOperationIdsUtilizeConcreteClassName() throws Exception { OpenAPI result = OpenApiProcessor.bootstrap(config, index); printToConsole(result); assertJsonEquals("resource.testOperationIdWithInheritance.json", result); + logs.assertNoLogContaining("Duplicate operationId"); } finally { System.clearProperty(SmallRyeOASConfig.OPERATION_ID_STRAGEGY); } diff --git a/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/Salutation.java b/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/Salutation.java index 1ffa4cb84..ab4556228 100644 --- a/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/Salutation.java +++ b/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/Salutation.java @@ -8,6 +8,8 @@ public interface Salutation { @GET @Produces(MediaType.TEXT_PLAIN) - public String get(); + default String get() { + return "hi"; + } } diff --git a/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationEnglish.java b/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationEnglish.java index 9b3703c76..db5f7326c 100644 --- a/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationEnglish.java +++ b/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationEnglish.java @@ -1,11 +1,16 @@ package test.io.smallrye.openapi.runtime.scanner.resources.jakarta; +import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("/english") public class SalutationEnglish implements Salutation { @Override + @GET + @Produces(MediaType.TEXT_PLAIN) public String get() { return "hello"; } diff --git a/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationSpanish.java b/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationSpanish.java index a3b252852..6a1743c0b 100644 --- a/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationSpanish.java +++ b/extension-jaxrs/src/test/java/test/io/smallrye/openapi/runtime/scanner/resources/jakarta/SalutationSpanish.java @@ -1,11 +1,16 @@ package test.io.smallrye.openapi.runtime.scanner.resources.jakarta; +import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("/spanish") public class SalutationSpanish implements Salutation { @Override + @GET + @Produces(MediaType.TEXT_PLAIN) public String get() { return "hola"; }