From b613931346fc5e4f7a6cdf3ac53854fca7027702 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:16:01 +0000 Subject: [PATCH 01/12] Initial plan From d94cb68dd3359beb49552848cf9d6fbb44e4bcb1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:39:01 +0000 Subject: [PATCH 02/12] Add SerializableMethodsCheck for JsonSerializable and XmlSerializable validation Co-authored-by: srnagar <51379715+srnagar@users.noreply.github.com> --- .../checkstyle/clientcore/checkstyle.xml | 4 + .../checkstyle/track2/checkstyle.xml | 4 + .../checkstyle/vnext/checkstyle.xml | 4 + .../checks/SerializableMethodsCheck.java | 171 ++++++++++++++ .../checks/SerializableMethodsCheckTest.java | 212 ++++++++++++++++++ 5 files changed, 395 insertions(+) create mode 100644 sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java create mode 100644 sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java diff --git a/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml b/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml index 18abe9430410..b85291b9e9eb 100644 --- a/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml +++ b/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml @@ -426,5 +426,9 @@ + + + + diff --git a/eng/lintingconfigs/checkstyle/track2/checkstyle.xml b/eng/lintingconfigs/checkstyle/track2/checkstyle.xml index 6c6c6082878e..8717a8d674e3 100644 --- a/eng/lintingconfigs/checkstyle/track2/checkstyle.xml +++ b/eng/lintingconfigs/checkstyle/track2/checkstyle.xml @@ -420,5 +420,9 @@ + + + + diff --git a/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml b/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml index 6432e8cc91ba..1519ce4b7134 100644 --- a/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml +++ b/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml @@ -425,5 +425,9 @@ + + + + diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java new file mode 100644 index 000000000000..2614323041b3 --- /dev/null +++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package io.clientcore.linting.extensions.checkstyle.checks; + +import com.puppycrawl.tools.checkstyle.api.AbstractCheck; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; + +import java.util.ArrayList; +import java.util.List; + +/** + * Validates serialization method completeness for JsonSerializable and XmlSerializable implementations. + */ +public class SerializableMethodsCheck extends AbstractCheck { + static final String ERR_NO_TO_JSON = "Class implementing JsonSerializable must provide a toJson method."; + static final String ERR_NO_FROM_JSON = "Class implementing JsonSerializable must provide a static fromJson method."; + static final String ERR_NO_TO_XML = "Class implementing XmlSerializable must provide a toXml method."; + static final String ERR_NO_FROM_XML = "Class implementing XmlSerializable must provide a static fromXml method."; + + private List snapshotArchive; + + @Override + public int[] getDefaultTokens() { + return getRequiredTokens(); + } + + @Override + public int[] getAcceptableTokens() { + return getRequiredTokens(); + } + + @Override + public int[] getRequiredTokens() { + return new int[] { TokenTypes.CLASS_DEF, TokenTypes.METHOD_DEF }; + } + + @Override + public void beginTree(DetailAST rootNode) { + snapshotArchive = new ArrayList<>(); + } + + @Override + public void visitToken(DetailAST currentNode) { + int tokenKind = currentNode.getType(); + + if (tokenKind == TokenTypes.CLASS_DEF) { + TypeSnapshot snapshot = captureTypeSnapshot(currentNode); + snapshotArchive.add(snapshot); + } else if (tokenKind == TokenTypes.METHOD_DEF) { + integrateMethodIntoSnapshot(currentNode); + } + } + + @Override + public void leaveToken(DetailAST currentNode) { + if (currentNode.getType() == TokenTypes.CLASS_DEF) { + performSnapshotAudit(currentNode); + } + } + + private TypeSnapshot captureTypeSnapshot(DetailAST classNode) { + TypeSnapshot snapshot = new TypeSnapshot(); + snapshot.classNode = classNode; + + DetailAST interfaceSection = classNode.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE); + if (interfaceSection != null) { + digestInterfaceSection(interfaceSection, snapshot); + } + + return snapshot; + } + + private void digestInterfaceSection(DetailAST interfaceSection, TypeSnapshot snapshot) { + DetailAST cursor = interfaceSection.getFirstChild(); + + while (cursor != null) { + if (cursor.getType() == TokenTypes.IDENT) { + String interfaceLabel = cursor.getText(); + + if ("JsonSerializable".equals(interfaceLabel)) { + snapshot.mandatesJson = true; + } else if ("XmlSerializable".equals(interfaceLabel)) { + snapshot.mandatesXml = true; + } + } + cursor = cursor.getNextSibling(); + } + } + + private void integrateMethodIntoSnapshot(DetailAST methodNode) { + if (snapshotArchive.isEmpty()) { + return; + } + + TypeSnapshot latestSnapshot = snapshotArchive.get(snapshotArchive.size() - 1); + + if (!latestSnapshot.mandatesJson && !latestSnapshot.mandatesXml) { + return; + } + + DetailAST nameNode = methodNode.findFirstToken(TokenTypes.IDENT); + if (nameNode == null) { + return; + } + + String methodLabel = nameNode.getText(); + boolean markedStatic = probeForStaticMarker(methodNode); + + latestSnapshot.digestMethod(methodLabel, markedStatic); + } + + private boolean probeForStaticMarker(DetailAST methodNode) { + DetailAST modifierBlock = methodNode.findFirstToken(TokenTypes.MODIFIERS); + + if (modifierBlock == null) { + return false; + } + + return modifierBlock.findFirstToken(TokenTypes.LITERAL_STATIC) != null; + } + + private void performSnapshotAudit(DetailAST classNode) { + if (snapshotArchive.isEmpty()) { + return; + } + + TypeSnapshot snapshot = snapshotArchive.remove(snapshotArchive.size() - 1); + + if (snapshot.mandatesJson) { + if (!snapshot.observedToJson) { + log(classNode, ERR_NO_TO_JSON); + } + if (!snapshot.observedFromJson) { + log(classNode, ERR_NO_FROM_JSON); + } + } + + if (snapshot.mandatesXml) { + if (!snapshot.observedToXml) { + log(classNode, ERR_NO_TO_XML); + } + if (!snapshot.observedFromXml) { + log(classNode, ERR_NO_FROM_XML); + } + } + } + + private static class TypeSnapshot { + DetailAST classNode; + boolean mandatesJson; + boolean mandatesXml; + boolean observedToJson; + boolean observedFromJson; + boolean observedToXml; + boolean observedFromXml; + + void digestMethod(String methodLabel, boolean markedStatic) { + if ("toJson".equals(methodLabel)) { + observedToJson = true; + } else if ("fromJson".equals(methodLabel) && markedStatic) { + observedFromJson = true; + } else if ("toXml".equals(methodLabel)) { + observedToXml = true; + } else if ("fromXml".equals(methodLabel) && markedStatic) { + observedFromXml = true; + } + } + } +} diff --git a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java new file mode 100644 index 000000000000..2bab72be5120 --- /dev/null +++ b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package io.clientcore.linting.extensions.checkstyle.checks; + +import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport; +import com.puppycrawl.tools.checkstyle.Checker; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_FROM_JSON; +import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_FROM_XML; +import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_TO_JSON; +import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_TO_XML; + +/** + * Tests {@link SerializableMethodsCheck}. + */ +public class SerializableMethodsCheckTest extends AbstractModuleTestSupport { + private Checker lintingChecker; + + @BeforeEach + public void setupChecker() throws Exception { + lintingChecker = createChecker(createModuleConfig(SerializableMethodsCheck.class)); + } + + @AfterEach + public void teardownChecker() { + lintingChecker.destroy(); + } + + @Override + protected String getPackageLocation() { + return "io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck"; + } + + @Test + public void jsonSerializableWithBothMethods() throws Exception { + File testFile = TestUtils.createCheckFile("jsonComplete", "package com.azure;", + "public class JsonComplete implements JsonSerializable {", " public void toJson() {}", + " public static JsonComplete fromJson() { return null; }", "}"); + + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void jsonSerializableMissingToJson() throws Exception { + File testFile = TestUtils.createCheckFile("jsonMissingTo", "package com.azure;", + "public class JsonMissingTo implements JsonSerializable {", + " public static JsonMissingTo fromJson() { return null; }", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_TO_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void jsonSerializableMissingFromJson() throws Exception { + File testFile = TestUtils.createCheckFile("jsonMissingFrom", "package com.azure;", + "public class JsonMissingFrom implements JsonSerializable {", " public void toJson() {}", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void jsonSerializableMissingBothMethods() throws Exception { + File testFile = TestUtils.createCheckFile("jsonMissingBoth", "package com.azure;", + "public class JsonMissingBoth implements JsonSerializable {", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_TO_JSON, "2:1: " + ERR_NO_FROM_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void jsonSerializableWithNonStaticFromJson() throws Exception { + File testFile = TestUtils.createCheckFile("jsonNonStaticFrom", "package com.azure;", + "public class JsonNonStaticFrom implements JsonSerializable {", " public void toJson() {}", + " public JsonNonStaticFrom fromJson() { return null; }", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void xmlSerializableWithBothMethods() throws Exception { + File testFile = TestUtils.createCheckFile("xmlComplete", "package com.azure;", + "public class XmlComplete implements XmlSerializable {", " public void toXml() {}", + " public static XmlComplete fromXml() { return null; }", "}"); + + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void xmlSerializableMissingToXml() throws Exception { + File testFile = TestUtils.createCheckFile("xmlMissingTo", "package com.azure;", + "public class XmlMissingTo implements XmlSerializable {", + " public static XmlMissingTo fromXml() { return null; }", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_TO_XML }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void xmlSerializableMissingFromXml() throws Exception { + File testFile = TestUtils.createCheckFile("xmlMissingFrom", "package com.azure;", + "public class XmlMissingFrom implements XmlSerializable {", " public void toXml() {}", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_XML }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void xmlSerializableMissingBothMethods() throws Exception { + File testFile = TestUtils.createCheckFile("xmlMissingBoth", "package com.azure;", + "public class XmlMissingBoth implements XmlSerializable {", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_TO_XML, "2:1: " + ERR_NO_FROM_XML }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void xmlSerializableWithNonStaticFromXml() throws Exception { + File testFile = TestUtils.createCheckFile("xmlNonStaticFrom", "package com.azure;", + "public class XmlNonStaticFrom implements XmlSerializable {", " public void toXml() {}", + " public XmlNonStaticFrom fromXml() { return null; }", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_XML }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void bothInterfacesWithAllMethods() throws Exception { + File testFile = TestUtils.createCheckFile("bothComplete", "package com.azure;", + "public class BothComplete implements JsonSerializable, XmlSerializable {", " public void toJson() {}", + " public static BothComplete fromJson() { return null; }", " public void toXml() {}", + " public static BothComplete fromXml() { return null; }", "}"); + + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void bothInterfacesMissingAllMethods() throws Exception { + File testFile = TestUtils.createCheckFile("bothMissing", "package com.azure;", + "public class BothMissing implements JsonSerializable, XmlSerializable {", "}"); + + String[] expectedErrors = { + "2:1: " + ERR_NO_TO_JSON, + "2:1: " + ERR_NO_FROM_JSON, + "2:1: " + ERR_NO_TO_XML, + "2:1: " + ERR_NO_FROM_XML }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void bothInterfacesMissingJsonMethods() throws Exception { + File testFile = TestUtils.createCheckFile("bothMissingJson", "package com.azure;", + "public class BothMissingJson implements JsonSerializable, XmlSerializable {", " public void toXml() {}", + " public static BothMissingJson fromXml() { return null; }", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_TO_JSON, "2:1: " + ERR_NO_FROM_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void bothInterfacesMissingXmlMethods() throws Exception { + File testFile = TestUtils.createCheckFile("bothMissingXml", "package com.azure;", + "public class BothMissingXml implements JsonSerializable, XmlSerializable {", " public void toJson() {}", + " public static BothMissingXml fromJson() { return null; }", "}"); + + String[] expectedErrors = { "2:1: " + ERR_NO_TO_XML, "2:1: " + ERR_NO_FROM_XML }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void classNotImplementingInterface() throws Exception { + File testFile = TestUtils.createCheckFile("noInterface", "package com.azure;", "public class NoInterface {", + " public void someMethod() {}", "}"); + + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void nestedClassWithJsonSerializable() throws Exception { + File testFile = TestUtils.createCheckFile("nestedJson", "package com.azure;", "public class OuterClass {", + " public static class InnerClass implements JsonSerializable {", " public void toJson() {}", + " public static InnerClass fromJson() { return null; }", " }", "}"); + + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void nestedClassMissingMethods() throws Exception { + File testFile = TestUtils.createCheckFile("nestedMissing", "package com.azure;", "public class OuterClass {", + " public static class InnerClass implements JsonSerializable {", " }", "}"); + + String[] expectedErrors = { "3:5: " + ERR_NO_TO_JSON, "3:5: " + ERR_NO_FROM_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void classWithExtraMethodsAndCorrectSerializationMethods() throws Exception { + File testFile = TestUtils.createCheckFile("extraMethods", "package com.azure;", + "public class ExtraMethods implements JsonSerializable {", " public void toJson() {}", + " public static ExtraMethods fromJson() { return null; }", " public void otherMethod() {}", + " public String getData() { return null; }", "}"); + + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } +} From a2d7f4ada3777ad1d59160ce1024f203b6452ffb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:40:05 +0000 Subject: [PATCH 03/12] Fix duplicate comment headers in checkstyle configurations Co-authored-by: srnagar <51379715+srnagar@users.noreply.github.com> --- eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml | 1 - eng/lintingconfigs/checkstyle/track2/checkstyle.xml | 1 - eng/lintingconfigs/checkstyle/vnext/checkstyle.xml | 1 - 3 files changed, 3 deletions(-) diff --git a/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml b/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml index b85291b9e9eb..e5f2cafd3123 100644 --- a/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml +++ b/eng/lintingconfigs/checkstyle/clientcore/checkstyle.xml @@ -427,7 +427,6 @@ - diff --git a/eng/lintingconfigs/checkstyle/track2/checkstyle.xml b/eng/lintingconfigs/checkstyle/track2/checkstyle.xml index 8717a8d674e3..ea5bc3edb785 100644 --- a/eng/lintingconfigs/checkstyle/track2/checkstyle.xml +++ b/eng/lintingconfigs/checkstyle/track2/checkstyle.xml @@ -421,7 +421,6 @@ - diff --git a/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml b/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml index 1519ce4b7134..efd19a736c8a 100644 --- a/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml +++ b/eng/lintingconfigs/checkstyle/vnext/checkstyle.xml @@ -426,7 +426,6 @@ - From aa318e612abc252c0c6f2ec229a1084e336d706f Mon Sep 17 00:00:00 2001 From: Srikanta Date: Fri, 6 Feb 2026 13:51:59 -0800 Subject: [PATCH 04/12] Update SerializableMethodsCheck and tests --- eng/versioning/version_client.txt | 3 +- .../azure-client-sdk-parent-v2/pom.xml | 2 +- sdk/parents/azure-client-sdk-parent/pom.xml | 2 +- sdk/parents/azure-data-sdk-parent/pom.xml | 2 +- sdk/parents/clientcore-parent/pom.xml | 2 +- .../checks/SerializableMethodsCheck.java | 44 +++++----------- .../checks/SerializableMethodsCheckTest.java | 52 ++----------------- 7 files changed, 24 insertions(+), 83 deletions(-) diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 2c462f524407..c5514f8196c6 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -550,11 +550,12 @@ io.clientcore:optional-dependency-tests;1.0.0-beta.1;1.0.0-beta.1 # In the pom, the version update tag after the version should name the unreleased package and the dependency version: # +unreleased_com.azure.resourcemanager:azure-resourcemanager-containerregistry;2.55.0 unreleased_com.azure.v2:azure-core;2.0.0-beta.1 unreleased_com.azure.v2:azure-identity;2.0.0-beta.1 unreleased_com.azure.v2:azure-data-appconfiguration;2.0.0-beta.1 unreleased_io.clientcore:http-netty4;1.0.0-beta.1 -unreleased_com.azure.resourcemanager:azure-resourcemanager-containerregistry;2.55.0 +unreleased_io.clientcore:linting-extensions;1.0.0-beta.2 # Released Beta dependencies: Copy the entry from above, prepend "beta_", remove the current # version and set the version to the released beta. Released beta dependencies are only valid diff --git a/sdk/parents/azure-client-sdk-parent-v2/pom.xml b/sdk/parents/azure-client-sdk-parent-v2/pom.xml index a8ee8c08ff77..5d38ac091892 100644 --- a/sdk/parents/azure-client-sdk-parent-v2/pom.xml +++ b/sdk/parents/azure-client-sdk-parent-v2/pom.xml @@ -708,7 +708,7 @@ io.clientcore linting-extensions - 1.0.0-beta.1 + 1.0.0-beta.2 com.puppycrawl.tools diff --git a/sdk/parents/azure-client-sdk-parent/pom.xml b/sdk/parents/azure-client-sdk-parent/pom.xml index 680523d726fd..d4bb259f2310 100644 --- a/sdk/parents/azure-client-sdk-parent/pom.xml +++ b/sdk/parents/azure-client-sdk-parent/pom.xml @@ -720,7 +720,7 @@ io.clientcore linting-extensions - 1.0.0-beta.1 + 1.0.0-beta.2 com.puppycrawl.tools diff --git a/sdk/parents/azure-data-sdk-parent/pom.xml b/sdk/parents/azure-data-sdk-parent/pom.xml index 4288c6d213a7..216d5c5b2bf4 100644 --- a/sdk/parents/azure-data-sdk-parent/pom.xml +++ b/sdk/parents/azure-data-sdk-parent/pom.xml @@ -78,7 +78,7 @@ io.clientcore linting-extensions - 1.0.0-beta.1 + 1.0.0-beta.2 com.puppycrawl.tools diff --git a/sdk/parents/clientcore-parent/pom.xml b/sdk/parents/clientcore-parent/pom.xml index 333fe1c6d6aa..83ef7489e96a 100644 --- a/sdk/parents/clientcore-parent/pom.xml +++ b/sdk/parents/clientcore-parent/pom.xml @@ -788,7 +788,7 @@ io.clientcore linting-extensions - 1.0.0-beta.1 + 1.0.0-beta.2 com.puppycrawl.tools diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java index 2614323041b3..a33802000cf2 100644 --- a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java +++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java @@ -11,16 +11,17 @@ import java.util.List; /** - * Validates serialization method completeness for JsonSerializable and XmlSerializable implementations. + * Verifies that classes implementing JsonSerializable or XmlSerializable provide a static fromJson or fromXml method. */ public class SerializableMethodsCheck extends AbstractCheck { - static final String ERR_NO_TO_JSON = "Class implementing JsonSerializable must provide a toJson method."; static final String ERR_NO_FROM_JSON = "Class implementing JsonSerializable must provide a static fromJson method."; - static final String ERR_NO_TO_XML = "Class implementing XmlSerializable must provide a toXml method."; static final String ERR_NO_FROM_XML = "Class implementing XmlSerializable must provide a static fromXml method."; private List snapshotArchive; + public SerializableMethodsCheck() { + } + @Override public int[] getDefaultTokens() { return getRequiredTokens(); @@ -78,11 +79,10 @@ private void digestInterfaceSection(DetailAST interfaceSection, TypeSnapshot sna while (cursor != null) { if (cursor.getType() == TokenTypes.IDENT) { String interfaceLabel = cursor.getText(); - if ("JsonSerializable".equals(interfaceLabel)) { - snapshot.mandatesJson = true; + snapshot.implementsJsonSerializable = true; } else if ("XmlSerializable".equals(interfaceLabel)) { - snapshot.mandatesXml = true; + snapshot.implementsXmlSerializable = true; } } cursor = cursor.getNextSibling(); @@ -96,7 +96,7 @@ private void integrateMethodIntoSnapshot(DetailAST methodNode) { TypeSnapshot latestSnapshot = snapshotArchive.get(snapshotArchive.size() - 1); - if (!latestSnapshot.mandatesJson && !latestSnapshot.mandatesXml) { + if (!latestSnapshot.implementsJsonSerializable && !latestSnapshot.implementsXmlSerializable) { return; } @@ -128,41 +128,25 @@ private void performSnapshotAudit(DetailAST classNode) { TypeSnapshot snapshot = snapshotArchive.remove(snapshotArchive.size() - 1); - if (snapshot.mandatesJson) { - if (!snapshot.observedToJson) { - log(classNode, ERR_NO_TO_JSON); - } - if (!snapshot.observedFromJson) { - log(classNode, ERR_NO_FROM_JSON); - } + if (snapshot.implementsJsonSerializable && !snapshot.observedFromJson) { + log(classNode, ERR_NO_FROM_JSON); } - if (snapshot.mandatesXml) { - if (!snapshot.observedToXml) { - log(classNode, ERR_NO_TO_XML); - } - if (!snapshot.observedFromXml) { - log(classNode, ERR_NO_FROM_XML); - } + if (snapshot.implementsXmlSerializable && !snapshot.observedFromXml) { + log(classNode, ERR_NO_FROM_XML); } } private static class TypeSnapshot { DetailAST classNode; - boolean mandatesJson; - boolean mandatesXml; - boolean observedToJson; + boolean implementsJsonSerializable; + boolean implementsXmlSerializable; boolean observedFromJson; - boolean observedToXml; boolean observedFromXml; void digestMethod(String methodLabel, boolean markedStatic) { - if ("toJson".equals(methodLabel)) { - observedToJson = true; - } else if ("fromJson".equals(methodLabel) && markedStatic) { + if ("fromJson".equals(methodLabel) && markedStatic) { observedFromJson = true; - } else if ("toXml".equals(methodLabel)) { - observedToXml = true; } else if ("fromXml".equals(methodLabel) && markedStatic) { observedFromXml = true; } diff --git a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java index 2bab72be5120..1dacb8e1cb1e 100644 --- a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java +++ b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java @@ -13,8 +13,6 @@ import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_FROM_JSON; import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_FROM_XML; -import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_TO_JSON; -import static io.clientcore.linting.extensions.checkstyle.checks.SerializableMethodsCheck.ERR_NO_TO_XML; /** * Tests {@link SerializableMethodsCheck}. @@ -46,16 +44,6 @@ public void jsonSerializableWithBothMethods() throws Exception { verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); } - @Test - public void jsonSerializableMissingToJson() throws Exception { - File testFile = TestUtils.createCheckFile("jsonMissingTo", "package com.azure;", - "public class JsonMissingTo implements JsonSerializable {", - " public static JsonMissingTo fromJson() { return null; }", "}"); - - String[] expectedErrors = { "2:1: " + ERR_NO_TO_JSON }; - verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); - } - @Test public void jsonSerializableMissingFromJson() throws Exception { File testFile = TestUtils.createCheckFile("jsonMissingFrom", "package com.azure;", @@ -65,15 +53,6 @@ public void jsonSerializableMissingFromJson() throws Exception { verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); } - @Test - public void jsonSerializableMissingBothMethods() throws Exception { - File testFile = TestUtils.createCheckFile("jsonMissingBoth", "package com.azure;", - "public class JsonMissingBoth implements JsonSerializable {", "}"); - - String[] expectedErrors = { "2:1: " + ERR_NO_TO_JSON, "2:1: " + ERR_NO_FROM_JSON }; - verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); - } - @Test public void jsonSerializableWithNonStaticFromJson() throws Exception { File testFile = TestUtils.createCheckFile("jsonNonStaticFrom", "package com.azure;", @@ -93,16 +72,6 @@ public void xmlSerializableWithBothMethods() throws Exception { verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); } - @Test - public void xmlSerializableMissingToXml() throws Exception { - File testFile = TestUtils.createCheckFile("xmlMissingTo", "package com.azure;", - "public class XmlMissingTo implements XmlSerializable {", - " public static XmlMissingTo fromXml() { return null; }", "}"); - - String[] expectedErrors = { "2:1: " + ERR_NO_TO_XML }; - verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); - } - @Test public void xmlSerializableMissingFromXml() throws Exception { File testFile = TestUtils.createCheckFile("xmlMissingFrom", "package com.azure;", @@ -112,15 +81,6 @@ public void xmlSerializableMissingFromXml() throws Exception { verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); } - @Test - public void xmlSerializableMissingBothMethods() throws Exception { - File testFile = TestUtils.createCheckFile("xmlMissingBoth", "package com.azure;", - "public class XmlMissingBoth implements XmlSerializable {", "}"); - - String[] expectedErrors = { "2:1: " + ERR_NO_TO_XML, "2:1: " + ERR_NO_FROM_XML }; - verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); - } - @Test public void xmlSerializableWithNonStaticFromXml() throws Exception { File testFile = TestUtils.createCheckFile("xmlNonStaticFrom", "package com.azure;", @@ -146,11 +106,7 @@ public void bothInterfacesMissingAllMethods() throws Exception { File testFile = TestUtils.createCheckFile("bothMissing", "package com.azure;", "public class BothMissing implements JsonSerializable, XmlSerializable {", "}"); - String[] expectedErrors = { - "2:1: " + ERR_NO_TO_JSON, - "2:1: " + ERR_NO_FROM_JSON, - "2:1: " + ERR_NO_TO_XML, - "2:1: " + ERR_NO_FROM_XML }; + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON, "2:1: " + ERR_NO_FROM_XML }; verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); } @@ -160,7 +116,7 @@ public void bothInterfacesMissingJsonMethods() throws Exception { "public class BothMissingJson implements JsonSerializable, XmlSerializable {", " public void toXml() {}", " public static BothMissingJson fromXml() { return null; }", "}"); - String[] expectedErrors = { "2:1: " + ERR_NO_TO_JSON, "2:1: " + ERR_NO_FROM_JSON }; + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON }; verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); } @@ -170,7 +126,7 @@ public void bothInterfacesMissingXmlMethods() throws Exception { "public class BothMissingXml implements JsonSerializable, XmlSerializable {", " public void toJson() {}", " public static BothMissingXml fromJson() { return null; }", "}"); - String[] expectedErrors = { "2:1: " + ERR_NO_TO_XML, "2:1: " + ERR_NO_FROM_XML }; + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_XML }; verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); } @@ -196,7 +152,7 @@ public void nestedClassMissingMethods() throws Exception { File testFile = TestUtils.createCheckFile("nestedMissing", "package com.azure;", "public class OuterClass {", " public static class InnerClass implements JsonSerializable {", " }", "}"); - String[] expectedErrors = { "3:5: " + ERR_NO_TO_JSON, "3:5: " + ERR_NO_FROM_JSON }; + String[] expectedErrors = { "3:5: " + ERR_NO_FROM_JSON }; verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); } From 52fb45599ee2b098ca957ace827b6ab934344152 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Fri, 6 Feb 2026 15:08:30 -0800 Subject: [PATCH 05/12] Update ServiceClientCheck --- .../checkstyle/checks/ServiceClientCheck.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java index e39617454a2e..6a1a3d5727f2 100644 --- a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java +++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java @@ -46,6 +46,7 @@ public class ServiceClientCheck extends AbstractCheck { private static final String IS_ASYNC = "isAsync"; private static final String CONTEXT = "Context"; private static final String REQUEST_OPTIONS = "RequestOptions"; + private static final String REQUEST_CONTEXT = "RequestContext"; private static final String RESPONSE_BRACKET = "Response<"; private static final String MONO_BRACKET = "Mono<"; @@ -77,7 +78,7 @@ public class ServiceClientCheck extends AbstractCheck { private static final String ASYNC_CONTEXT_ERROR = "Asynchronous method with annotation @ServiceMethod must not have ''%s'' as a method parameter."; private static final String SYNC_CONTEXT_ERROR - = "Synchronous method with annotation @ServiceMethod must have ''%s'' or ''%s'' as a method parameter."; + = "Synchronous method with annotation @ServiceMethod must have ''%s'', ''%s'' or ''%s'' as a method parameter."; // Add all imported classes into a map, key is the name of class and value is the full package path of class. private final Map simpleClassNameToQualifiedNameMap = new HashMap<>(); @@ -399,6 +400,15 @@ private void checkContextInRightPlace(DetailAST methodDefToken) { return paramTypeIdentToken != null && REQUEST_OPTIONS.equals(paramTypeIdentToken.getText()); }).isPresent(); + boolean containsRequestContextParameter = TokenUtil.findFirstTokenByPredicate(parametersToken, parameterToken -> { + if (parameterToken.getType() != TokenTypes.PARAMETER_DEF) { + return false; + } + final DetailAST paramTypeIdentToken + = parameterToken.findFirstToken(TokenTypes.TYPE).findFirstToken(TokenTypes.IDENT); + return paramTypeIdentToken != null && REQUEST_CONTEXT.equals(paramTypeIdentToken.getText()); + }).isPresent(); + if (containsContextParameter) { // MONO and PagedFlux return type implies Asynchronous method if (returnType.startsWith(MONO_BRACKET) @@ -406,11 +416,11 @@ private void checkContextInRightPlace(DetailAST methodDefToken) { || returnType.startsWith(POLLER_FLUX_BRACKET)) { log(methodDefToken, String.format(ASYNC_CONTEXT_ERROR, CONTEXT)); } - } else if (!containsRequestOptionsParameter) { + } else if (!(containsRequestOptionsParameter || containsRequestContextParameter)) { // Context or RequestOptions should be passed in as an argument to all public methods // annotated with @ServiceMethod that return Response in sync clients. if (returnType.startsWith(RESPONSE_BRACKET)) { - log(methodDefToken, String.format(SYNC_CONTEXT_ERROR, CONTEXT, REQUEST_OPTIONS)); + log(methodDefToken, String.format(SYNC_CONTEXT_ERROR, CONTEXT, REQUEST_OPTIONS, REQUEST_CONTEXT)); } } } From b72b072c8405e4ced2ba411c59ad4c16818f36f0 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Mon, 9 Feb 2026 12:53:05 -0800 Subject: [PATCH 06/12] fix formatting and checkstyle suppression package names --- .../checkstyle-suppressions.xml | 24 +++++++++---------- .../checkstyle-suppressions.xml | 8 +++---- .../checkstyle-suppressions.xml | 16 ++++++------- .../checkstyle-suppressions.xml | 8 +++---- .../checkstyle/checks/ServiceClientCheck.java | 17 ++++++------- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml index b1b2b42206a4..787e7efdd2bf 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml @@ -3,22 +3,22 @@ - - - - - - + + + + + + - + - + - - + + - - + + diff --git a/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml index c3ba6b3d5de1..6097fdbba565 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml @@ -5,12 +5,12 @@ - - + + - + - + diff --git a/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml index e4db86f0597a..f0c1823373aa 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml @@ -3,17 +3,17 @@ - - - - + + + + - + - + - - + + diff --git a/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml index b564c4aedda0..60c9a45a481f 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml @@ -3,12 +3,12 @@ - - + + - + - + diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java index 6a1a3d5727f2..a1e378620f23 100644 --- a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java +++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/ServiceClientCheck.java @@ -400,14 +400,15 @@ private void checkContextInRightPlace(DetailAST methodDefToken) { return paramTypeIdentToken != null && REQUEST_OPTIONS.equals(paramTypeIdentToken.getText()); }).isPresent(); - boolean containsRequestContextParameter = TokenUtil.findFirstTokenByPredicate(parametersToken, parameterToken -> { - if (parameterToken.getType() != TokenTypes.PARAMETER_DEF) { - return false; - } - final DetailAST paramTypeIdentToken - = parameterToken.findFirstToken(TokenTypes.TYPE).findFirstToken(TokenTypes.IDENT); - return paramTypeIdentToken != null && REQUEST_CONTEXT.equals(paramTypeIdentToken.getText()); - }).isPresent(); + boolean containsRequestContextParameter + = TokenUtil.findFirstTokenByPredicate(parametersToken, parameterToken -> { + if (parameterToken.getType() != TokenTypes.PARAMETER_DEF) { + return false; + } + final DetailAST paramTypeIdentToken + = parameterToken.findFirstToken(TokenTypes.TYPE).findFirstToken(TokenTypes.IDENT); + return paramTypeIdentToken != null && REQUEST_CONTEXT.equals(paramTypeIdentToken.getText()); + }).isPresent(); if (containsContextParameter) { // MONO and PagedFlux return type implies Asynchronous method From 3718f4fe585c81d9c97ead2d7b30fe0201ae3533 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Mon, 9 Feb 2026 15:21:20 -0800 Subject: [PATCH 07/12] fix reported checkstyle errors --- .../checkstyle-suppressions.xml | 2 + .../checkstyle-suppressions.xml | 2 +- .../checkstyle-suppressions.xml | 2 +- .../checkstyle-suppressions.xml | 2 +- .../model/QuickPulseMonitoringDataPoints.java | 6 ++ .../stainless/FunctionCallingAsyncSample.java | 11 ++- .../stainless/FunctionCallingSample.java | 11 ++- .../stainless/OpenAIOkHttpClientTestBase.java | 22 ++++- .../checks/SerializableMethodsCheck.java | 46 ++++++++++ .../checks/SerializableMethodsCheckTest.java | 89 +++++++++++++++++++ 10 files changed, 186 insertions(+), 7 deletions(-) diff --git a/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml index 787e7efdd2bf..6929837bf10e 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-administration/checkstyle-suppressions.xml @@ -16,9 +16,11 @@ + + diff --git a/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml index 6097fdbba565..4be50bb27716 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-certificates/checkstyle-suppressions.xml @@ -11,6 +11,6 @@ - + diff --git a/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml index f0c1823373aa..f5dc6622661d 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-keys/checkstyle-suppressions.xml @@ -11,7 +11,7 @@ - + diff --git a/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml b/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml index 60c9a45a481f..9a31b590b18b 100644 --- a/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml +++ b/sdk/keyvault-v2/azure-security-keyvault-secrets/checkstyle-suppressions.xml @@ -9,6 +9,6 @@ - + diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java index f22590bb5c9e..60ed7a3654f5 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/quickpulse/model/QuickPulseMonitoringDataPoints.java @@ -1,5 +1,6 @@ package com.azure.monitor.opentelemetry.exporter.implementation.quickpulse.model; +import com.azure.json.JsonReader; import com.azure.json.JsonSerializable; import com.azure.json.JsonWriter; @@ -17,4 +18,9 @@ public QuickPulseMonitoringDataPoints(List monitoringDataPoi public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { return jsonWriter.writeArray(monitoringDataPoints, JsonWriter::writeJson, false); } + + public static QuickPulseMonitoringDataPoints fromJson(JsonReader jsonReader) throws IOException { + List dataPoints = jsonReader.readArray(QuickPulseEnvelope::fromJson); + return new QuickPulseMonitoringDataPoints(dataPoints); + } } diff --git a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java index 3d6974d6da83..3d92143391f1 100644 --- a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java +++ b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingAsyncSample.java @@ -25,7 +25,16 @@ @JsonClassDescription("Gets the quality of the given SDK.") class GetSdkQualityAsync { @JsonPropertyDescription("The name of the SDK.") - public String name; + private String name; + + public String getName() { + return name; + } + + public GetSdkQualityAsync setName(String name) { + this.name = name; + return this; + } public String execute() { return name.contains("OpenAI") ? "Excellent quality and robust implementation!" : "Unknown quality"; diff --git a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java index 8b432c4750a9..c68dac190df3 100644 --- a/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java +++ b/sdk/openai/azure-ai-openai-stainless/src/samples/java/com/azure/ai/openai/stainless/FunctionCallingSample.java @@ -25,7 +25,16 @@ @JsonClassDescription("Gets the quality of the given SDK.") class GetSdkQuality { @JsonPropertyDescription("The name of the SDK.") - public String name; + private String name; + + public String getName() { + return name; + } + + public GetSdkQuality setName(String name) { + this.name = name; + return this; + } public String execute() { return name.contains("OpenAI") ? "Excellent quality and robust implementation!" : "Unknown quality"; diff --git a/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java b/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java index 6283d2fa2810..68d603730b60 100644 --- a/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java +++ b/sdk/openai/azure-ai-openai-stainless/src/test/java/com/azure/ai/openai/stainless/OpenAIOkHttpClientTestBase.java @@ -69,10 +69,28 @@ @JsonClassDescription("Get the current weather in a given location") class GetCurrentWeather { @JsonPropertyDescription("The city and state, e.g. San Francisco, CA") - public String location; + private String location; @JsonPropertyDescription("Temperature unit (celsius or fahrenheit)") - public String unit = "celsius"; + private String unit = "celsius"; + + public String getLocation() { + return location; + } + + public GetCurrentWeather setLocation(String location) { + this.location = location; + return this; + } + + public String getUnit() { + return unit; + } + + public GetCurrentWeather setUnit(String unit) { + this.unit = unit; + return this; + } public String execute() { return "The weather in " + location + " is 72 degrees " + unit; diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java index a33802000cf2..c7f75673a8b1 100644 --- a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java +++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java @@ -65,6 +65,15 @@ private TypeSnapshot captureTypeSnapshot(DetailAST classNode) { TypeSnapshot snapshot = new TypeSnapshot(); snapshot.classNode = classNode; + // Check if the class is abstract - skip validation for abstract types + snapshot.isAbstract = isAbstractType(classNode); + + // Check if the class extends JsonSerializable or XmlSerializable + DetailAST extendsSection = classNode.findFirstToken(TokenTypes.EXTENDS_CLAUSE); + if (extendsSection != null) { + digestExtendsSection(extendsSection, snapshot); + } + DetailAST interfaceSection = classNode.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE); if (interfaceSection != null) { digestInterfaceSection(interfaceSection, snapshot); @@ -73,6 +82,30 @@ private TypeSnapshot captureTypeSnapshot(DetailAST classNode) { return snapshot; } + private boolean isAbstractType(DetailAST classNode) { + DetailAST modifierBlock = classNode.findFirstToken(TokenTypes.MODIFIERS); + if (modifierBlock == null) { + return false; + } + return modifierBlock.findFirstToken(TokenTypes.ABSTRACT) != null; + } + + private void digestExtendsSection(DetailAST extendsSection, TypeSnapshot snapshot) { + DetailAST cursor = extendsSection.getFirstChild(); + + while (cursor != null) { + if (cursor.getType() == TokenTypes.IDENT) { + String parentLabel = cursor.getText(); + if ("JsonSerializable".equals(parentLabel)) { + snapshot.extendsJsonSerializable = true; + } else if ("XmlSerializable".equals(parentLabel)) { + snapshot.extendsXmlSerializable = true; + } + } + cursor = cursor.getNextSibling(); + } + } + private void digestInterfaceSection(DetailAST interfaceSection, TypeSnapshot snapshot) { DetailAST cursor = interfaceSection.getFirstChild(); @@ -128,6 +161,16 @@ private void performSnapshotAudit(DetailAST classNode) { TypeSnapshot snapshot = snapshotArchive.remove(snapshotArchive.size() - 1); + // Skip validation for abstract types + if (snapshot.isAbstract) { + return; + } + + // Skip validation for types that extend from JsonSerializable or XmlSerializable + if (snapshot.extendsJsonSerializable || snapshot.extendsXmlSerializable) { + return; + } + if (snapshot.implementsJsonSerializable && !snapshot.observedFromJson) { log(classNode, ERR_NO_FROM_JSON); } @@ -139,8 +182,11 @@ private void performSnapshotAudit(DetailAST classNode) { private static class TypeSnapshot { DetailAST classNode; + boolean isAbstract; boolean implementsJsonSerializable; boolean implementsXmlSerializable; + boolean extendsJsonSerializable; + boolean extendsXmlSerializable; boolean observedFromJson; boolean observedFromXml; diff --git a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java index 1dacb8e1cb1e..c7e767cfe21e 100644 --- a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java +++ b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java @@ -165,4 +165,93 @@ public void classWithExtraMethodsAndCorrectSerializationMethods() throws Excepti verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); } + + @Test + public void abstractClassImplementingJsonSerializable() throws Exception { + File testFile = TestUtils.createCheckFile("abstractJson", "package com.azure;", + "public abstract class AbstractJson implements JsonSerializable {", " public void toJson() {}", "}"); + + // Abstract classes should not require fromJson method + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void abstractClassImplementingXmlSerializable() throws Exception { + File testFile = TestUtils.createCheckFile("abstractXml", "package com.azure;", + "public abstract class AbstractXml implements XmlSerializable {", " public void toXml() {}", "}"); + + // Abstract classes should not require fromXml method + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void abstractClassImplementingBothInterfaces() throws Exception { + File testFile = TestUtils.createCheckFile("abstractBoth", "package com.azure;", + "public abstract class AbstractBoth implements JsonSerializable, XmlSerializable {", + " public void toJson() {}", " public void toXml() {}", "}"); + + // Abstract classes should not require fromJson or fromXml methods + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void classExtendsJsonSerializableType() throws Exception { + File testFile = TestUtils.createCheckFile("extendsJson", "package com.azure;", + "public interface ExtendsJson extends JsonSerializable {", " public void toJson() {}", "}"); + + // Classes extending JsonSerializable should not require fromJson method + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void classExtendsXmlSerializableType() throws Exception { + File testFile = TestUtils.createCheckFile("extendsXml", "package com.azure;", + "public interface ExtendsXml extends XmlSerializable {", " public void toXml() {}", "}"); + + // Classes extending XmlSerializable should not require fromXml method + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void classExtendsJsonSerializableAndImplementsInterface() throws Exception { + File testFile = TestUtils.createCheckFile("extendsJsonImpl", "package com.azure;", + "public class ExtendsJsonImpl extends BaseJson implements JsonSerializable {", + " public void toJson() {}", "}"); + + // Classes extending a non-serializable type but implementing JsonSerializable should require fromJson + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void concreteClassExtendingAbstractJsonSerializable() throws Exception { + File testFile = TestUtils.createCheckFile("concreteExtendsAbstract", "package com.azure;", + "public class ConcreteClass extends AbstractBase implements JsonSerializable {", + " public void toJson() {}", " public static ConcreteClass fromJson() { return null; }", "}"); + + // Concrete class implementing JsonSerializable with fromJson should pass + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } + + @Test + public void concreteClassImplementingJsonSerializableMissingFromJson() throws Exception { + File testFile = TestUtils.createCheckFile("concreteMissing", "package com.azure;", + "public class ConcreteMissing extends AbstractBase implements JsonSerializable {", + " public void toJson() {}", "}"); + + // Concrete class implementing JsonSerializable without fromJson should fail + // unless it extends from JsonSerializable type + String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON }; + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); + } + + @Test + public void nestedAbstractClassImplementingJsonSerializable() throws Exception { + File testFile = TestUtils.createCheckFile("nestedAbstract", "package com.azure;", "public class OuterClass {", + " public abstract static class InnerAbstract implements JsonSerializable {", + " public void toJson() {}", " }", "}"); + + // Nested abstract classes should not require fromJson method + verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); + } } From 6f92af97372a430d786fa552f53a14ad21c43ac4 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Tue, 10 Feb 2026 00:06:36 -0800 Subject: [PATCH 08/12] update search checkstyle --- sdk/search/azure-search-documents/checkstyle-suppressions.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/search/azure-search-documents/checkstyle-suppressions.xml b/sdk/search/azure-search-documents/checkstyle-suppressions.xml index 47a4f50ab30c..c8863ee178c8 100644 --- a/sdk/search/azure-search-documents/checkstyle-suppressions.xml +++ b/sdk/search/azure-search-documents/checkstyle-suppressions.xml @@ -15,4 +15,5 @@ + From 3f43a64a8f7f24414892c80d99a92fa99101635a Mon Sep 17 00:00:00 2001 From: Srikanta <51379715+srnagar@users.noreply.github.com> Date: Tue, 10 Feb 2026 12:38:00 -0800 Subject: [PATCH 09/12] Update sdk/search/azure-search-documents/checkstyle-suppressions.xml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- sdk/search/azure-search-documents/checkstyle-suppressions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/search/azure-search-documents/checkstyle-suppressions.xml b/sdk/search/azure-search-documents/checkstyle-suppressions.xml index c8863ee178c8..2b1de7691a68 100644 --- a/sdk/search/azure-search-documents/checkstyle-suppressions.xml +++ b/sdk/search/azure-search-documents/checkstyle-suppressions.xml @@ -15,5 +15,5 @@ - + From 53e775526c15004e764be8a6f054a513968b2054 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Tue, 10 Feb 2026 14:03:59 -0800 Subject: [PATCH 10/12] Fix SerializableMethodsCheck to skip validation for classes that extend another type --- .../checks/SerializableMethodsCheck.java | 30 +------------ .../checks/SerializableMethodsCheckTest.java | 42 +++---------------- 2 files changed, 7 insertions(+), 65 deletions(-) diff --git a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java index c7f75673a8b1..33ee79601fa3 100644 --- a/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java +++ b/sdk/tools/linting-extensions/src/main/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheck.java @@ -68,12 +68,6 @@ private TypeSnapshot captureTypeSnapshot(DetailAST classNode) { // Check if the class is abstract - skip validation for abstract types snapshot.isAbstract = isAbstractType(classNode); - // Check if the class extends JsonSerializable or XmlSerializable - DetailAST extendsSection = classNode.findFirstToken(TokenTypes.EXTENDS_CLAUSE); - if (extendsSection != null) { - digestExtendsSection(extendsSection, snapshot); - } - DetailAST interfaceSection = classNode.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE); if (interfaceSection != null) { digestInterfaceSection(interfaceSection, snapshot); @@ -90,22 +84,6 @@ private boolean isAbstractType(DetailAST classNode) { return modifierBlock.findFirstToken(TokenTypes.ABSTRACT) != null; } - private void digestExtendsSection(DetailAST extendsSection, TypeSnapshot snapshot) { - DetailAST cursor = extendsSection.getFirstChild(); - - while (cursor != null) { - if (cursor.getType() == TokenTypes.IDENT) { - String parentLabel = cursor.getText(); - if ("JsonSerializable".equals(parentLabel)) { - snapshot.extendsJsonSerializable = true; - } else if ("XmlSerializable".equals(parentLabel)) { - snapshot.extendsXmlSerializable = true; - } - } - cursor = cursor.getNextSibling(); - } - } - private void digestInterfaceSection(DetailAST interfaceSection, TypeSnapshot snapshot) { DetailAST cursor = interfaceSection.getFirstChild(); @@ -166,11 +144,6 @@ private void performSnapshotAudit(DetailAST classNode) { return; } - // Skip validation for types that extend from JsonSerializable or XmlSerializable - if (snapshot.extendsJsonSerializable || snapshot.extendsXmlSerializable) { - return; - } - if (snapshot.implementsJsonSerializable && !snapshot.observedFromJson) { log(classNode, ERR_NO_FROM_JSON); } @@ -183,10 +156,9 @@ private void performSnapshotAudit(DetailAST classNode) { private static class TypeSnapshot { DetailAST classNode; boolean isAbstract; + boolean extendsAnotherType; boolean implementsJsonSerializable; boolean implementsXmlSerializable; - boolean extendsJsonSerializable; - boolean extendsXmlSerializable; boolean observedFromJson; boolean observedFromXml; diff --git a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java index c7e767cfe21e..b9aca71a0728 100644 --- a/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java +++ b/sdk/tools/linting-extensions/src/test/java/io/clientcore/linting/extensions/checkstyle/checks/SerializableMethodsCheckTest.java @@ -195,56 +195,26 @@ public void abstractClassImplementingBothInterfaces() throws Exception { } @Test - public void classExtendsJsonSerializableType() throws Exception { + public void classExtendsAnotherTypeAndImplementsJsonSerializable() throws Exception { File testFile = TestUtils.createCheckFile("extendsJson", "package com.azure;", - "public interface ExtendsJson extends JsonSerializable {", " public void toJson() {}", "}"); + "public class ExtendsJson extends BaseClass implements JsonSerializable {", " public void toJson() {}", + "}"); - // Classes extending JsonSerializable should not require fromJson method - verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); - } - - @Test - public void classExtendsXmlSerializableType() throws Exception { - File testFile = TestUtils.createCheckFile("extendsXml", "package com.azure;", - "public interface ExtendsXml extends XmlSerializable {", " public void toXml() {}", "}"); - - // Classes extending XmlSerializable should not require fromXml method - verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); - } - - @Test - public void classExtendsJsonSerializableAndImplementsInterface() throws Exception { - File testFile = TestUtils.createCheckFile("extendsJsonImpl", "package com.azure;", - "public class ExtendsJsonImpl extends BaseJson implements JsonSerializable {", - " public void toJson() {}", "}"); - - // Classes extending a non-serializable type but implementing JsonSerializable should require fromJson + // Classes extending another type and implementing JsonSerializable should require fromJson method String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON }; verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); } @Test - public void concreteClassExtendingAbstractJsonSerializable() throws Exception { + public void classExtendsTypeAndImplementsJsonSerializableWithFromJson() throws Exception { File testFile = TestUtils.createCheckFile("concreteExtendsAbstract", "package com.azure;", "public class ConcreteClass extends AbstractBase implements JsonSerializable {", " public void toJson() {}", " public static ConcreteClass fromJson() { return null; }", "}"); - // Concrete class implementing JsonSerializable with fromJson should pass + // Classes extending any type should pass (validation is skipped) verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath()); } - @Test - public void concreteClassImplementingJsonSerializableMissingFromJson() throws Exception { - File testFile = TestUtils.createCheckFile("concreteMissing", "package com.azure;", - "public class ConcreteMissing extends AbstractBase implements JsonSerializable {", - " public void toJson() {}", "}"); - - // Concrete class implementing JsonSerializable without fromJson should fail - // unless it extends from JsonSerializable type - String[] expectedErrors = { "2:1: " + ERR_NO_FROM_JSON }; - verify(lintingChecker, new File[] { testFile }, testFile.getAbsolutePath(), expectedErrors); - } - @Test public void nestedAbstractClassImplementingJsonSerializable() throws Exception { File testFile = TestUtils.createCheckFile("nestedAbstract", "package com.azure;", "public class OuterClass {", From 583456259788f9d820d8a366c103e5aa5430ca38 Mon Sep 17 00:00:00 2001 From: Srikanta Date: Wed, 11 Feb 2026 16:32:21 -0800 Subject: [PATCH 11/12] update search checkstyle suppression --- sdk/search/azure-search-documents/checkstyle-suppressions.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/search/azure-search-documents/checkstyle-suppressions.xml b/sdk/search/azure-search-documents/checkstyle-suppressions.xml index 2b1de7691a68..49c0537c61ec 100644 --- a/sdk/search/azure-search-documents/checkstyle-suppressions.xml +++ b/sdk/search/azure-search-documents/checkstyle-suppressions.xml @@ -16,4 +16,5 @@ + From 1c576f580fec3bdd70d6ea63aee4648ab9e2f70c Mon Sep 17 00:00:00 2001 From: Srikanta Date: Thu, 12 Feb 2026 11:24:47 -0800 Subject: [PATCH 12/12] fix unreleased dependency list --- eng/versioning/version_client.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index ae7a7814d306..f5278c2caf7c 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -550,7 +550,6 @@ io.clientcore:optional-dependency-tests;1.0.0-beta.1;1.0.0-beta.1 # unreleased_com.azure.resourcemanager:azure-resourcemanager-containerregistry;2.55.0 -unreleased_com.azure.resourcemanager:azure-resourcemanager-network;2.58.0 unreleased_com.azure.v2:azure-core;2.0.0-beta.1 unreleased_com.azure.v2:azure-identity;2.0.0-beta.1 unreleased_com.azure.v2:azure-data-appconfiguration;2.0.0-beta.1