Refactor: centralize JSON-to-class conversion

The functionality for converting a JSON string into an instance of a
custom class has been centralized into new `jsonToClass` functions to
streamline the code and improve maintainability.

Change-Id: Ic8d3dedcc85f792f0d5ab790294cac23682d352e
Signed-off-by: Patrizio <patrizio.gelosi@amarulasolutions.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptClient.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptClient.java
index adf040b..d6e693a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptClient.java
@@ -12,7 +12,7 @@
 import java.util.List;
 import java.util.Optional;
 
-import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.jsonToClass;
 
 @Slf4j
 abstract public class ChatGptClient extends ClientBase {
@@ -38,8 +38,7 @@
             return convertResponseContentFromJson(finalContent.toString());
         }
         else {
-            ChatGptResponseUnstreamed chatGptResponseUnstreamed =
-                    getGson().fromJson(body, ChatGptResponseUnstreamed.class);
+            ChatGptResponseUnstreamed chatGptResponseUnstreamed = jsonToClass(body, ChatGptResponseUnstreamed.class);
             return getResponseContent(chatGptResponseUnstreamed.getChoices().get(0).getMessage().getToolCalls());
         }
     }
@@ -74,7 +73,7 @@
             return Optional.empty();
         }
         ChatGptResponseStreamed chatGptResponseStreamed =
-                getGson().fromJson(line.substring("data: ".length()), ChatGptResponseStreamed.class);
+                jsonToClass(line.substring("data: ".length()), ChatGptResponseStreamed.class);
         ChatGptResponseMessage delta = chatGptResponseStreamed.getChoices().get(0).getDelta();
         if (delta == null || delta.getToolCalls() == null) {
             return Optional.empty();
@@ -84,7 +83,7 @@
     }
 
     private ChatGptResponseContent convertResponseContentFromJson(String content) {
-        return getGson().fromJson(content, ChatGptResponseContent.class);
+        return jsonToClass(content, ChatGptResponseContent.class);
     }
 
     private ChatGptToolCall.Function getFunction(List<ChatGptToolCall> toolCalls, int ind) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptTools.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptTools.java
