From 0a10aca350c6fdcf0861f372683a2ee1f8f4b783 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Thu, 29 Aug 2024 15:37:37 -0400 Subject: [PATCH] Enhance OpenSearch APIs annotation processor with OpenSearch version validation (#15502) Signed-off-by: Andriy Redko --- .../processor/ApiAnnotationProcessor.java | 61 +++++++++++++++++++ .../ApiAnnotationProcessorTests.java | 31 ++++++++++ .../PublicApiAnnotatedUnparseable.java | 16 +++++ .../PublicApiWithDeprecatedApiMethod.java | 20 ++++++ 4 files changed, 128 insertions(+) create mode 100644 libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiAnnotatedUnparseable.java create mode 100644 libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiWithDeprecatedApiMethod.java diff --git a/libs/common/src/main/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessor.java b/libs/common/src/main/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessor.java index 569f48a8465f3..94ec0db3a9712 100644 --- a/libs/common/src/main/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessor.java +++ b/libs/common/src/main/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessor.java @@ -59,6 +59,7 @@ public class ApiAnnotationProcessor extends AbstractProcessor { private static final String OPENSEARCH_PACKAGE = "org.opensearch"; private final Set reported = new HashSet<>(); + private final Set validated = new HashSet<>(); private final Set processed = new HashSet<>(); private Kind reportFailureAs = Kind.ERROR; @@ -85,6 +86,8 @@ public boolean process(Set annotations, RoundEnvironment ); for (var element : elements) { + validate(element); + if (!checkPackage(element)) { continue; } @@ -100,6 +103,64 @@ public boolean process(Set annotations, RoundEnvironment return false; } + private void validate(Element element) { + // The element was validated already + if (validated.contains(element)) { + return; + } + + validated.add(element); + + final PublicApi publicApi = element.getAnnotation(PublicApi.class); + if (publicApi != null) { + if (!validateVersion(publicApi.since())) { + processingEnv.getMessager() + .printMessage( + reportFailureAs, + "The type " + element + " has @PublicApi annotation with unparseable OpenSearch version: " + publicApi.since() + ); + } + } + + final DeprecatedApi deprecatedApi = element.getAnnotation(DeprecatedApi.class); + if (deprecatedApi != null) { + if (!validateVersion(deprecatedApi.since())) { + processingEnv.getMessager() + .printMessage( + reportFailureAs, + "The type " + + element + + " has @DeprecatedApi annotation with unparseable OpenSearch version: " + + deprecatedApi.since() + ); + } + } + } + + private boolean validateVersion(String version) { + String[] parts = version.split("[.-]"); + if (parts.length < 3 || parts.length > 4) { + return false; + } + + int major = Integer.parseInt(parts[0]); + if (major > 3 || major < 0) { + return false; + } + + int minor = Integer.parseInt(parts[1]); + if (minor < 0) { + return false; + } + + int patch = Integer.parseInt(parts[2]); + if (patch < 0) { + return false; + } + + return true; + } + /** * Check top level executable element * @param executable top level executable element diff --git a/libs/common/src/test/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessorTests.java b/libs/common/src/test/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessorTests.java index 52162e3df0c1c..716dcc3b9015f 100644 --- a/libs/common/src/test/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessorTests.java +++ b/libs/common/src/test/java/org/opensearch/common/annotation/processor/ApiAnnotationProcessorTests.java @@ -486,4 +486,35 @@ public void testPublicApiConstructorAnnotatedInternalApi() { assertThat(failure.diagnotics(), not(hasItem(matching(Diagnostic.Kind.ERROR)))); } + + public void testPublicApiUnparseableVersion() { + final CompilerResult result = compile("PublicApiAnnotatedUnparseable.java"); + assertThat(result, instanceOf(Failure.class)); + + final Failure failure = (Failure) result; + assertThat(failure.diagnotics(), hasSize(3)); + + assertThat( + failure.diagnotics(), + hasItem( + matching( + Diagnostic.Kind.ERROR, + containsString( + "The type org.opensearch.common.annotation.processor.PublicApiAnnotatedUnparseable has @PublicApi annotation with unparseable OpenSearch version: 2.x" + ) + ) + ) + ); + } + + public void testPublicApiWithDeprecatedApiMethod() { + final CompilerResult result = compile("PublicApiWithDeprecatedApiMethod.java"); + assertThat(result, instanceOf(Failure.class)); + + final Failure failure = (Failure) result; + assertThat(failure.diagnotics(), hasSize(2)); + + assertThat(failure.diagnotics(), not(hasItem(matching(Diagnostic.Kind.ERROR)))); + } + } diff --git a/libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiAnnotatedUnparseable.java b/libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiAnnotatedUnparseable.java new file mode 100644 index 0000000000000..44779450c9fd1 --- /dev/null +++ b/libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiAnnotatedUnparseable.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.annotation.processor; + +import org.opensearch.common.annotation.PublicApi; + +@PublicApi(since = "2.x") +public class PublicApiAnnotatedUnparseable { + +} diff --git a/libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiWithDeprecatedApiMethod.java b/libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiWithDeprecatedApiMethod.java new file mode 100644 index 0000000000000..3cb28d3360830 --- /dev/null +++ b/libs/common/src/test/resources/org/opensearch/common/annotation/processor/PublicApiWithDeprecatedApiMethod.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.annotation.processor; + +import org.opensearch.common.annotation.DeprecatedApi; +import org.opensearch.common.annotation.PublicApi; + +@PublicApi(since = "1.0.0") +public class PublicApiWithDeprecatedApiMethod { + @DeprecatedApi(since = "0.1.0") + public void method() { + + } +}