Skip to content

Commit 1e114cd

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Introduced ConnectionClient and IntegrationClient to get OPENAPISPEC of connection
PiperOrigin-RevId: 783786672
1 parent 32aeee6 commit 1e114cd

File tree

9 files changed

+2694
-337
lines changed

9 files changed

+2694
-337
lines changed

core/src/main/java/com/google/adk/tools/applicationintegrationtoolset/ApplicationIntegrationToolset.java

Lines changed: 124 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
package com.google.adk.tools.applicationintegrationtoolset;
22

3+
import static com.google.common.base.Strings.isNullOrEmpty;
4+
35
import com.fasterxml.jackson.databind.JsonNode;
46
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.fasterxml.jackson.databind.node.ObjectNode;
58
import com.google.adk.tools.BaseTool;
6-
import com.google.adk.tools.applicationintegrationtoolset.ApplicationIntegrationTool.DefaultHttpExecutor;
7-
import com.google.adk.tools.applicationintegrationtoolset.ApplicationIntegrationTool.HttpExecutor;
8-
import com.google.auth.oauth2.GoogleCredentials;
9-
import com.google.common.collect.ImmutableList;
10-
import com.google.common.collect.ImmutableMap;
11-
import java.io.IOException;
12-
import java.net.URI;
13-
import java.net.http.HttpRequest;
14-
import java.net.http.HttpResponse;
9+
import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.DefaultHttpExecutor;
10+
import com.google.adk.tools.applicationintegrationtoolset.IntegrationConnectorTool.HttpExecutor;
1511
import java.util.ArrayList;
16-
import java.util.Arrays;
1712
import java.util.Iterator;
1813
import java.util.List;
1914
import java.util.Map;
@@ -23,10 +18,15 @@
2318
public class ApplicationIntegrationToolset {
2419
String project;
2520
String location;
26-
String integration;
27-
List<String> triggers;
28-
private final HttpExecutor httpExecutor;
21+
@Nullable String integration;
22+
@Nullable List<String> triggers;
23+
@Nullable String connection;
24+
@Nullable Map<String, List<String>> entityOperations;
25+
@Nullable List<String> actions;
26+
@Nullable String toolNamePrefix;
27+
@Nullable String toolInstructions;
2928
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
29+
HttpExecutor httpExecutor;
3030

3131
/**
3232
* ApplicationIntegrationToolset generates tools from a given Application Integration resource.
@@ -35,80 +35,80 @@ public class ApplicationIntegrationToolset {
3535
*
3636
* <p>integrationTool = new ApplicationIntegrationToolset( project="test-project",
3737
* location="us-central1", integration="test-integration",
38-
* triggers=ImmutableList.of("api_trigger/test_trigger", "api_trigger/test_trigger_2"));
38+
* triggers=ImmutableList.of("api_trigger/test_trigger",
39+
* "api_trigger/test_trigger_2"),connection=null,enitityOperations=null,actions=null,toolNamePrefix="test-integration-tool",toolInstructions="This
40+
* tool is used to get response from test-integration.");
41+
*
42+
* <p>connectionTool = new ApplicationIntegrationToolset( project="test-project",
43+
* location="us-central1", integration=null, triggers=null, connection="test-connection",
44+
* entityOperations=ImmutableMap.of("Entity1", ImmutableList.of("LIST", "GET", "UPDATE")),
45+
* "Entity2", ImmutableList.of()), actions=ImmutableList.of("ExecuteCustomQuery"),
46+
* toolNamePrefix="test-tool", toolInstructions="This tool is used to list, get and update issues
47+
* in Jira.");
3948
*
4049
* @param project The GCP project ID.
4150
* @param location The GCP location of integration.
4251
* @param integration The integration name.
4352
* @param triggers(Optional) The list of trigger ids in the integration.
53+
* @param connection(Optional) The connection name.
54+
* @param entityOperations(Optional) The entity operations.
55+
* @param actions(Optional) The actions.
56+
* @param toolNamePrefix(Optional) The tool name prefix.
57+
* @param toolInstructions(Optional) The tool instructions.
4458
*/
4559
public ApplicationIntegrationToolset(
46-
String project, String location, String integration, List<String> triggers) {
47-
this(project, location, integration, triggers, new DefaultHttpExecutor());
60+
String project,
61+
String location,
62+
String integration,
63+
List<String> triggers,
64+
String connection,
65+
Map<String, List<String>> entityOperations,
66+
List<String> actions,
67+
String toolNamePrefix,
68+
String toolInstructions) {
69+
this(
70+
project,
71+
location,
72+
integration,
73+
triggers,
74+
connection,
75+
entityOperations,
76+
actions,
77+
toolNamePrefix,
78+
toolInstructions,
79+
new DefaultHttpExecutor());
4880
}
4981

5082
ApplicationIntegrationToolset(
5183
String project,
5284
String location,
5385
String integration,
5486
List<String> triggers,
87+
String connection,
88+
Map<String, List<String>> entityOperations,
89+
List<String> actions,
90+
String toolNamePrefix,
91+
String toolInstructions,
5592
HttpExecutor httpExecutor) {
5693
this.project = project;
5794
this.location = location;
5895
this.integration = integration;
5996
this.triggers = triggers;
97+
this.connection = connection;
98+
this.entityOperations = entityOperations;
99+
this.actions = actions;
100+
this.toolNamePrefix = toolNamePrefix;
101+
this.toolInstructions = toolInstructions;
60102
this.httpExecutor = httpExecutor;
61103
}
62104

63-
String generateOpenApiSpec() throws Exception {
64-
String url =
65-
String.format(
66-
"https://%s-integrations.googleapis.com/v1/projects/%s/locations/%s:generateOpenApiSpec",
67-
this.location, this.project, this.location);
68-
69-
String jsonRequestBody =
70-
OBJECT_MAPPER.writeValueAsString(
71-
ImmutableMap.of(
72-
"apiTriggerResources",
73-
ImmutableList.of(
74-
ImmutableMap.of(
75-
"integrationResource",
76-
this.integration,
77-
"triggerId",
78-
Arrays.asList(this.triggers))),
79-
"fileFormat",
80-
"JSON"));
81-
HttpRequest request =
82-
HttpRequest.newBuilder()
83-
.uri(URI.create(url))
84-
.header("Authorization", "Bearer " + getAccessToken())
85-
.header("Content-Type", "application/json")
86-
.POST(HttpRequest.BodyPublishers.ofString(jsonRequestBody))
87-
.build();
88-
HttpResponse<String> response =
89-
httpExecutor.send(request, HttpResponse.BodyHandlers.ofString());
90-
91-
if (response.statusCode() < 200 || response.statusCode() >= 300) {
92-
throw new Exception("Error fetching OpenAPI spec. Status: " + response.statusCode());
93-
}
94-
return response.body();
95-
}
96-
97-
String getAccessToken() throws IOException {
98-
GoogleCredentials credentials =
99-
GoogleCredentials.getApplicationDefault()
100-
.createScoped(ImmutableList.of("https://www.googleapis.com/auth/cloud-platform"));
101-
credentials.refreshIfExpired();
102-
return credentials.getAccessToken().getTokenValue();
103-
}
104-
105105
List<String> getPathUrl(String openApiSchemaString) throws Exception {
106106
List<String> pathUrls = new ArrayList<>();
107107
JsonNode topLevelNode = OBJECT_MAPPER.readTree(openApiSchemaString);
108108
JsonNode specNode = topLevelNode.path("openApiSpec");
109109
if (specNode.isMissingNode() || !specNode.isTextual()) {
110110
throw new IllegalArgumentException(
111-
"API response must contain an 'openApiSpec' key with a string value.");
111+
"Failed to get OpenApiSpec, please check the project and region for the integration.");
112112
}
113113
JsonNode rootNode = OBJECT_MAPPER.readTree(specNode.asText());
114114
JsonNode pathsNode = rootNode.path("paths");
@@ -121,29 +121,74 @@ List<String> getPathUrl(String openApiSchemaString) throws Exception {
121121
return pathUrls;
122122
}
123123

124-
@Nullable String extractTriggerIdFromPath(String path) {
125-
String prefix = "triggerId=api_trigger/";
126-
int startIndex = path.indexOf(prefix);
127-
if (startIndex == -1) {
128-
return null;
129-
}
130-
return path.substring(startIndex + prefix.length());
131-
}
132-
133124
public List<BaseTool> getTools() throws Exception {
134-
String openApiSchemaString = generateOpenApiSpec();
135-
List<String> pathUrls = getPathUrl(openApiSchemaString);
136-
125+
String openApiSchemaString = null;
137126
List<BaseTool> tools = new ArrayList<>();
138-
for (String pathUrl : pathUrls) {
139-
String toolName = extractTriggerIdFromPath(pathUrl);
140-
if (toolName != null) {
141-
tools.add(new ApplicationIntegrationTool(openApiSchemaString, pathUrl, toolName, ""));
142-
} else {
143-
System.err.println(
144-
"Failed to get tool name , Please check the integration name , trigger id and location"
145-
+ " and project id.");
127+
if (!isNullOrEmpty(this.integration)) {
128+
IntegrationClient integrationClient =
129+
new IntegrationClient(
130+
this.project,
131+
this.location,
132+
this.integration,
133+
this.triggers,
134+
null,
135+
null,
136+
null,
137+
this.httpExecutor);
138+
openApiSchemaString = integrationClient.generateOpenApiSpec();
139+
List<String> pathUrls = getPathUrl(openApiSchemaString);
140+
for (String pathUrl : pathUrls) {
141+
String toolName = integrationClient.getOperationIdFromPathUrl(openApiSchemaString, pathUrl);
142+
if (toolName != null) {
143+
tools.add(
144+
new IntegrationConnectorTool(
145+
openApiSchemaString, pathUrl, toolName, "", null, null, null, this.httpExecutor));
146+
}
146147
}
148+
} else if (!isNullOrEmpty(this.connection)
149+
&& (this.entityOperations != null || this.actions != null)) {
150+
IntegrationClient integrationClient =
151+
new IntegrationClient(
152+
this.project,
153+
this.location,
154+
null,
155+
null,
156+
this.connection,
157+
this.entityOperations,
158+
this.actions,
159+
this.httpExecutor);
160+
ObjectNode parentOpenApiSpec = OBJECT_MAPPER.createObjectNode();
161+
ObjectNode openApiSpec =
162+
integrationClient.getOpenApiSpecForConnection(toolNamePrefix, toolInstructions);
163+
String openApiSpecString = OBJECT_MAPPER.writeValueAsString(openApiSpec);
164+
parentOpenApiSpec.put("openApiSpec", openApiSpecString);
165+
openApiSchemaString = OBJECT_MAPPER.writeValueAsString(parentOpenApiSpec);
166+
List<String> pathUrls = getPathUrl(openApiSchemaString);
167+
for (String pathUrl : pathUrls) {
168+
String toolName = integrationClient.getOperationIdFromPathUrl(openApiSchemaString, pathUrl);
169+
if (!isNullOrEmpty(toolName)) {
170+
ConnectionsClient connectionsClient =
171+
new ConnectionsClient(
172+
this.project, this.location, this.connection, this.httpExecutor, OBJECT_MAPPER);
173+
ConnectionsClient.ConnectionDetails connectionDetails =
174+
connectionsClient.getConnectionDetails();
175+
176+
tools.add(
177+
new IntegrationConnectorTool(
178+
openApiSchemaString,
179+
pathUrl,
180+
toolName,
181+
"",
182+
connectionDetails.name,
183+
connectionDetails.serviceName,
184+
connectionDetails.host,
185+
this.httpExecutor));
186+
}
187+
}
188+
} else {
189+
throw new IllegalArgumentException(
190+
"Invalid request, Either integration or (connection and"
191+
+ " (entityOperations or actions)) should be provided.");
147192
}
148193

149194
return tools;

0 commit comments

Comments
 (0)