ignoredNewArchives = new ArrayList<>();
+ for (JsonNode node : configuration) {
+ if (!node.isTextual()) {
+ throw new IllegalArgumentException(CONFIGURATION_ERROR_MESSAGE);
+ }
+
+ ignoredNewArchives.add(node.asText());
+ }
+
+ this.ignoredNewArchives = ignoredNewArchives;
+ }
}
@Override
public TransformationResult tryTransform(@Nullable E oldElement, @Nullable E newElement, Difference difference) {
+ if (!enabled) {
+ // If this transform isn't enabled, keep the current result.
+ return TransformationResult.keep();
+ }
+
// RevApi will always add newArchive and newArchiveRole together.
String newArchive = difference.attachments.get("newArchive");
String newArchiveRole = difference.attachments.get("newArchiveRole");
@@ -51,10 +91,16 @@ public TransformationResult tryTransform(@Nullable E oldElement, @Nullable E new
return TransformationResult.keep();
}
- if (!newArchive.startsWith("com.azure:azure-core:")
- && !newArchive.startsWith("com.azure:azure-json:")
- && !newArchive.startsWith("com.azure:azure-xml:")) {
- // The difference isn't from the azure-core, azure-json, or azure-xml SDK, keep the current result.
+ boolean shouldKeep = true;
+ for (String ignoredNewArchive : ignoredNewArchives) {
+ if (newArchive.startsWith(ignoredNewArchive)) {
+ shouldKeep = false;
+ break;
+ }
+ }
+
+ if (shouldKeep) {
+ // The difference didn't match any 'ignoredNewArchives', keep the current result.
return TransformationResult.keep();
}
@@ -62,7 +108,7 @@ public TransformationResult tryTransform(@Nullable E oldElement, @Nullable E new
// infinite transformation loop as RevApi will keep running the transformation pipeline until there are no
// transformations applied in the pipeline run.
if (difference.criticality == Criticality.ERROR) {
- // The difference is from azure-core and azure-core is a dependency to this SDK, discard it for now.
+ // The difference is from an ignored new archive, discard it.
// In the future this could retain it with a lower criticality level for informational reasons.
return TransformationResult.discard();
} else {
diff --git a/eng/code-quality-reports/src/main/java/com/azure/tools/revapi/transforms/PrefixMatcher.java b/eng/code-quality-reports/src/main/java/com/azure/tools/revapi/transforms/PrefixMatcher.java
new file mode 100644
index 000000000000..4c212367fd3c
--- /dev/null
+++ b/eng/code-quality-reports/src/main/java/com/azure/tools/revapi/transforms/PrefixMatcher.java
@@ -0,0 +1,48 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+package com.azure.tools.revapi.transforms;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * A {@link Predicate} implementation that matches an input against an initial prefix and an optional list of
+ * sub-prefixes.
+ *
+ * The initial prefix must always be matched to be able to return true, then if there are any sub-prefixes only one of
+ * those must be matched to return true. If sub-prefixes are empty then only the initial prefix must be matched.
+ */
+final class PrefixMatcher implements Predicate {
+ private final String initialPrefix;
+ private final int initialPrefixLength;
+ private final List subPrefixes;
+
+ PrefixMatcher(String initialPrefix, List subPrefixes) {
+ this.initialPrefix = initialPrefix;
+ this.initialPrefixLength = initialPrefix.length();
+ this.subPrefixes = subPrefixes;
+ }
+
+ @Override
+ public boolean test(String s) {
+ if (s == null) {
+ return false;
+ }
+
+ if (!s.startsWith(initialPrefix)) {
+ return false;
+ }
+
+ if (subPrefixes.isEmpty()) {
+ return true;
+ }
+
+ for (String subPrefix : subPrefixes) {
+ if (s.regionMatches(initialPrefixLength, subPrefix, 0, subPrefix.length())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/eng/code-quality-reports/src/main/java/com/azure/tools/revapi/transforms/RevApiUtils.java b/eng/code-quality-reports/src/main/java/com/azure/tools/revapi/transforms/RevApiUtils.java
new file mode 100644
index 000000000000..67056e1d4c10
--- /dev/null
+++ b/eng/code-quality-reports/src/main/java/com/azure/tools/revapi/transforms/RevApiUtils.java
@@ -0,0 +1,93 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+package com.azure.tools.revapi.transforms;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.revapi.AnalysisContext;
+
+import javax.annotation.Nonnull;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.TypeElement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class containing methods to work with RevApi.
+ */
+final class RevApiUtils {
+ private static final String CONFIGURATION_ERROR_MESSAGE = "Configuration '%s' must be an object where each key is "
+ + "an initial prefix to be matched and the value is an array of strings representing sub-prefixes to match, "
+ + "where an empty array of sub-prefixes indicates only the initial prefix needs to be matched.";
+
+ /**
+ * Finds the outermost class {@link TypeElement} for the given {@link Element}.
+ *
+ * @param el The element to find the outermost class for.
+ * @return The outermost class for the given element, may return null.
+ */
+ static TypeElement findOuterMostClass(Element el) {
+ while (el != null && !(el instanceof TypeElement)) {
+ el = el.getEnclosingElement();
+ }
+
+ if (el == null) {
+ return null;
+ }
+
+ return ((TypeElement) el).getNestingKind() == NestingKind.TOP_LEVEL
+ ? (TypeElement) el
+ : findOuterMostClass(el.getEnclosingElement());
+ }
+
+ static List createPrefixMatchersFromConfiguration(@Nonnull AnalysisContext analysisContext,
+ String propertyName) {
+ JsonNode configuration = analysisContext.getConfigurationNode().get(propertyName);
+ if (configuration == null) {
+ return Collections.emptyList();
+ }
+
+ if (!configuration.isObject()) {
+ throw new IllegalArgumentException(String.format(CONFIGURATION_ERROR_MESSAGE, propertyName));
+ }
+
+ List prefixMatchers = new ArrayList<>();
+ ObjectNode prefixesObject = (ObjectNode) configuration;
+ for (Iterator> it = prefixesObject.fields(); it.hasNext(); ) {
+ Map.Entry allowedPrefixKvp = it.next();
+ String initialPrefix = allowedPrefixKvp.getKey();
+ if (initialPrefix.isEmpty()) {
+ throw new IllegalArgumentException(String.format(CONFIGURATION_ERROR_MESSAGE, propertyName));
+ }
+
+ JsonNode allowedPrefix = allowedPrefixKvp.getValue();
+ if (!allowedPrefix.isArray()) {
+ throw new IllegalArgumentException(String.format(CONFIGURATION_ERROR_MESSAGE, propertyName));
+ }
+
+ ArrayNode subPrefixes = (ArrayNode) allowedPrefix;
+ List subPrefixList = new ArrayList<>();
+
+ for (JsonNode subPrefix : subPrefixes) {
+ if (!subPrefix.isTextual()) {
+ throw new IllegalArgumentException(String.format(CONFIGURATION_ERROR_MESSAGE, propertyName));
+ }
+
+ subPrefixList.add(subPrefix.asText());
+ }
+
+ prefixMatchers.add(new PrefixMatcher(initialPrefix, subPrefixList));
+ }
+
+ return prefixMatchers;
+ }
+
+ private RevApiUtils() {
+ // Private constructor to prevent instantiation.
+ }
+}
diff --git a/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.DifferenceTransform b/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.DifferenceTransform
index 2933e6c3e323..7474ffc9e854 100644
--- a/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.DifferenceTransform
+++ b/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.DifferenceTransform
@@ -1,3 +1,3 @@
-com.azure.tools.revapi.transforms.AzureSdkAllowedExternalApis
-com.azure.tools.revapi.transforms.JacksonDatabindRemovalTransform
-com.azure.tools.revapi.transforms.TransitiveCoreChangesTransform
+com.azure.tools.revapi.transforms.AllowedExternalApis
+com.azure.tools.revapi.transforms.IgnoredJacksonDatabindRemovalTransform
+com.azure.tools.revapi.transforms.IgnoredTransitiveChangesTransform
diff --git a/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.TreeFilterProvider b/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.TreeFilterProvider
index 28443e149c65..53e3a61e11e9 100644
--- a/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.TreeFilterProvider
+++ b/eng/code-quality-reports/src/main/resources/META-INF/services/org.revapi.TreeFilterProvider
@@ -1 +1 @@
-com.azure.tools.revapi.transforms.AzureSdkTreeFilterProvider
+com.azure.tools.revapi.transforms.ClassAndPackageTreeFilterProvider
diff --git a/eng/code-quality-reports/src/main/resources/revapi/clientcore-revapi.json b/eng/code-quality-reports/src/main/resources/revapi/clientcore-revapi.json
new file mode 100644
index 000000000000..d2efa6306151
--- /dev/null
+++ b/eng/code-quality-reports/src/main/resources/revapi/clientcore-revapi.json
@@ -0,0 +1,91 @@
+[
+ {
+ "extension": "revapi.java",
+ "configuration": {
+ "missing-classes": {
+ "behavior": "ignore",
+ "ignoreMissingAnnotations": false
+ },
+ "matchOverloads": true
+ }
+ },
+ {
+ "extension": "revapi.versions",
+ "configuration": {
+ "enabled": true,
+ "semantic0": false,
+ "versionIncreaseAllows": {
+ "major": {
+ "severity": "BREAKING"
+ },
+ "minor": {
+ "severity": "NON_BREAKING"
+ },
+ "patch": {
+ "severity": "EQUIVALENT"
+ }
+ },
+ "onAllowed": {
+ "remove": true,
+ "attachments": {
+ "breaksVersioningRules": "false"
+ }
+ },
+ "onDisallowed": {
+ "criticality": "error",
+ "attachments": {
+ "breaksVersioningRules": "true"
+ }
+ },
+ "passThroughDifferences": [
+ "java.class.nonPublicPartOfAPI"
+ ]
+ }
+ },
+ {
+ "extension": "allowed-external-apis",
+ "configuration": {
+ "enabled": true,
+ "allowedPrefixes": {
+ "io.": [
+ "clientcore.core"
+ ]
+ }
+ }
+ },
+ {
+ "extension": "ignored-transitive-changes",
+ "configuration": {
+ "enabled": true,
+ "ignoredNewArchives": [
+ "io.clientcore:core:"
+ ]
+ }
+ },
+ {
+ "extension": "class-and-package-tree-filter-provider",
+ "configuration": {
+ "ignoredClasses": {
+ },
+ "ignoredPackages": {
+ "kotlin": [],
+ "okhttp3": [],
+ "okio": [],
+ "org.": [
+ "junit"
+ ]
+ },
+ "ignoredPackagesPatterns": [
+ "io\\.clientcore\\..*(implementation|samples).*"
+ ]
+ }
+ },
+ {
+ "extension": "revapi.differences",
+ "configuration": {
+ "ignore": true,
+ "differences": [
+ ]
+ }
+ }
+]
diff --git a/eng/code-quality-reports/src/main/resources/revapi/revapi.json b/eng/code-quality-reports/src/main/resources/revapi/revapi.json
index c273b71cff54..923af61d9e10 100644
--- a/eng/code-quality-reports/src/main/resources/revapi/revapi.json
+++ b/eng/code-quality-reports/src/main/resources/revapi/revapi.json
@@ -42,6 +42,121 @@
]
}
},
+ {
+ "extension": "allowed-external-apis",
+ "configuration": {
+ "enabled": true,
+ "allowedPrefixes": {
+ "com.azure.": [
+ "communication.common.",
+ "core.",
+ "cosmos.",
+ "data.appconfiguration.",
+ "data.schemaregistry.",
+ "identity.",
+ "json.",
+ "messaging.eventgrid.",
+ "messaging.eventhub.",
+ "messaging.servicebus.",
+ "perf.test.core.",
+ "resourcemanager.",
+ "security.keyvault.",
+ "spring.cloud.appconfiguration.config.",
+ "spring.cloud.feature.",
+ "storage.",
+ "xml."
+ ],
+ "com.mysql.cj.": [],
+ "io.": [
+ "clientcore.",
+ "cloudevents.",
+ "opentelemetry."
+ ],
+ "org.": [
+ "json.",
+ "postgresql.",
+ "reactivestreams.",
+ "springframework.util.ErrorHandler"
+ ],
+ "redis.clients.jedis": []
+ }
+ }
+ },
+ {
+ "extension": "ignored-jackson-databind-removal",
+ "configuration": {
+ "enabled": true
+ }
+ },
+ {
+ "extension": "ignored-transitive-changes",
+ "configuration": {
+ "enabled": true,
+ "ignoredNewArchives": [
+ "com.azure:azure-core:",
+ "com.azure:azure-json:",
+ "com.azure:azure-xml:"
+ ]
+ }
+ },
+ {
+ "extension": "class-and-package-tree-filter-provider",
+ "configuration": {
+ "ignoredClasses": {
+ "com.azure.": [
+ "core.util.Configuration",
+ "cosmos.BridgeInternal",
+ "cosmos.CosmosBridgeInternal",
+ "cosmos.models.ModelBridgeInternal",
+ "cosmos.util.UtilBridgeInternal",
+ "spring.cloud.config.AppConfigurationBootstrapConfiguration",
+ "spring.cloud.config.AppConfigurationRefresh",
+ "spring.cloud.config.properties.AppConfigurationProviderProperties",
+ "spring.cloud.config.web.AppConfigurationEndpoint",
+ "spring.cloud.config.web.pushrefresh.AppConfigurationRefreshEvent"
+ ]
+ },
+ "ignoredPackages": {
+ "com.": [
+ "azure.data.cosmos",
+ "fasterxml.jackson",
+ "google.gson",
+ "microsoft.azure",
+ "numbusds"
+ ],
+ "io.": [
+ "micrometer",
+ "netty",
+ "vertx"
+ ],
+ "javax.": [
+ "jms",
+ "servlet"
+ ],
+ "kotlin": [],
+ "okhttp3": [],
+ "okio": [],
+ "org.": [
+ "apache.avro",
+ "apache.commons",
+ "apache.qpid",
+ "junit",
+ "reactivestreams",
+ "slf4j",
+ "springframework"
+ ],
+ "reactor.": [
+ "core",
+ "netty",
+ "util"
+ ]
+ },
+ "ignoredPackagesPatterns": [
+ "com\\.azure\\..*(implementation|samples).*",
+ "com\\.azure\\.resourcemanager\\..*(fluent)(\\..*)?$"
+ ]
+ }
+ },
{
"extension": "revapi.differences",
"configuration": {
@@ -479,68 +594,6 @@
"old": ".*? com\\.azure\\.resourcemanager\\..*\\.models.*",
"justification": "Migration to azure-json."
},
- {
- "regex": true,
- "code": "java\\.annotation\\.(attributeRemoved|attributeAdded)",
- "old": ".*? com\\.azure\\.communication\\.messages\\.models.*",
- "justification": "Jackson annotation changed."
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.communication\\.phonenumbers\\.models.*",
- "new": ".*? com\\.azure\\.communication\\.phonenumbers\\.models.*",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.digitaltwins\\.core(\\.models)?.*",
- "new": ".*? com\\.azure\\.digitaltwins\\.core(\\.models)?.*",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.messaging\\.webpubsub\\.client\\.models.*",
- "new": ".*? com\\.azure\\.messaging\\.webpubsub\\.client\\.models.*",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.messaging\\.webpubsub\\.models.*",
- "new": ".*? com\\.azure\\.messaging\\.webpubsub\\.models.*",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.ai\\.translation\\.text\\.models.*",
- "new": ".*? com\\.azure\\.ai\\.translation\\.text\\.models.*",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.communication\\.callautomation\\.models.*",
- "new": ".*? com\\.azure\\.communication\\.callautomation\\.models.*",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.communication\\.chat\\.models.*",
- "new": ".*? com\\.azure\\.communication\\.chat\\.models.*",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.communication\\.email\\.models.*",
- "new": ".*? com\\.azure\\.communication\\.email\\.models.*",
- "justification": "Migration to azure-json"
- },
{
"regex": true,
"code": "java.class.nowFinal",
@@ -555,21 +608,6 @@
"new": "class com.azure.resourcemanager.(eventhubs|servicebus).models.UserAssignedIdentity",
"justification": "Class is now final."
},
- {
- "ignore": true,
- "code": "java.annotation.removed",
- "old": "method com.azure.communication.identity.models.CommunicationTokenScope com.azure.communication.identity.models.CommunicationTokenScope::fromString(java.lang.String)",
- "new": "method com.azure.communication.identity.models.CommunicationTokenScope com.azure.communication.identity.models.CommunicationTokenScope::fromString(java.lang.String)",
- "annotation": "@com.fasterxml.jackson.annotation.JsonCreator",
- "justification": "Migration to azure-json"
- },
- {
- "regex": true,
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.communication\\.messages\\.models(\\.channels)?.*",
- "new": ".*? com\\.azure\\.communication\\.messages\\.models(\\.channels)?.*",
- "justification": "Migration to azure-json"
- },
{
"code": "java.method.returnTypeErasureChanged",
"old": "method T com.azure.identity.AadCredentialBuilderBase>::clientId(java.lang.String) @ com.azure.identity.InteractiveBrowserCredentialBuilder",
@@ -582,11 +620,6 @@
"new": "method com.azure.identity.DeviceCodeCredentialBuilder com.azure.identity.DeviceCodeCredentialBuilder::clientId(java.lang.String)",
"justification": "Override flags this as a potential binary breaking change, but it isn't."
},
- {
- "code": "java\\.annotation\\.removed",
- "old": ".*? com\\.azure\\.compute\\.batch\\.models.*",
- "justification": "Removing Jackson annotations from Azure Batch in transition to stream-style."
- },
{
"ignore": true,
"code": "java.method.returnTypeChanged",
@@ -675,27 +708,12 @@
"new": "class com.azure.resourcemanager.sql.models.UserIdentity",
"justification": "Customer unlikely to subclass this class."
},
- {
- "ignore": true,
- "code": "java.annotation.removed",
- "old": "method com.azure.communication.rooms.models.ParticipantRole com.azure.communication.rooms.models.ParticipantRole::fromString(java.lang.String)",
- "new": "method com.azure.communication.rooms.models.ParticipantRole com.azure.communication.rooms.models.ParticipantRole::fromString(java.lang.String)",
- "annotation": "@com.fasterxml.jackson.annotation.JsonCreator",
- "justification": "Migration to azure-json"
- },
{
"ignore": true,
"code": "java.method.removed",
"old": "method com.azure.resourcemanager.compute.models.WindowsConfiguration com.azure.resourcemanager.compute.models.WindowsConfiguration::withEnableVMAgentPlatformUpdates(java.lang.Boolean)",
"justification": "Service changed the property to readOnly."
},
- {
- "regex": true,
- "code" : "java\\.annotation\\.removed",
- "old" : ".*? com\\.azure\\.developer\\.devcenter\\.models.*",
- "new" : ".*? com\\.azure\\.developer\\.devcenter\\.models.*",
- "justification": "Migration to azure-json"
- },
{
"ignore": true,
"code" : "java.method.removed",
diff --git a/eng/code-quality-reports/src/test/java/com/azure/tools/revapi/transforms/AzureSdkTreeFilterProviderTest.java b/eng/code-quality-reports/src/test/java/com/azure/tools/revapi/transforms/AzureSdkTreeFilterProviderTest.java
deleted file mode 100644
index 78f3fd074839..000000000000
--- a/eng/code-quality-reports/src/test/java/com/azure/tools/revapi/transforms/AzureSdkTreeFilterProviderTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-package com.azure.tools.revapi.transforms;
-
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
-
-import static com.azure.tools.revapi.transforms.AzureSdkTreeFilterProvider.excludeClass;
-import static com.azure.tools.revapi.transforms.AzureSdkTreeFilterProvider.excludePackage;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * Tests exclusion logic for {@link AzureSdkTreeFilterProvider}.
- */
-public class AzureSdkTreeFilterProviderTest {
- @ParameterizedTest
- @ValueSource(strings = {
- "com.azure.core.util.Configuration", "com.azure.cosmos.BridgeInternal", "com.azure.cosmos.CosmosBridgeInternal",
- "com.azure.cosmos.models.ModelBridgeInternal", "com.azure.cosmos.util.UtilBridgeInternal",
- "com.azure.spring.cloud.config.AppConfigurationBootstrapConfiguration",
- "com.azure.spring.cloud.config.AppConfigurationRefresh",
- "com.azure.spring.cloud.config.properties.AppConfigurationProviderProperties",
- "com.azure.spring.cloud.config.web.AppConfigurationEndpoint",
- "com.azure.spring.cloud.config.web.pushrefresh.AppConfigurationRefreshEvent"
- })
- public void classesThatShouldBeExcluded(String className) {
- assertTrue(excludeClass(className));
- }
-
- @ParameterizedTest
- @ValueSource(strings = {
- "com.azure.core.util.CoreUtils", "com.azure.cosmos.ConnectionMode", "com.azure.cosmos.CosmosClient",
- "com.azure.cosmos.models.ChangeFeedPolicy", "com.azure.cosmos.util.CosmosPagedFlux",
- "com.azure.spring.cloud.config.AppConfigurationConstants", "com.azure.spring.cloud.config.HostType",
- "com.azure.spring.cloud.config.properties.ConfigStore",
- "com.azure.spring.cloud.config.web.AppConfigurationWebAutoConfiguration",
- "com.azure.spring.cloud.config.web.pushrefresh.AppConfigurationRefreshEndpoint"
- })
- public void classesThatShouldNotBeExcluded(String className) {
- assertFalse(excludeClass(className));
- }
-
- @ParameterizedTest
- @ValueSource(strings = {
- "com.azure.data.cosmos", "com.azure.core.implementation", "com.azure.resourcemanager.fluent",
- "com.azure.resourcemanager.fluent.models", "com.fasterxml.jackson", "com.google.gson", "com.microsoft.azure",
- "com.nimbusds", "io.micrometer", "io.netty", "io.vertx", "javax.jms", "javax.servlet", "kotlin", "okhttp3",
- "okio", "org.apache.avro", "org.apache.commons", "org.apache.qpid", "org.junit", "org.slf4j",
- "org.springframework", "reactor.core", "reactor.netty", "reactor.util"
- })
- public void packagesThatShouldBeExcluded(String packageName) {
- assertTrue(excludePackage(packageName));
- }
-
- @ParameterizedTest
- @ValueSource(strings = {
- "com.azure.cosmos", "com.azure.core.util", "com.azure.resourcemanager.fluentcore"
- })
- public void packagesThatShouldBeNotExcluded(String packageName) {
- assertFalse(excludePackage(packageName));
- }
-}
diff --git a/eng/code-quality-reports/src/test/java/com/azure/tools/revapi/transforms/ClassAndPackageTreeFilterProviderTest.java b/eng/code-quality-reports/src/test/java/com/azure/tools/revapi/transforms/ClassAndPackageTreeFilterProviderTest.java
new file mode 100644
index 000000000000..3bc8995eeeea
--- /dev/null
+++ b/eng/code-quality-reports/src/test/java/com/azure/tools/revapi/transforms/ClassAndPackageTreeFilterProviderTest.java
@@ -0,0 +1,141 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.tools.revapi.transforms;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.revapi.AnalysisContext;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests exclusion logic for {@link ClassAndPackageTreeFilterProvider}.
+ */
+public class ClassAndPackageTreeFilterProviderTest {
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "com.azure.core.util.Configuration", "com.azure.cosmos.BridgeInternal", "com.azure.cosmos.CosmosBridgeInternal",
+ "com.azure.cosmos.models.ModelBridgeInternal", "com.azure.cosmos.util.UtilBridgeInternal",
+ "com.azure.spring.cloud.config.AppConfigurationBootstrapConfiguration",
+ "com.azure.spring.cloud.config.AppConfigurationRefresh",
+ "com.azure.spring.cloud.config.properties.AppConfigurationProviderProperties",
+ "com.azure.spring.cloud.config.web.AppConfigurationEndpoint",
+ "com.azure.spring.cloud.config.web.pushrefresh.AppConfigurationRefreshEvent"
+ })
+ public void classesThatShouldBeExcluded(String className) {
+ AnalysisContext context = AnalysisContext.builder().build().copyWithConfiguration(createDefaultConfiguration());
+
+ try (ClassAndPackageTreeFilterProvider treeFilterProvider = new ClassAndPackageTreeFilterProvider()) {
+ treeFilterProvider.initialize(context);
+ assertTrue(treeFilterProvider.excludeClass(className));
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "com.azure.core.util.CoreUtils", "com.azure.cosmos.ConnectionMode", "com.azure.cosmos.CosmosClient",
+ "com.azure.cosmos.models.ChangeFeedPolicy", "com.azure.cosmos.util.CosmosPagedFlux",
+ "com.azure.spring.cloud.config.AppConfigurationConstants", "com.azure.spring.cloud.config.HostType",
+ "com.azure.spring.cloud.config.properties.ConfigStore",
+ "com.azure.spring.cloud.config.web.AppConfigurationWebAutoConfiguration",
+ "com.azure.spring.cloud.config.web.pushrefresh.AppConfigurationRefreshEndpoint"
+ })
+ public void classesThatShouldNotBeExcluded(String className) {
+ AnalysisContext context = AnalysisContext.builder().build()
+ .copyWithConfiguration(createDefaultConfiguration());
+
+ try (ClassAndPackageTreeFilterProvider treeFilterProvider = new ClassAndPackageTreeFilterProvider()) {
+ treeFilterProvider.initialize(context);
+ assertFalse(treeFilterProvider.excludeClass(className));
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "com.azure.data.cosmos", "com.azure.core.implementation", "com.azure.resourcemanager.fluent",
+ "com.azure.resourcemanager.fluent.models", "com.fasterxml.jackson", "com.google.gson", "com.microsoft.azure",
+ "com.nimbusds", "io.micrometer", "io.netty", "io.vertx", "javax.jms", "javax.servlet", "kotlin", "okhttp3",
+ "okio", "org.apache.avro", "org.apache.commons", "org.apache.qpid", "org.junit", "org.slf4j",
+ "org.springframework", "reactor.core", "reactor.netty", "reactor.util"
+ })
+ public void packagesThatShouldBeExcluded(String packageName) {
+ AnalysisContext context = AnalysisContext.builder().build()
+ .copyWithConfiguration(createDefaultConfiguration());
+
+ try (ClassAndPackageTreeFilterProvider treeFilterProvider = new ClassAndPackageTreeFilterProvider()) {
+ treeFilterProvider.initialize(context);
+ assertTrue(treeFilterProvider.excludePackage(packageName));
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "com.azure.cosmos", "com.azure.core.util", "com.azure.resourcemanager.fluentcore"
+ })
+ public void packagesThatShouldBeNotExcluded(String packageName) {
+ AnalysisContext context = AnalysisContext.builder().build()
+ .copyWithConfiguration(createDefaultConfiguration());
+
+ try (ClassAndPackageTreeFilterProvider treeFilterProvider = new ClassAndPackageTreeFilterProvider()) {
+ treeFilterProvider.initialize(context);
+ assertFalse(treeFilterProvider.excludePackage(packageName));
+ }
+ }
+
+ private static JsonNode createDefaultConfiguration() {
+ ObjectNode configuration = JsonNodeFactory.instance.objectNode();
+ configuration.putObject("ignoredClasses")
+ .putArray("com.azure.")
+ .add("core.util.Configuration")
+ .add("cosmos.BridgeInternal")
+ .add("cosmos.CosmosBridgeInternal")
+ .add("cosmos.models.ModelBridgeInternal")
+ .add("cosmos.util.UtilBridgeInternal")
+ .add("spring.cloud.config.AppConfigurationBootstrapConfiguration")
+ .add("spring.cloud.config.AppConfigurationRefresh")
+ .add("spring.cloud.config.properties.AppConfigurationProviderProperties")
+ .add("spring.cloud.config.web.AppConfigurationEndpoint")
+ .add("spring.cloud.config.web.pushrefresh.AppConfigurationRefreshEvent");
+
+ ObjectNode ignoredPackages = configuration.putObject("ignoredPackages");
+ ignoredPackages.putArray("com.")
+ .add("azure.data.cosmos")
+ .add("fasterxml.jackson")
+ .add("google.gson")
+ .add("microsoft.azure")
+ .add("nimbusds");
+ ignoredPackages.putArray("io.")
+ .add("micrometer")
+ .add("netty")
+ .add("vertx");
+ ignoredPackages.putArray("javax.")
+ .add("jms")
+ .add("servlet");
+ ignoredPackages.putArray("kotlin");
+ ignoredPackages.putArray("okhttp3");
+ ignoredPackages.putArray("okio");
+ ignoredPackages.putArray("org.")
+ .add("apache.avro")
+ .add("apache.commons")
+ .add("apache.qpid")
+ .add("junit")
+ .add("reactivestreams")
+ .add("slf4j")
+ .add("springframework");
+ ignoredPackages.putArray("reactor.")
+ .add("core")
+ .add("netty")
+ .add("util");
+
+ configuration.putArray("ignoredPackagesPatterns")
+ .add("com\\.azure\\..*(implementation|samples).*")
+ .add("com\\.azure\\.resourcemanager\\..*(fluent)(\\..*)?$");
+
+ return configuration;
+ }
+}
diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt
index d8870096d251..3fd84877aaad 100644
--- a/eng/versioning/external_dependencies.txt
+++ b/eng/versioning/external_dependencies.txt
@@ -234,6 +234,9 @@ org.revapi:revapi-maven-plugin;0.14.6
# sdk\keyvault\microsoft-azure-keyvault-test\pom.xml
test_jar_com.microsoft.azure:azure-mgmt-resources;1.3.1-SNAPSHOT
+# Special test dependencies for clientcore integrations
+clientcore_dep_tests_org.slf4j:slf4j-simple;2.0.16
+
# everything under sdk\cosmos
cosmos_com.google.guava:guava;33.0.0-jre
cosmos_io.dropwizard.metrics:metrics-core;4.1.0
diff --git a/sdk/clientcore/optional-dependency-tests/pom.xml b/sdk/clientcore/optional-dependency-tests/pom.xml
index 4b241bec4626..1bbe325dffe4 100644
--- a/sdk/clientcore/optional-dependency-tests/pom.xml
+++ b/sdk/clientcore/optional-dependency-tests/pom.xml
@@ -70,7 +70,7 @@
org.slf4j
slf4j-simple
- 2.0.16
+ 2.0.16
test
diff --git a/sdk/parents/azure-client-sdk-parent/pom.xml b/sdk/parents/azure-client-sdk-parent/pom.xml
index 30d2b1f4df50..e9150052305b 100644
--- a/sdk/parents/azure-client-sdk-parent/pom.xml
+++ b/sdk/parents/azure-client-sdk-parent/pom.xml
@@ -1034,7 +1034,7 @@
- - azure-sdk-tree-provider
+ - class-and-package-tree-filter-provider
@@ -1055,6 +1055,11 @@
revapi-reporter-json
0.4.5
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.17.2
+
diff --git a/sdk/parents/clientcore-parent/pom.xml b/sdk/parents/clientcore-parent/pom.xml
index 799af97d104b..4617a1c40aa9 100644
--- a/sdk/parents/clientcore-parent/pom.xml
+++ b/sdk/parents/clientcore-parent/pom.xml
@@ -962,7 +962,7 @@
true
- revapi/revapi.json
+ revapi/clientcore-revapi.json
^\d+\.\d+\.\d+$
@@ -981,7 +981,7 @@
- - azure-sdk-tree-provider
+ - class-and-package-tree-filter-provider
@@ -1002,6 +1002,11 @@
revapi-reporter-json
0.4.5