diff --git a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolset.java b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolset.java index fc1abd4f0..7bad261ed 100644 --- a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolset.java +++ b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolset.java @@ -8,9 +8,8 @@ import com.google.adk.agents.ReadonlyContext; import com.google.adk.tools.BaseTool; import com.google.adk.tools.BaseToolset; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.DefaultHttpExecutor; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor; import io.reactivex.rxjava3.core.Flowable; +import java.net.http.HttpClient; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -30,7 +29,8 @@ public class ApplicationIntegrationToolset implements BaseToolset { @Nullable String toolNamePrefix; @Nullable String toolInstructions; public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - HttpExecutor httpExecutor; + private final HttpClient httpClient; + private final CredentialsHelper credentialsHelper; /** * ApplicationIntegrationToolset generates tools from a given Application Integration resource. @@ -85,7 +85,8 @@ public ApplicationIntegrationToolset( serviceAccountJson, toolNamePrefix, toolInstructions, - new DefaultHttpExecutor().createExecutor(serviceAccountJson)); + HttpClient.newHttpClient(), + new GoogleCredentialsHelper()); } ApplicationIntegrationToolset( @@ -99,7 +100,8 @@ public ApplicationIntegrationToolset( String serviceAccountJson, String toolNamePrefix, String toolInstructions, - HttpExecutor httpExecutor) { + HttpClient httpClient, + CredentialsHelper credentialsHelper) { this.project = project; this.location = location; this.integration = integration; @@ -110,7 +112,8 @@ public ApplicationIntegrationToolset( this.serviceAccountJson = serviceAccountJson; this.toolNamePrefix = toolNamePrefix; this.toolInstructions = toolInstructions; - this.httpExecutor = httpExecutor; + this.httpClient = httpClient; + this.credentialsHelper = credentialsHelper; } List getPathUrl(String openApiSchemaString) throws Exception { @@ -145,7 +148,9 @@ private List getAllTools() throws Exception { null, null, null, - this.httpExecutor); + this.serviceAccountJson, + this.httpClient, + this.credentialsHelper); openApiSchemaString = integrationClient.generateOpenApiSpec(); List pathUrls = getPathUrl(openApiSchemaString); for (String pathUrl : pathUrls) { @@ -161,7 +166,8 @@ private List getAllTools() throws Exception { null, null, this.serviceAccountJson, - this.httpExecutor)); + this.httpClient, + this.credentialsHelper)); } } } else if (!isNullOrEmpty(this.connection) @@ -175,7 +181,9 @@ private List getAllTools() throws Exception { this.connection, this.entityOperations, this.actions, - this.httpExecutor); + this.serviceAccountJson, + this.httpClient, + this.credentialsHelper); ObjectNode parentOpenApiSpec = OBJECT_MAPPER.createObjectNode(); ObjectNode openApiSpec = integrationClient.getOpenApiSpecForConnection(toolNamePrefix, toolInstructions); @@ -188,7 +196,14 @@ private List getAllTools() throws Exception { if (!isNullOrEmpty(toolName)) { ConnectionsClient connectionsClient = new ConnectionsClient( - this.project, this.location, this.connection, this.httpExecutor, OBJECT_MAPPER); + this.project, + this.location, + this.connection, + this.serviceAccountJson, + this.httpClient, + this.credentialsHelper, + OBJECT_MAPPER); + ConnectionsClient.ConnectionDetails connectionDetails = connectionsClient.getConnectionDetails(); @@ -202,7 +217,8 @@ private List getAllTools() throws Exception { connectionDetails.serviceName, connectionDetails.host, this.serviceAccountJson, - this.httpExecutor)); + this.httpClient, + this.credentialsHelper)); } } } else { diff --git a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClient.java b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClient.java index 4d9059005..36e94a957 100644 --- a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClient.java +++ b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClient.java @@ -4,11 +4,13 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor; +import com.google.auth.Credentials; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.net.URI; +import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.HashMap; @@ -28,7 +30,9 @@ public class ConnectionsClient { private final String location; private final String connection; private static final String CONNECTOR_URL = "https://connectors.googleapis.com"; - private final HttpExecutor httpExecutor; + private final HttpClient httpClient; + private final String serviceAccountJson; + private final CredentialsHelper credentialsHelper; private final ObjectMapper objectMapper; /** Represents details of a connection. */ @@ -63,13 +67,34 @@ public ConnectionsClient( String project, String location, String connection, - HttpExecutor httpExecutor, + String serviceAccountJson, + HttpClient httpClient, + CredentialsHelper credentialsHelper, ObjectMapper objectMapper) { + this.project = project; this.location = location; this.connection = connection; - this.httpExecutor = httpExecutor; + this.httpClient = Preconditions.checkNotNull(httpClient); this.objectMapper = objectMapper; + this.serviceAccountJson = serviceAccountJson; + this.credentialsHelper = Preconditions.checkNotNull(credentialsHelper); + } + + public ConnectionsClient( + String project, + String location, + String connection, + HttpClient httpClient, + ObjectMapper objectMapper) { + this( + project, + location, + connection, + null, + httpClient, + new GoogleCredentialsHelper(), + objectMapper); } /** @@ -173,16 +198,17 @@ public ActionSchema getActionSchema(String action) throws IOException, Interrupt } private HttpResponse executeApiCall(String url) throws IOException, InterruptedException { - HttpRequest request = + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Content-Type", "application/json") - .header("Authorization", "Bearer " + httpExecutor.getToken()) - .GET() - .build(); + .GET(); + + Credentials credentials = credentialsHelper.getGoogleCredentials(serviceAccountJson); + requestBuilder = CredentialsHelper.populateHeaders(requestBuilder, credentials); HttpResponse response = - httpExecutor.send(request, HttpResponse.BodyHandlers.ofString()); + httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 400) { String body = response.body(); diff --git a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/CredentialsHelper.java b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/CredentialsHelper.java new file mode 100644 index 000000000..8ef21293e --- /dev/null +++ b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/CredentialsHelper.java @@ -0,0 +1,23 @@ +package com.google.adk.tools.applicationintegrationtoolset; + +import com.google.auth.Credentials; +import java.io.IOException; +import java.net.http.HttpRequest; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +public interface CredentialsHelper { + + Credentials getGoogleCredentials(@Nullable String serviceAccountJson) throws IOException; + + public static HttpRequest.Builder populateHeaders( + HttpRequest.Builder builder, Credentials credentials) throws IOException { + for (Map.Entry> entry : credentials.getRequestMetadata().entrySet()) { + for (String value : entry.getValue()) { + builder = builder.header(entry.getKey(), value); + } + } + return builder; + } +} diff --git a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/GoogleCredentialsHelper.java b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/GoogleCredentialsHelper.java new file mode 100644 index 000000000..ca036d21b --- /dev/null +++ b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/GoogleCredentialsHelper.java @@ -0,0 +1,30 @@ +package com.google.adk.tools.applicationintegrationtoolset; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.ServiceAccountCredentials; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import javax.annotation.Nullable; + +public final class GoogleCredentialsHelper implements CredentialsHelper { + + @Override + public GoogleCredentials getGoogleCredentials(@Nullable String serviceAccountJson) + throws IOException { + GoogleCredentials credentials; + + if (serviceAccountJson != null && !serviceAccountJson.isBlank()) { + try (InputStream is = new ByteArrayInputStream(serviceAccountJson.getBytes(UTF_8))) { + credentials = ServiceAccountCredentials.fromStream(is); + } + } else { + credentials = GoogleCredentials.getApplicationDefault(); + } + credentials = credentials.createScoped("https://www.googleapis.com/auth/cloud-platform"); + credentials.refreshIfExpired(); + return credentials; + } +} diff --git a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClient.java b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClient.java index 2f43458c0..bb2ec5365 100644 --- a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClient.java +++ b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClient.java @@ -5,12 +5,13 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor; +import com.google.auth.Credentials; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.net.URI; +import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.Arrays; @@ -26,14 +27,17 @@ *

This class provides methods for retrieving OpenAPI spec for an integration or a connection. */ public class IntegrationClient { - String project; - String location; - String integration; - List triggers; - String connection; - Map> entityOperations; - List actions; - private final HttpExecutor httpExecutor; + private final String project; + private final String location; + private final String integration; + private final List triggers; + private final String connection; + private final Map> entityOperations; + private final List actions; + private final String serviceAccountJson; + private final HttpClient httpClient; + private final CredentialsHelper credentialsHelper; + public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); IntegrationClient( @@ -44,7 +48,10 @@ public class IntegrationClient { String connection, Map> entityOperations, List actions, - HttpExecutor httpExecutor) { + String serviceAccountJson, + HttpClient httpClient, + CredentialsHelper credentialsHelper) { + this.project = project; this.location = location; this.integration = integration; @@ -52,12 +59,55 @@ public class IntegrationClient { this.connection = connection; this.entityOperations = entityOperations; this.actions = actions; - this.httpExecutor = httpExecutor; + this.serviceAccountJson = serviceAccountJson; + this.httpClient = Preconditions.checkNotNull(httpClient); + this.credentialsHelper = Preconditions.checkNotNull(credentialsHelper); if (!isNullOrEmpty(connection)) { validate(); } } + IntegrationClient( + String project, + String location, + String integration, + List triggers, + String connection, + Map> entityOperations, + List actions) { + this( + project, + location, + integration, + triggers, + connection, + entityOperations, + actions, + HttpClient.newHttpClient()); + } + + IntegrationClient( + String project, + String location, + String integration, + List triggers, + String connection, + Map> entityOperations, + List actions, + HttpClient httpClient) { + this( + project, + location, + integration, + triggers, + connection, + entityOperations, + actions, + null, + httpClient, + new GoogleCredentialsHelper()); + } + private void validate() { // Check if both are null, throw exception @@ -116,15 +166,17 @@ String generateOpenApiSpec() throws Exception { Arrays.asList(this.triggers))), "fileFormat", "JSON")); - HttpRequest request = + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() .uri(URI.create(url)) - .header("Authorization", "Bearer " + httpExecutor.getToken()) .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(jsonRequestBody)) - .build(); + .POST(HttpRequest.BodyPublishers.ofString(jsonRequestBody)); + + Credentials credentials = credentialsHelper.getGoogleCredentials(serviceAccountJson); + requestBuilder = CredentialsHelper.populateHeaders(requestBuilder, credentials); + HttpResponse response = - httpExecutor.send(request, HttpResponse.BodyHandlers.ofString()); + httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()); if (response.statusCode() < 200 || response.statusCode() >= 300) { throw new Exception("Error fetching OpenAPI spec. Status: " + response.statusCode()); @@ -326,6 +378,12 @@ String getOperationIdFromPathUrl(String openApiSchemaString, String pathUrl) thr ConnectionsClient createConnectionsClient() { return new ConnectionsClient( - this.project, this.location, this.connection, this.httpExecutor, OBJECT_MAPPER); + this.project, + this.location, + this.connection, + this.serviceAccountJson, + this.httpClient, + this.credentialsHelper, + OBJECT_MAPPER); } } diff --git a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorTool.java b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorTool.java index 86b39e732..ed938d4b7 100644 --- a/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorTool.java +++ b/core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorTool.java @@ -1,7 +1,6 @@ package com.google.adk.tools.applicationintegrationtoolset; import static com.google.common.base.Strings.isNullOrEmpty; -import static java.nio.charset.StandardCharsets.UTF_8; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -9,16 +8,15 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.adk.tools.BaseTool; import com.google.adk.tools.ToolContext; -import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.Credentials; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Streams; import com.google.genai.types.FunctionDeclaration; import com.google.genai.types.Schema; import io.reactivex.rxjava3.core.Single; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -34,88 +32,18 @@ public class IntegrationConnectorTool extends BaseTool { private final String openApiSpec; private final String pathUrl; - private final HttpExecutor httpExecutor; private final String connectionName; private final String serviceName; private final String host; + private final String serviceAccountJson; + private final HttpClient httpClient; + private final CredentialsHelper credentialsHelper; + private String entity; private String operation; private String action; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - interface HttpExecutor { - HttpResponse send(HttpRequest request, HttpResponse.BodyHandler responseBodyHandler) - throws IOException, InterruptedException; - - String getToken() throws IOException; - - public HttpExecutor createExecutor(String serviceAccountJson); - } - - static class DefaultHttpExecutor implements HttpExecutor { - private final HttpClient client = HttpClient.newHttpClient(); - private final String serviceAccountJson; - - /** Default constructor for when no service account is specified. */ - DefaultHttpExecutor() { - this(null); - } - /** - * Constructor that accepts an optional service account JSON string. - * - * @param serviceAccountJson The service account key as a JSON string, or null. - */ - DefaultHttpExecutor(@Nullable String serviceAccountJson) { - this.serviceAccountJson = serviceAccountJson; - } - - @Override - public HttpResponse send( - HttpRequest request, HttpResponse.BodyHandler responseBodyHandler) - throws IOException, InterruptedException { - return client.send(request, responseBodyHandler); - } - - @Override - public String getToken() throws IOException { - GoogleCredentials credentials; - - if (this.serviceAccountJson != null && !this.serviceAccountJson.trim().isEmpty()) { - try (InputStream is = new ByteArrayInputStream(this.serviceAccountJson.getBytes(UTF_8))) { - credentials = - GoogleCredentials.fromStream(is) - .createScoped("https://www.googleapis.com/auth/cloud-platform"); - } catch (IOException e) { - throw new IOException("Failed to load credentials from service_account_json.", e); - } - } else { - try { - credentials = - GoogleCredentials.getApplicationDefault() - .createScoped("https://www.googleapis.com/auth/cloud-platform"); - } catch (IOException e) { - throw new IOException( - "Please provide a service account or configure Application Default Credentials. To" - + " set up ADC, see" - + " https://cloud.google.com/docs/authentication/external/set-up-adc.", - e); - } - } - - credentials.refreshIfExpired(); - return credentials.getAccessToken().getTokenValue(); - } - - @Override - public HttpExecutor createExecutor(String serviceAccountJson) { - if (isNullOrEmpty(serviceAccountJson)) { - return new DefaultHttpExecutor(); - } else { - return new DefaultHttpExecutor(serviceAccountJson); - } - } - } + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final ImmutableList EXCLUDE_FIELDS = ImmutableList.of("connectionName", "serviceName", "host", "entity", "operation", "action"); @@ -130,16 +58,7 @@ public HttpExecutor createExecutor(String serviceAccountJson) { String toolName, String toolDescription, String serviceAccountJson) { - this( - openApiSpec, - pathUrl, - toolName, - toolDescription, - null, - null, - null, - serviceAccountJson, - new DefaultHttpExecutor().createExecutor(serviceAccountJson)); + this(openApiSpec, pathUrl, toolName, toolDescription, null, null, null, serviceAccountJson); } /** @@ -164,7 +83,8 @@ public HttpExecutor createExecutor(String serviceAccountJson) { serviceName, host, serviceAccountJson, - new DefaultHttpExecutor().createExecutor(serviceAccountJson)); + HttpClient.newHttpClient(), + new GoogleCredentialsHelper()); } IntegrationConnectorTool( @@ -176,14 +96,17 @@ public HttpExecutor createExecutor(String serviceAccountJson) { @Nullable String serviceName, @Nullable String host, @Nullable String serviceAccountJson, - HttpExecutor httpExecutor) { + HttpClient httpClient, + CredentialsHelper credentialsHelper) { super(toolName, toolDescription); this.openApiSpec = openApiSpec; this.pathUrl = pathUrl; this.connectionName = connectionName; this.serviceName = serviceName; this.host = host; - this.httpExecutor = httpExecutor; + this.serviceAccountJson = serviceAccountJson; + this.httpClient = Preconditions.checkNotNull(httpClient); + this.credentialsHelper = Preconditions.checkNotNull(credentialsHelper); } Schema toGeminiSchema(String openApiSchema, String operationId) throws Exception { @@ -247,15 +170,17 @@ private String executeIntegration(Map args) throws Exception { } catch (IOException e) { throw new Exception("Error converting args to JSON: " + e.getMessage(), e); } - HttpRequest request = + Credentials credentials = credentialsHelper.getGoogleCredentials(this.serviceAccountJson); + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() .uri(URI.create(url)) - .header("Authorization", "Bearer " + httpExecutor.getToken()) .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(jsonRequestBody)) - .build(); + .POST(HttpRequest.BodyPublishers.ofString(jsonRequestBody)); + + requestBuilder = CredentialsHelper.populateHeaders(requestBuilder, credentials); + HttpResponse response = - httpExecutor.send(request, HttpResponse.BodyHandlers.ofString()); + httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()); if (response.statusCode() < 200 || response.statusCode() >= 300) { throw new Exception( diff --git a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolsetTest.java b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolsetTest.java index be80b9ed3..a0a04201e 100644 --- a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolsetTest.java +++ b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolsetTest.java @@ -9,13 +9,16 @@ import com.fasterxml.jackson.core.JsonParseException; import com.google.adk.tools.BaseTool; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor; +import com.google.auth.Credentials; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.List; import java.util.Objects; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,14 +31,20 @@ public final class ApplicationIntegrationToolsetTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - @Mock private HttpExecutor mockHttpExecutor; + @Mock private HttpClient mockHttpClient; + @Mock private CredentialsHelper mockCredentialsHelper; + @Mock private Credentials mockCredentials; private static final String LOCATION = "us-central1"; private static final String PROJECT = "test-project"; - private static final String MOCK_ACCESS_TOKEN = "test-token"; private static final String CONNECTION = "projects/test-project/locations/us-central1/connections/test-conn"; + @Before + public void setUp() throws IOException { + when(mockCredentialsHelper.getGoogleCredentials(any())).thenReturn(mockCredentials); + } + @Test public void getTools_forIntegration_success() throws Exception { ApplicationIntegrationToolset toolset = @@ -50,7 +59,8 @@ public void getTools_forIntegration_success() throws Exception { null, null, null, - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); String mockOpenApiSpecJson = "{\"openApiSpec\":" @@ -59,10 +69,9 @@ public void getTools_forIntegration_success() throws Exception { @SuppressWarnings("unchecked") HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN); when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(mockOpenApiSpecJson); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); List tools = toolset.getTools(null).toList().blockingGet(); @@ -84,7 +93,8 @@ public void getTools_forConnection_success() throws Exception { null, "Jira", "Tools for Jira", - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); String mockConnectionDetailsJson = "{\"name\":\"" @@ -97,12 +107,11 @@ public void getTools_forConnection_success() throws Exception { @SuppressWarnings("unchecked") HttpResponse mockHttpResponse = mock(HttpResponse.class); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN); when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()) .thenReturn(mockConnectionDetailsJson) .thenReturn(mockEntitySchemaJson); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); List tools = toolset.getTools(null).toList().blockingGet(); @@ -124,7 +133,8 @@ public void getTools_invalidArguments_emitsError() { null, null, null, - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); toolset .getTools(null) @@ -142,7 +152,18 @@ public void getTools_invalidArguments_emitsError() { public void getTools_forConnection_noEntityOperationsOrActions_emitsError() { ApplicationIntegrationToolset toolset = new ApplicationIntegrationToolset( - PROJECT, LOCATION, null, null, null, null, null, null, null, null, mockHttpExecutor); + PROJECT, + LOCATION, + null, + null, + null, + null, + null, + null, + null, + null, + mockHttpClient, + mockCredentialsHelper); toolset .getTools(null) diff --git a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClientTest.java b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClientTest.java index 1f09394cf..d5723e7f1 100644 --- a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClientTest.java +++ b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/ConnectionsClientTest.java @@ -10,10 +10,11 @@ import com.google.adk.tools.applicationintegrationtoolset.ConnectionsClient.ActionSchema; import com.google.adk.tools.applicationintegrationtoolset.ConnectionsClient.ConnectionDetails; import com.google.adk.tools.applicationintegrationtoolset.ConnectionsClient.EntitySchemaAndOperations; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor; +import com.google.auth.Credentials; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; +import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.Map; @@ -31,8 +32,10 @@ public class ConnectionsClientTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - @Mock private HttpExecutor mockHttpExecutor; + @Mock private HttpClient mockHttpClient; @Mock private HttpResponse mockHttpResponse; + @Mock private CredentialsHelper mockCredentialsHelper; + @Mock private Credentials mockCredentials; private ConnectionsClient client; private final ObjectMapper objectMapper = new ObjectMapper(); @@ -43,8 +46,16 @@ public class ConnectionsClientTest { @Before public void setUp() throws IOException { - client = new ConnectionsClient(PROJECT, LOCATION, CONNECTION, mockHttpExecutor, objectMapper); - when(mockHttpExecutor.getToken()).thenReturn("fake-test-token"); + when(mockCredentialsHelper.getGoogleCredentials(any())).thenReturn(mockCredentials); + client = + new ConnectionsClient( + PROJECT, + LOCATION, + CONNECTION, + null, + mockHttpClient, + mockCredentialsHelper, + objectMapper); } @Test @@ -57,7 +68,7 @@ public void getConnectionDetails_success_parsesResponseCorrectly() throws Except when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(mockJsonResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); ConnectionDetails details = client.getConnectionDetails(); @@ -80,7 +91,7 @@ public void getEntitySchemaAndOperations_withPolling_success() throws Exception .thenReturn(initialCallResponse) .thenReturn(firstPollResponse) .thenReturn(finalPollResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); EntitySchemaAndOperations result = client.getEntitySchemaAndOperations("Issue"); @@ -93,7 +104,7 @@ public void getEntitySchemaAndOperations_withPolling_success() throws Exception public void getEntitySchemaAndOperations_noOperationId_throwsIOException() throws Exception { when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn("{}"); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(), any()); IOException e = assertThrows(IOException.class, () -> client.getEntitySchemaAndOperations("InvalidEntity")); @@ -113,7 +124,7 @@ public void getActionSchema_success() throws Exception { when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(initialCallResponse).thenReturn(finalPollResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); ActionSchema result = client.getActionSchema("TestAction"); @@ -128,7 +139,7 @@ public void getActionSchema_success() throws Exception { public void getActionSchema_noOperationId_throwsIOException() throws Exception { when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn("{}"); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(), any()); IOException e = assertThrows(IOException.class, () -> client.getActionSchema("InvalidAction")); assertThat(e) @@ -141,7 +152,7 @@ public void getActionSchema_noOperationId_throwsIOException() throws Exception { public void executeApiCall_on403_throwsSecurityException() throws Exception { when(mockHttpResponse.statusCode()).thenReturn(403); when(mockHttpResponse.body()).thenReturn("Permission Denied Error"); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(), any()); SecurityException e = assertThrows(SecurityException.class, () -> client.getConnectionDetails()); @@ -154,7 +165,7 @@ public void executeApiCall_on403_throwsSecurityException() throws Exception { public void executeApiCall_on404_throwsIllegalArgumentException() throws Exception { when(mockHttpResponse.statusCode()).thenReturn(404); when(mockHttpResponse.body()).thenReturn("Not Found Error"); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(), any()); IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> client.getConnectionDetails()); diff --git a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/CredentialsHelperTest.java b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/CredentialsHelperTest.java new file mode 100644 index 000000000..7e6687990 --- /dev/null +++ b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/CredentialsHelperTest.java @@ -0,0 +1,40 @@ +package com.google.adk.tools.applicationintegrationtoolset; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.auth.Credentials; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.net.URI; +import java.net.http.HttpRequest; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(JUnit4.class) +public final class CredentialsHelperTest { + + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock private Credentials mockCredentials; + + @Test + public void populateHeaders_success() throws Exception { + when(mockCredentials.getRequestMetadata()) + .thenReturn( + ImmutableMap.of( + "header1", ImmutableList.of("header1_value1", "header1_value2"), + "header2", ImmutableList.of("header2_value1"))); + HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create("http://example.com")); + builder = CredentialsHelper.populateHeaders(builder, mockCredentials); + + assertThat(builder.build().headers().allValues("header1")) + .containsExactly("header1_value1", "header1_value2"); + assertThat(builder.build().headers().allValues("header2")).containsExactly("header2_value1"); + } +} diff --git a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClientTest.java b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClientTest.java index 6753be68f..104efe495 100644 --- a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClientTest.java +++ b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationClientTest.java @@ -11,10 +11,11 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.adk.tools.applicationintegrationtoolset.ConnectionsClient.EntitySchemaAndOperations; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor; +import com.google.auth.Credentials; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.IOException; +import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.ArrayList; @@ -35,9 +36,11 @@ public class IntegrationClientTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - @Mock private HttpExecutor mockHttpExecutor; + @Mock private HttpClient mockHttpClient; @Mock private HttpResponse mockHttpResponse; @Mock private ConnectionsClient mockConnectionsClient; // The mock we want the factory to return + @Mock private CredentialsHelper mockCredentialsHelper; + @Mock private Credentials mockCredentials; private static final String PROJECT = "test-project"; private static final String LOCATION = "us-central1"; @@ -48,7 +51,7 @@ public class IntegrationClientTest { @Before public void setUp() throws IOException { - when(mockHttpExecutor.getToken()).thenReturn("fake-test-token"); + when(mockCredentialsHelper.getGoogleCredentials(any())).thenReturn(mockCredentials); } @Test @@ -65,7 +68,9 @@ public void constructor_entityOperationsNullAndActionsNull_throwsException() { CONNECTION, null, null, - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception) .hasMessageThat() @@ -86,7 +91,9 @@ public void constructor_entityOperationsEmpty_throwsException() { CONNECTION, ImmutableMap.of(), null, - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception).hasMessageThat().contains("entityOperations map cannot be empty"); } @@ -108,7 +115,7 @@ public void constructor_entityOperationsNullKey_throwsException() { CONNECTION, invalidEntityOperations, null, - mockHttpExecutor)); + mockHttpClient)); assertThat(exception) .hasMessageThat() @@ -132,7 +139,7 @@ public void constructor_entityOperationsEmptyKey_throwsException() { CONNECTION, invalidEntityOperations, null, - mockHttpExecutor)); + mockHttpClient)); assertThat(exception) .hasMessageThat() @@ -156,7 +163,9 @@ public void constructor_entityOperationsNullListValue_throwsException() { CONNECTION, invalidEntityOperations, null, - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception).hasMessageThat().contains("Operations for entity 'key1' cannot be null"); } @@ -181,7 +190,9 @@ public void constructor_entityOperationsListWithNullString_throwsException() { CONNECTION, invalidEntityOperations, null, - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception) .hasMessageThat() @@ -205,7 +216,9 @@ public void constructor_entityOperationsListWithEmptyString_throwsException() { CONNECTION, invalidEntityOperations, null, - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception) .hasMessageThat() @@ -227,7 +240,9 @@ public void constructor_actionsEmpty_throwsException() { CONNECTION, null, ImmutableList.of(), - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception).hasMessageThat().contains("Actions list cannot be empty"); } @@ -250,7 +265,9 @@ public void constructor_actionsListWithNull_throwsException() { CONNECTION, null, invalidActions, - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception).hasMessageThat().contains("Actions list cannot contain null values"); } @@ -270,7 +287,9 @@ public void constructor_actionsListWithEmptyString_throwsException() { CONNECTION, null, ImmutableList.of("action1", ""), - mockHttpExecutor)); + null, + mockHttpClient, + mockCredentialsHelper)); assertThat(exception).hasMessageThat().contains("Actions list cannot contain empty strings"); } @@ -288,7 +307,9 @@ public void constructor_validActions_success() { CONNECTION, null, validActions, - mockHttpExecutor); + null, + mockHttpClient, + mockCredentialsHelper); } @Test @@ -305,7 +326,9 @@ public void constructor_validEntityOperations_success() { CONNECTION, validEntityOperations, null, - mockHttpExecutor); + null, + mockHttpClient, + mockCredentialsHelper); } @Test @@ -320,12 +343,14 @@ public void generateOpenApiSpec_success() throws Exception { null, null, null, - mockHttpExecutor); + null, + mockHttpClient, + mockCredentialsHelper); String mockResponse = "{\"openApiSpec\":\"{}\"}"; when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(mockResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); String result = client.generateOpenApiSpec(); @@ -337,10 +362,19 @@ public void generateOpenApiSpec_success() throws Exception { public void generateOpenApiSpec_httpError_throwsException() throws Exception { IntegrationClient client = new IntegrationClient( - PROJECT, LOCATION, INTEGRATION, null, null, null, null, mockHttpExecutor); + PROJECT, + LOCATION, + INTEGRATION, + null, + null, + null, + null, + null, + mockHttpClient, + mockCredentialsHelper); when(mockHttpResponse.statusCode()).thenReturn(404); when(mockHttpResponse.body()).thenReturn("Not Found"); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); Exception exception = assertThrows(Exception.class, client::generateOpenApiSpec); assertThat(exception).hasMessageThat().contains("Error fetching OpenAPI spec. Status: 404"); @@ -357,7 +391,9 @@ public void getOpenApiSpecForConnection_success() throws Exception { CONNECTION, ImmutableMap.of("Issue", ImmutableList.of("GET")), null, - mockHttpExecutor); + null, + mockHttpClient, + mockCredentialsHelper); IntegrationClient spiedClient = spy(realClient); @@ -378,7 +414,8 @@ public void getOpenApiSpecForConnection_success() throws Exception { @Test public void getOperationIdFromPathUrl_success() throws Exception { IntegrationClient client = - new IntegrationClient(null, null, null, null, null, null, null, null); + new IntegrationClient( + null, null, null, null, null, null, null, null, mockHttpClient, mockCredentialsHelper); String openApiSpec = "{\"openApiSpec\":" + "\"{\\\"paths\\\":{\\\"/my/path\\\":{\\\"post\\\":{\\\"operationId\\\":\\\"my-op-id\\\"}}}}\"}"; @@ -391,7 +428,8 @@ public void getOperationIdFromPathUrl_success() throws Exception { @Test public void getOperationIdFromPathUrl_pathNotFound_throwsException() { IntegrationClient client = - new IntegrationClient(null, null, null, null, null, null, null, null); + new IntegrationClient( + null, null, null, null, null, null, null, null, mockHttpClient, mockCredentialsHelper); String openApiSpec = "{\"openApiSpec\":" + "\"{\\\"paths\\\":{\\\"/my/path\\\":{\\\"post\\\":{\\\"operationId\\\":\\\"my-op-id\\\"}}}}\"}"; @@ -405,7 +443,8 @@ public void getOperationIdFromPathUrl_pathNotFound_throwsException() { @Test public void getOperationIdFromPathUrl_invalidOpenApiSpec_throwsException() { IntegrationClient client = - new IntegrationClient(null, null, null, null, null, null, null, null); + new IntegrationClient( + null, null, null, null, null, null, null, null, mockHttpClient, mockCredentialsHelper); String openApiSpec = "{\"invalidKey\":\"value\"}"; IllegalArgumentException e = @@ -427,7 +466,9 @@ public void getOpenApiSpecForConnection_connectionsClientThrowsException_throwsE CONNECTION, ImmutableMap.of("Issue", ImmutableList.of("GET")), null, - mockHttpExecutor); + null, + mockHttpClient, + mockCredentialsHelper); IntegrationClient spyClient = spy(client); doReturn(mockConnectionsClient).when(spyClient).createConnectionsClient(); diff --git a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorToolTest.java b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorToolTest.java index 9ed3c67da..4287a8e6b 100644 --- a/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorToolTest.java +++ b/core/src/test/java/com/google/adk/tools/applicationintegrationtoolset/IntegrationConnectorToolTest.java @@ -8,13 +8,15 @@ import static org.mockito.Mockito.when; import com.google.adk.tools.ToolContext; -import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor; +import com.google.auth.Credentials; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.genai.types.FunctionDeclaration; import com.google.genai.types.Schema; import com.google.genai.types.Type; import io.reactivex.rxjava3.observers.TestObserver; +import java.io.IOException; +import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.HashMap; @@ -34,16 +36,16 @@ public final class IntegrationConnectorToolTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - @Mock private HttpExecutor mockHttpExecutor; + @Mock private HttpClient mockHttpClient; @Mock private HttpResponse mockHttpResponse; @Mock private ToolContext mockToolContext; + @Mock private CredentialsHelper mockCredentialsHelper; + @Mock private Credentials mockCredentials; private IntegrationConnectorTool integrationTool; private IntegrationConnectorTool connectorTool; private IntegrationConnectorTool connectorToolWithAction; private IntegrationConnectorTool connectorToolWithServiceAccount; - private static final String MOCK_ACCESS_TOKEN = "test-token"; - private static final String MOCK_ACCESS_TOKEN_2 = "test-token-2"; private static final String MOCK_SERVICE_ACCOUNT_JSON = "{\"type\": \"service_account\",\"project_id\": \"test-project\",\"private_key_data\":" + " \"test-private-key-data\",\"client_email\": \"test-client-email\",\"client_id\":" @@ -84,7 +86,9 @@ public final class IntegrationConnectorToolTest { + " ,\\\"x-operation\\\":\\\"EXECUTE_ACTION\\\",\\\"requestBody\\\":{\\\"required\\\":true,\\\"content\\\":{\\\"application/json\\\":{\\\"schema\\\":{\\\"$ref\\\":\\\"#/components/schemas/ActionRequest\\\"}}}}}}},\\\"components\\\":{\\\"schemas\\\":{\\\"ActionRequest\\\":{\\\"type\\\":\\\"object\\\",\\\"required\\\":[\\\"connectionName\\\",\\\"action\\\",\\\"operation\\\"],\\\"properties\\\":{\\\"connectionName\\\":{\\\"type\\\":\\\"string\\\"},\\\"serviceName\\\":{\\\"type\\\":\\\"string\\\"},\\\"host\\\":{\\\"type\\\":\\\"string\\\"},\\\"action\\\":{\\\"type\\\":\\\"string\\\"},\\\"operation\\\":{\\\"type\\\":\\\"string\\\"}}}}}}\"}"; @Before - public void setUp() { + public void setUp() throws IOException { + when(mockCredentialsHelper.getGoogleCredentials(any())).thenReturn(mockCredentials); + integrationTool = new IntegrationConnectorTool( MOCK_INTEGRATION_OPEN_API_SPEC, @@ -95,7 +99,8 @@ public void setUp() { null, null, null, - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); connectorTool = new IntegrationConnectorTool( @@ -107,7 +112,8 @@ public void setUp() { "test-service", "test-host", null, - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); connectorToolWithAction = new IntegrationConnectorTool( @@ -119,7 +125,8 @@ public void setUp() { "test-service-action", "test-host-action", null, - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); connectorToolWithServiceAccount = new IntegrationConnectorTool( @@ -131,7 +138,8 @@ public void setUp() { "test-service-action", "test-host-action", MOCK_SERVICE_ACCOUNT_JSON, - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); } @Test @@ -193,7 +201,8 @@ public void integrationTool_declaration_operationNotFound_returnsEmpty() { null, null, null, - mockHttpExecutor); + mockHttpClient, + mockCredentialsHelper); Optional declarationOpt = badTool.declaration(); @@ -207,11 +216,10 @@ public void integrationTool_runAsync_success() throws Exception { Map inputArgs = new HashMap<>(ImmutableMap.of("username", "testuser")); IntegrationConnectorTool spyTool = spy(integrationTool); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN); when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(expectedResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); spyTool .runAsync(inputArgs, mockToolContext) @@ -229,10 +237,9 @@ public void connectorTool_runAsync_success() throws Exception { Map inputArgs = new HashMap<>(); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN); when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(expectedResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(), any()); TestObserver> testObserver = spyTool.runAsync(inputArgs, mockToolContext).test(); @@ -254,10 +261,10 @@ public void connectorToolWithAction_runAsync_success() throws Exception { Map inputArgs = new HashMap<>(); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN); when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(expectedResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(), any()); + + doReturn(mockHttpResponse).when(mockHttpClient).send(any(), any()); TestObserver> testObserver = spyTool.runAsync(inputArgs, mockToolContext).test(); @@ -277,12 +284,11 @@ public void runAsync_serviceAccountJson_throwsPermissionDenied() throws Exceptio String errorResponse = "{\"error\":{\"message\":\"Permission denied.\"}}"; Map inputArgs = new HashMap<>(ImmutableMap.of("username", "testuser")); IntegrationConnectorTool spyTool = spy(connectorToolWithServiceAccount); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN); when(mockHttpResponse.statusCode()).thenReturn(403); when(mockHttpResponse.body()).thenReturn(errorResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); String expectedErrorMessage = "Error executing integration. Status: 403 , Response: " + errorResponse; @@ -303,10 +309,9 @@ public void connectorToolWithServiceAccount_runAsync_success() throws Exception Map inputArgs = new HashMap<>(); inputArgs.put("payload", "data"); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN_2); when(mockHttpResponse.statusCode()).thenReturn(200); when(mockHttpResponse.body()).thenReturn(expectedResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(), any()); TestObserver> testObserver = spyTool.runAsync(inputArgs, mockToolContext).test(); @@ -327,12 +332,11 @@ public void runAsync_httpError_returnsErrorMap() throws Exception { String errorResponse = "{\"error\":{\"message\":\"Permission denied.\"}}"; Map inputArgs = new HashMap<>(ImmutableMap.of("username", "testuser")); IntegrationConnectorTool spyTool = spy(integrationTool); - when(mockHttpExecutor.getToken()).thenReturn(MOCK_ACCESS_TOKEN); when(mockHttpResponse.statusCode()).thenReturn(403); when(mockHttpResponse.body()).thenReturn(errorResponse); - doReturn(mockHttpResponse).when(mockHttpExecutor).send(any(HttpRequest.class), any()); + doReturn(mockHttpResponse).when(mockHttpClient).send(any(HttpRequest.class), any()); String expectedErrorMessage = "Error executing integration. Status: 403 , Response: " + errorResponse;