index 06ff8b9..cf06eac 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptTools.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/common/client/api/chatgpt/ChatGptTools.java
@@ -8,7 +8,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 
-import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.jsonToClass;
 
 @Slf4j
 public class ChatGptTools {
@@ -31,7 +31,7 @@
         try (InputStreamReader reader = FileUtils.getInputStreamReader(
                 String.format(FILENAME_TOOL_FORMAT, functionName)
         )) {
-            tools = getGson().fromJson(reader, ChatGptTool.class);
+            tools = jsonToClass(reader, ChatGptTool.class);
             log.debug("Successfully loaded format replies tool from JSON.");
         } catch (IOException e) {
             throw new RuntimeException("Failed to load data for ChatGPT `" + functionName + "` tool", e);
@@ -44,7 +44,7 @@
         try (InputStreamReader reader = FileUtils.getInputStreamReader(
                 String.format(FILENAME_TOOL_CHOICE_FORMAT, functionName)
         )) {
-            toolChoice = getGson().fromJson(reader, ChatGptToolChoice.class);
+            toolChoice = jsonToClass(reader, ChatGptToolChoice.class);
             log.debug("Successfully loaded format replies tool choice from JSON.");
         } catch (IOException e) {
             throw new RuntimeException("Failed to load data for ChatGPT `" + functionName + "` tool choice", e);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptApiBase.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptApiBase.java
index bfd2fd9..4548dab 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptApiBase.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptApiBase.java
@@ -7,7 +7,7 @@
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.Request;
 
-import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.jsonToClass;
 
 @Slf4j
 public abstract class ChatGptApiBase extends ClientBase {
@@ -25,7 +25,7 @@
         clientResponse = httpClient.execute(request);
         log.debug("ChatGPT Client response: {}", clientResponse);
 
-        return getGson().fromJson(clientResponse, clazz);
+        return jsonToClass(clientResponse, clazz);
     }
 
     public ChatGptRunResponse getChatGptResponse(Request request) throws OpenAiConnectionFailException {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java
index 33bcd04..e8d1b48 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/mode/stateful/client/api/chatgpt/ChatGptClientStateful.java
@@ -19,7 +19,7 @@
 import com.googlesource.gerrit.plugins.chatgpt.mode.stateful.model.api.chatgpt.ChatGptThreadMessageResponse;
 import lombok.extern.slf4j.Slf4j;
 
-import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.jsonToClass;
 import static com.googlesource.gerrit.plugins.chatgpt.utils.JsonTextUtils.isJsonObjectAsString;
 import static com.googlesource.gerrit.plugins.chatgpt.utils.JsonTextUtils.unwrapJsonCode;
 
@@ -166,6 +166,6 @@
 
     private ChatGptResponseContent extractResponseContent(String responseText) {
         log.debug("Extracting response content from JSON.");
-        return getGson().fromJson(unwrapJsonCode(responseText), ChatGptResponseContent.class);
+        return jsonToClass(unwrapJsonCode(responseText), ChatGptResponseContent.class);
     }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/GsonUtils.java b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/GsonUtils.java
index 9598a7d..8f3ea04 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/GsonUtils.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chatgpt/utils/GsonUtils.java
@@ -3,6 +3,8 @@
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 
+import java.io.InputStreamReader;
+
 public class GsonUtils {
     public static Gson getGson() {
         return new Gson();
@@ -13,4 +15,12 @@
                 .disableHtmlEscaping()
                 .create();
     }
+
+    public static <T> T jsonToClass(String content, Class<T> clazz) {
+        return getGson().fromJson(content, clazz);
+    }
+
+    public static <T> T jsonToClass(InputStreamReader content, Class<T> clazz) {
+        return getGson().fromJson(content, clazz);
+    }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTaskSpecificTest.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTaskSpecificTest.java
index e886e80..2170833 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTaskSpecificTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTaskSpecificTest.java
@@ -18,6 +18,7 @@
 import static com.googlesource.gerrit.plugins.chatgpt.mode.common.client.prompt.ChatGptPromptFactory.getChatGptPromptStateful;
 import static com.googlesource.gerrit.plugins.chatgpt.settings.Settings.GERRIT_PATCH_SET_FILENAME;
 import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.jsonToClass;
 import static org.mockito.Mockito.when;
 
 @Slf4j
@@ -54,7 +55,7 @@
     }
 
     private String filterOutSubsetRunStepsResponse(int from, int to) {
-        ChatGptListResponse runStepsResponse = getGson().fromJson(
+        ChatGptListResponse runStepsResponse = jsonToClass(
                 readTestFile(RESOURCE_STATEFUL_PATH + "chatGptRunStepsResponse.json"),
                 ChatGptListResponse.class
         );
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTestBase.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTestBase.java
index 4215530..cc09f67 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTestBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewStatefulTestBase.java
@@ -29,6 +29,7 @@
 import static com.googlesource.gerrit.plugins.chatgpt.mode.common.client.prompt.ChatGptPromptFactory.getChatGptPromptStateful;
 import static com.googlesource.gerrit.plugins.chatgpt.mode.stateful.client.api.chatgpt.ChatGptPoller.COMPLETED_STATUS;
 import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.jsonToClass;
 import static java.net.HttpURLConnection.HTTP_OK;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
@@ -153,10 +154,10 @@
     }
 
     protected String getReviewMessage(String responseFile, int tollCallId) {
-        ChatGptListResponse responseContent = getGson().fromJson(readTestFile(responseFile), ChatGptListResponse.class);
+        ChatGptListResponse responseContent = jsonToClass(readTestFile(responseFile), ChatGptListResponse.class);
         String reviewJsonResponse = responseContent.getData().get(0).getStepDetails().getToolCalls().get(tollCallId)
                 .getFunction().getArguments();
-        return getGson().fromJson(reviewJsonResponse, ChatGptResponseContent.class).getReplies().get(0).getReply();
+        return jsonToClass(reviewJsonResponse, ChatGptResponseContent.class).getReplies().get(0).getReply();
     }
 
     protected List<ReviewInput.CommentInput> getCapturedComments(ArgumentCaptor<ReviewInput> captor, String filename) {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java
index 073a173..940937c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/chatgpt/ChatGptReviewTestBase.java
@@ -71,7 +71,7 @@
 
 import static com.google.gerrit.extensions.client.ChangeKind.REWORK;
 import static com.googlesource.gerrit.plugins.chatgpt.listener.EventHandlerTask.EVENT_CLASS_MAP;
-import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.getGson;
+import static com.googlesource.gerrit.plugins.chatgpt.utils.GsonUtils.jsonToClass;
 import static org.mockito.Mockito.*;
 
 public class ChatGptReviewTestBase extends ChatGptTestBase {
@@ -309,7 +309,7 @@
     protected ArgumentCaptor<ReviewInput> testRequestSent() throws RestApiException {
         ArgumentCaptor<ReviewInput> reviewInputCaptor = ArgumentCaptor.forClass(ReviewInput.class);
         verify(revisionApiMock).review(reviewInputCaptor.capture());
-        gptRequestBody = getGson().fromJson(patchSetReviewer.getChatGptClient().getRequestBody(), JsonObject.class);
+        gptRequestBody = jsonToClass(patchSetReviewer.getChatGptClient().getRequestBody(), JsonObject.class);
         return reviewInputCaptor;
     }