Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -85,7 +85,8 @@ public ApplicationIntegrationToolset(
serviceAccountJson,
toolNamePrefix,
toolInstructions,
new DefaultHttpExecutor().createExecutor(serviceAccountJson));
HttpClient.newHttpClient(),
new GoogleCredentialsHelper());
}

ApplicationIntegrationToolset(
Expand All @@ -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;
Expand All @@ -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<String> getPathUrl(String openApiSchemaString) throws Exception {
Expand Down Expand Up @@ -145,7 +148,9 @@ private List<BaseTool> getAllTools() throws Exception {
null,
null,
null,
this.httpExecutor);
this.serviceAccountJson,
this.httpClient,
this.credentialsHelper);
openApiSchemaString = integrationClient.generateOpenApiSpec();
List<String> pathUrls = getPathUrl(openApiSchemaString);
for (String pathUrl : pathUrls) {
Expand All @@ -161,7 +166,8 @@ private List<BaseTool> getAllTools() throws Exception {
null,
null,
this.serviceAccountJson,
this.httpExecutor));
this.httpClient,
this.credentialsHelper));
}
}
} else if (!isNullOrEmpty(this.connection)
Expand All @@ -175,7 +181,9 @@ private List<BaseTool> 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);
Expand All @@ -188,7 +196,14 @@ private List<BaseTool> 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();

Expand All @@ -202,7 +217,8 @@ private List<BaseTool> getAllTools() throws Exception {
connectionDetails.serviceName,
connectionDetails.host,
this.serviceAccountJson,
this.httpExecutor));
this.httpClient,
this.credentialsHelper));
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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. */
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -173,16 +198,17 @@ public ActionSchema getActionSchema(String action) throws IOException, Interrupt
}

private HttpResponse<String> 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<String> response =
httpExecutor.send(request, HttpResponse.BodyHandlers.ofString());
httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());

if (response.statusCode() >= 400) {
String body = response.body();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, List<String>> entry : credentials.getRequestMetadata().entrySet()) {
for (String value : entry.getValue()) {
builder = builder.header(entry.getKey(), value);
}
}
return builder;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,14 +27,17 @@
* <p>This class provides methods for retrieving OpenAPI spec for an integration or a connection.
*/
public class IntegrationClient {
String project;
String location;
String integration;
List<String> triggers;
String connection;
Map<String, List<String>> entityOperations;
List<String> actions;
private final HttpExecutor httpExecutor;
private final String project;
private final String location;
private final String integration;
private final List<String> triggers;
private final String connection;
private final Map<String, List<String>> entityOperations;
private final List<String> actions;
private final String serviceAccountJson;
private final HttpClient httpClient;
private final CredentialsHelper credentialsHelper;

public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

IntegrationClient(
Expand All @@ -44,20 +48,66 @@ public class IntegrationClient {
String connection,
Map<String, List<String>> entityOperations,
List<String> actions,
HttpExecutor httpExecutor) {
String serviceAccountJson,
HttpClient httpClient,
CredentialsHelper credentialsHelper) {

this.project = project;
this.location = location;
this.integration = integration;
this.triggers = triggers;
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<String> triggers,
String connection,
Map<String, List<String>> entityOperations,
List<String> actions) {
this(
project,
location,
integration,
triggers,
connection,
entityOperations,
actions,
HttpClient.newHttpClient());
}

IntegrationClient(
String project,
String location,
String integration,
List<String> triggers,
String connection,
Map<String, List<String>> entityOperations,
List<String> 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

Expand Down Expand Up @@ -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<String> 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());
Expand Down Expand Up @@ -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);
}
}
Loading