mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-13 16:45:01 -05:00
Compare commits
22 Commits
feat/add-g
...
pr/dumians
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
251ef22839 | ||
|
|
8dc4bd7dd6 | ||
|
|
a00d0edcd5 | ||
|
|
d1eb1799a0 | ||
|
|
2b81d6099a | ||
|
|
53865b6e21 | ||
|
|
6843b46328 | ||
|
|
05152f732d | ||
|
|
a48101b3c5 | ||
|
|
418d6d791e | ||
|
|
452d686750 | ||
|
|
8fb74263a7 | ||
|
|
09f3bc7959 | ||
|
|
7970b8787e | ||
|
|
0b7a86ae58 | ||
|
|
0797142103 | ||
|
|
f26750e834 | ||
|
|
c46d7d6fa0 | ||
|
|
5e2034d146 | ||
|
|
e2272ccdbc | ||
|
|
97f68129f5 | ||
|
|
fea96fed03 |
@@ -943,7 +943,7 @@ steps:
|
||||
# Install the C compiler and Oracle SDK headers needed for cgo
|
||||
dnf install -y gcc oracle-instantclient-devel
|
||||
# Install Go
|
||||
curl -L -o go.tar.gz "https://go.dev/dl/go1.25.1.linux-amd64.tar.gz"
|
||||
curl -L -o go.tar.gz "https://go.dev/dl/go1.25.5.linux-amd64.tar.gz"
|
||||
tar -C /usr/local -xzf go.tar.gz
|
||||
export PATH="/usr/local/go/bin:$$PATH"
|
||||
|
||||
|
||||
1
.github/release-please.yml
vendored
1
.github/release-please.yml
vendored
@@ -37,6 +37,7 @@ extraFiles: [
|
||||
"docs/en/how-to/connect-ide/postgres_mcp.md",
|
||||
"docs/en/how-to/connect-ide/neo4j_mcp.md",
|
||||
"docs/en/how-to/connect-ide/sqlite_mcp.md",
|
||||
"docs/en/how-to/connect-ide/oracle_mcp.md",
|
||||
"gemini-extension.json",
|
||||
{
|
||||
"type": "json",
|
||||
|
||||
499
cmd/root_test.go
499
cmd/root_test.go
@@ -618,6 +618,505 @@ func TestSingleEdit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrebuiltTools(t *testing.T) {
|
||||
// Get prebuilt configs
|
||||
alloydb_omni_config, _ := prebuiltconfigs.Get("alloydb-omni")
|
||||
alloydb_admin_config, _ := prebuiltconfigs.Get("alloydb-postgres-admin")
|
||||
alloydb_config, _ := prebuiltconfigs.Get("alloydb-postgres")
|
||||
bigquery_config, _ := prebuiltconfigs.Get("bigquery")
|
||||
clickhouse_config, _ := prebuiltconfigs.Get("clickhouse")
|
||||
cloudsqlpg_config, _ := prebuiltconfigs.Get("cloud-sql-postgres")
|
||||
cloudsqlpg_admin_config, _ := prebuiltconfigs.Get("cloud-sql-postgres-admin")
|
||||
cloudsqlmysql_config, _ := prebuiltconfigs.Get("cloud-sql-mysql")
|
||||
cloudsqlmysql_admin_config, _ := prebuiltconfigs.Get("cloud-sql-mysql-admin")
|
||||
cloudsqlmssql_config, _ := prebuiltconfigs.Get("cloud-sql-mssql")
|
||||
cloudsqlmssql_admin_config, _ := prebuiltconfigs.Get("cloud-sql-mssql-admin")
|
||||
dataplex_config, _ := prebuiltconfigs.Get("dataplex")
|
||||
firestoreconfig, _ := prebuiltconfigs.Get("firestore")
|
||||
mysql_config, _ := prebuiltconfigs.Get("mysql")
|
||||
mssql_config, _ := prebuiltconfigs.Get("mssql")
|
||||
looker_config, _ := prebuiltconfigs.Get("looker")
|
||||
lookerca_config, _ := prebuiltconfigs.Get("looker-conversational-analytics")
|
||||
postgresconfig, _ := prebuiltconfigs.Get("postgres")
|
||||
spanner_config, _ := prebuiltconfigs.Get("spanner")
|
||||
spannerpg_config, _ := prebuiltconfigs.Get("spanner-postgres")
|
||||
mindsdb_config, _ := prebuiltconfigs.Get("mindsdb")
|
||||
sqlite_config, _ := prebuiltconfigs.Get("sqlite")
|
||||
neo4jconfig, _ := prebuiltconfigs.Get("neo4j")
|
||||
alloydbobsvconfig, _ := prebuiltconfigs.Get("alloydb-postgres-observability")
|
||||
cloudsqlpgobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-postgres-observability")
|
||||
cloudsqlmysqlobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-mysql-observability")
|
||||
cloudsqlmssqlobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-mssql-observability")
|
||||
serverless_spark_config, _ := prebuiltconfigs.Get("serverless-spark")
|
||||
cloudhealthcare_config, _ := prebuiltconfigs.Get("cloud-healthcare")
|
||||
snowflake_config, _ := prebuiltconfigs.Get("snowflake")
|
||||
|
||||
// Set environment variables
|
||||
t.Setenv("API_KEY", "your_api_key")
|
||||
|
||||
t.Setenv("BIGQUERY_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("DATAPLEX_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("FIRESTORE_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("FIRESTORE_DATABASE", "your_firestore_db_name")
|
||||
|
||||
t.Setenv("SPANNER_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("SPANNER_INSTANCE", "your_spanner_instance")
|
||||
t.Setenv("SPANNER_DATABASE", "your_spanner_db")
|
||||
|
||||
t.Setenv("ALLOYDB_POSTGRES_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("ALLOYDB_POSTGRES_REGION", "your_gcp_region")
|
||||
t.Setenv("ALLOYDB_POSTGRES_CLUSTER", "your_alloydb_cluster")
|
||||
t.Setenv("ALLOYDB_POSTGRES_INSTANCE", "your_alloydb_instance")
|
||||
t.Setenv("ALLOYDB_POSTGRES_DATABASE", "your_alloydb_db")
|
||||
t.Setenv("ALLOYDB_POSTGRES_USER", "your_alloydb_user")
|
||||
t.Setenv("ALLOYDB_POSTGRES_PASSWORD", "your_alloydb_password")
|
||||
|
||||
t.Setenv("ALLOYDB_OMNI_HOST", "localhost")
|
||||
t.Setenv("ALLOYDB_OMNI_PORT", "5432")
|
||||
t.Setenv("ALLOYDB_OMNI_DATABASE", "your_alloydb_db")
|
||||
t.Setenv("ALLOYDB_OMNI_USER", "your_alloydb_user")
|
||||
t.Setenv("ALLOYDB_OMNI_PASSWORD", "your_alloydb_password")
|
||||
|
||||
t.Setenv("CLICKHOUSE_PROTOCOL", "your_clickhouse_protocol")
|
||||
t.Setenv("CLICKHOUSE_DATABASE", "your_clickhouse_database")
|
||||
t.Setenv("CLICKHOUSE_PASSWORD", "your_clickhouse_password")
|
||||
t.Setenv("CLICKHOUSE_USER", "your_clickhouse_user")
|
||||
t.Setenv("CLICKHOUSE_HOST", "your_clickhosue_host")
|
||||
t.Setenv("CLICKHOUSE_PORT", "8123")
|
||||
|
||||
t.Setenv("CLOUD_SQL_POSTGRES_PROJECT", "your_pg_project")
|
||||
t.Setenv("CLOUD_SQL_POSTGRES_INSTANCE", "your_pg_instance")
|
||||
t.Setenv("CLOUD_SQL_POSTGRES_DATABASE", "your_pg_db")
|
||||
t.Setenv("CLOUD_SQL_POSTGRES_REGION", "your_pg_region")
|
||||
t.Setenv("CLOUD_SQL_POSTGRES_USER", "your_pg_user")
|
||||
t.Setenv("CLOUD_SQL_POSTGRES_PASS", "your_pg_pass")
|
||||
|
||||
t.Setenv("CLOUD_SQL_MYSQL_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("CLOUD_SQL_MYSQL_REGION", "your_gcp_region")
|
||||
t.Setenv("CLOUD_SQL_MYSQL_INSTANCE", "your_instance")
|
||||
t.Setenv("CLOUD_SQL_MYSQL_DATABASE", "your_cloudsql_mysql_db")
|
||||
t.Setenv("CLOUD_SQL_MYSQL_USER", "your_cloudsql_mysql_user")
|
||||
t.Setenv("CLOUD_SQL_MYSQL_PASSWORD", "your_cloudsql_mysql_password")
|
||||
|
||||
t.Setenv("CLOUD_SQL_MSSQL_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("CLOUD_SQL_MSSQL_REGION", "your_gcp_region")
|
||||
t.Setenv("CLOUD_SQL_MSSQL_INSTANCE", "your_cloudsql_mssql_instance")
|
||||
t.Setenv("CLOUD_SQL_MSSQL_DATABASE", "your_cloudsql_mssql_db")
|
||||
t.Setenv("CLOUD_SQL_MSSQL_IP_ADDRESS", "127.0.0.1")
|
||||
t.Setenv("CLOUD_SQL_MSSQL_USER", "your_cloudsql_mssql_user")
|
||||
t.Setenv("CLOUD_SQL_MSSQL_PASSWORD", "your_cloudsql_mssql_password")
|
||||
t.Setenv("CLOUD_SQL_POSTGRES_PASSWORD", "your_cloudsql_pg_password")
|
||||
|
||||
t.Setenv("SERVERLESS_SPARK_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("SERVERLESS_SPARK_LOCATION", "your_gcp_location")
|
||||
|
||||
t.Setenv("POSTGRES_HOST", "localhost")
|
||||
t.Setenv("POSTGRES_PORT", "5432")
|
||||
t.Setenv("POSTGRES_DATABASE", "your_postgres_db")
|
||||
t.Setenv("POSTGRES_USER", "your_postgres_user")
|
||||
t.Setenv("POSTGRES_PASSWORD", "your_postgres_password")
|
||||
|
||||
t.Setenv("MYSQL_HOST", "localhost")
|
||||
t.Setenv("MYSQL_PORT", "3306")
|
||||
t.Setenv("MYSQL_DATABASE", "your_mysql_db")
|
||||
t.Setenv("MYSQL_USER", "your_mysql_user")
|
||||
t.Setenv("MYSQL_PASSWORD", "your_mysql_password")
|
||||
|
||||
t.Setenv("MSSQL_HOST", "localhost")
|
||||
t.Setenv("MSSQL_PORT", "1433")
|
||||
t.Setenv("MSSQL_DATABASE", "your_mssql_db")
|
||||
t.Setenv("MSSQL_USER", "your_mssql_user")
|
||||
t.Setenv("MSSQL_PASSWORD", "your_mssql_password")
|
||||
|
||||
t.Setenv("MINDSDB_HOST", "localhost")
|
||||
t.Setenv("MINDSDB_PORT", "47334")
|
||||
t.Setenv("MINDSDB_DATABASE", "your_mindsdb_db")
|
||||
t.Setenv("MINDSDB_USER", "your_mindsdb_user")
|
||||
t.Setenv("MINDSDB_PASS", "your_mindsdb_password")
|
||||
|
||||
t.Setenv("LOOKER_BASE_URL", "https://your_company.looker.com")
|
||||
t.Setenv("LOOKER_CLIENT_ID", "your_looker_client_id")
|
||||
t.Setenv("LOOKER_CLIENT_SECRET", "your_looker_client_secret")
|
||||
t.Setenv("LOOKER_VERIFY_SSL", "true")
|
||||
|
||||
t.Setenv("LOOKER_PROJECT", "your_project_id")
|
||||
t.Setenv("LOOKER_LOCATION", "us")
|
||||
|
||||
t.Setenv("SQLITE_DATABASE", "test.db")
|
||||
|
||||
t.Setenv("NEO4J_URI", "bolt://localhost:7687")
|
||||
t.Setenv("NEO4J_DATABASE", "neo4j")
|
||||
t.Setenv("NEO4J_USERNAME", "your_neo4j_user")
|
||||
t.Setenv("NEO4J_PASSWORD", "your_neo4j_password")
|
||||
|
||||
t.Setenv("CLOUD_HEALTHCARE_PROJECT", "your_gcp_project_id")
|
||||
t.Setenv("CLOUD_HEALTHCARE_REGION", "your_gcp_region")
|
||||
t.Setenv("CLOUD_HEALTHCARE_DATASET", "your_healthcare_dataset")
|
||||
|
||||
t.Setenv("SNOWFLAKE_ACCOUNT", "your_account")
|
||||
t.Setenv("SNOWFLAKE_USER", "your_username")
|
||||
t.Setenv("SNOWFLAKE_PASSWORD", "your_pass")
|
||||
t.Setenv("SNOWFLAKE_DATABASE", "your_db")
|
||||
t.Setenv("SNOWFLAKE_SCHEMA", "your_schema")
|
||||
t.Setenv("SNOWFLAKE_WAREHOUSE", "your_wh")
|
||||
t.Setenv("SNOWFLAKE_ROLE", "your_role")
|
||||
|
||||
t.Setenv("ORACLE_USERNAME", "your_oracle_db_username")
|
||||
t.Setenv("ORACLE_PASS", "your_oracle_db_password")
|
||||
t.Setenv("ORACLE_HOST", "your_oracle_db_host")
|
||||
t.Setenv("ORACLE_PORT", "your_oracle_db_port")
|
||||
t.Setenv("ORACLE_SERVICE_NAME", "your_oracle_service_name")
|
||||
t.Setenv("ORACLE_USE_OCI", "your_oracle_db_use_oci")
|
||||
t.Setenv("ORACLE_WALLET_LOCATION", "your_path_to_oracldb_wallet")
|
||||
t.Setenv("ORACLE_TNS_ADMIN", "your_path_to_tns_admin")
|
||||
|
||||
ctx, err := testutils.ContextWithNewLogger()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
tcs := []struct {
|
||||
name string
|
||||
in []byte
|
||||
wantToolset server.ToolsetConfigs
|
||||
}{
|
||||
{
|
||||
name: "alloydb omni prebuilt tools",
|
||||
in: alloydb_omni_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"alloydb_omni_database_tools": tools.ToolsetConfig{
|
||||
Name: "alloydb_omni_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_columnar_configurations", "list_columnar_recommended_columns", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alloydb postgres admin prebuilt tools",
|
||||
in: alloydb_admin_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"alloydb_postgres_admin_tools": tools.ToolsetConfig{
|
||||
Name: "alloydb_postgres_admin_tools",
|
||||
ToolNames: []string{"create_cluster", "wait_for_operation", "create_instance", "list_clusters", "list_instances", "list_users", "create_user", "get_cluster", "get_instance", "get_user"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsql pg admin prebuilt tools",
|
||||
in: cloudsqlpg_admin_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_postgres_admin_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_postgres_admin_tools",
|
||||
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "postgres_upgrade_precheck", "clone_instance", "create_backup", "restore_backup"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsql mysql admin prebuilt tools",
|
||||
in: cloudsqlmysql_admin_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_mysql_admin_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_mysql_admin_tools",
|
||||
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "clone_instance", "create_backup", "restore_backup"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsql mssql admin prebuilt tools",
|
||||
in: cloudsqlmssql_admin_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_mssql_admin_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_mssql_admin_tools",
|
||||
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "clone_instance", "create_backup", "restore_backup"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alloydb prebuilt tools",
|
||||
in: alloydb_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"alloydb_postgres_database_tools": tools.ToolsetConfig{
|
||||
Name: "alloydb_postgres_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bigquery prebuilt tools",
|
||||
in: bigquery_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"bigquery_database_tools": tools.ToolsetConfig{
|
||||
Name: "bigquery_database_tools",
|
||||
ToolNames: []string{"analyze_contribution", "ask_data_insights", "execute_sql", "forecast", "get_dataset_info", "get_table_info", "list_dataset_ids", "list_table_ids", "search_catalog"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "clickhouse prebuilt tools",
|
||||
in: clickhouse_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"clickhouse_database_tools": tools.ToolsetConfig{
|
||||
Name: "clickhouse_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_databases", "list_tables"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsqlpg prebuilt tools",
|
||||
in: cloudsqlpg_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_postgres_database_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_postgres_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsqlmysql prebuilt tools",
|
||||
in: cloudsqlmysql_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_mysql_database_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_mysql_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables", "get_query_plan", "list_active_queries", "list_tables_missing_unique_indexes", "list_table_fragmentation"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsqlmssql prebuilt tools",
|
||||
in: cloudsqlmssql_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_mssql_database_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_mssql_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dataplex prebuilt tools",
|
||||
in: dataplex_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"dataplex_tools": tools.ToolsetConfig{
|
||||
Name: "dataplex_tools",
|
||||
ToolNames: []string{"search_entries", "lookup_entry", "search_aspect_types"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "serverless spark prebuilt tools",
|
||||
in: serverless_spark_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"serverless_spark_tools": tools.ToolsetConfig{
|
||||
Name: "serverless_spark_tools",
|
||||
ToolNames: []string{"list_batches", "get_batch", "cancel_batch", "create_pyspark_batch", "create_spark_batch"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "firestore prebuilt tools",
|
||||
in: firestoreconfig,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"firestore_database_tools": tools.ToolsetConfig{
|
||||
Name: "firestore_database_tools",
|
||||
ToolNames: []string{"get_documents", "add_documents", "update_document", "list_collections", "delete_documents", "query_collection", "get_rules", "validate_rules"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mysql prebuilt tools",
|
||||
in: mysql_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"mysql_database_tools": tools.ToolsetConfig{
|
||||
Name: "mysql_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables", "get_query_plan", "list_active_queries", "list_tables_missing_unique_indexes", "list_table_fragmentation"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mssql prebuilt tools",
|
||||
in: mssql_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"mssql_database_tools": tools.ToolsetConfig{
|
||||
Name: "mssql_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "looker prebuilt tools",
|
||||
in: looker_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"looker_tools": tools.ToolsetConfig{
|
||||
Name: "looker_tools",
|
||||
ToolNames: []string{"get_models", "get_explores", "get_dimensions", "get_measures", "get_filters", "get_parameters", "query", "query_sql", "query_url", "get_looks", "run_look", "make_look", "get_dashboards", "run_dashboard", "make_dashboard", "add_dashboard_element", "add_dashboard_filter", "generate_embed_url", "health_pulse", "health_analyze", "health_vacuum", "dev_mode", "get_projects", "get_project_files", "get_project_file", "create_project_file", "update_project_file", "delete_project_file", "validate_project", "get_connections", "get_connection_schemas", "get_connection_databases", "get_connection_tables", "get_connection_table_columns"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "looker-conversational-analytics prebuilt tools",
|
||||
in: lookerca_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"looker_conversational_analytics_tools": tools.ToolsetConfig{
|
||||
Name: "looker_conversational_analytics_tools",
|
||||
ToolNames: []string{"ask_data_insights", "get_models", "get_explores"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "postgres prebuilt tools",
|
||||
in: postgresconfig,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"postgres_database_tools": tools.ToolsetConfig{
|
||||
Name: "postgres_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "spanner prebuilt tools",
|
||||
in: spanner_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"spanner-database-tools": tools.ToolsetConfig{
|
||||
Name: "spanner-database-tools",
|
||||
ToolNames: []string{"execute_sql", "execute_sql_dql", "list_tables", "list_graphs"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "spanner pg prebuilt tools",
|
||||
in: spannerpg_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"spanner_postgres_database_tools": tools.ToolsetConfig{
|
||||
Name: "spanner_postgres_database_tools",
|
||||
ToolNames: []string{"execute_sql", "execute_sql_dql", "list_tables"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mindsdb prebuilt tools",
|
||||
in: mindsdb_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"mindsdb-tools": tools.ToolsetConfig{
|
||||
Name: "mindsdb-tools",
|
||||
ToolNames: []string{"mindsdb-execute-sql", "mindsdb-sql"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sqlite prebuilt tools",
|
||||
in: sqlite_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"sqlite_database_tools": tools.ToolsetConfig{
|
||||
Name: "sqlite_database_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "neo4j prebuilt tools",
|
||||
in: neo4jconfig,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"neo4j_database_tools": tools.ToolsetConfig{
|
||||
Name: "neo4j_database_tools",
|
||||
ToolNames: []string{"execute_cypher", "get_schema"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alloydb postgres observability prebuilt tools",
|
||||
in: alloydbobsvconfig,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"alloydb_postgres_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||
Name: "alloydb_postgres_cloud_monitoring_tools",
|
||||
ToolNames: []string{"get_system_metrics", "get_query_metrics"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsql postgres observability prebuilt tools",
|
||||
in: cloudsqlpgobsvconfig,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_postgres_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_postgres_cloud_monitoring_tools",
|
||||
ToolNames: []string{"get_system_metrics", "get_query_metrics"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsql mysql observability prebuilt tools",
|
||||
in: cloudsqlmysqlobsvconfig,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_mysql_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_mysql_cloud_monitoring_tools",
|
||||
ToolNames: []string{"get_system_metrics", "get_query_metrics"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloudsql mssql observability prebuilt tools",
|
||||
in: cloudsqlmssqlobsvconfig,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_sql_mssql_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_sql_mssql_cloud_monitoring_tools",
|
||||
ToolNames: []string{"get_system_metrics"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cloud healthcare prebuilt tools",
|
||||
in: cloudhealthcare_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"cloud_healthcare_dataset_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_healthcare_dataset_tools",
|
||||
ToolNames: []string{"get_dataset", "list_dicom_stores", "list_fhir_stores"},
|
||||
},
|
||||
"cloud_healthcare_fhir_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_healthcare_fhir_tools",
|
||||
ToolNames: []string{"get_fhir_store", "get_fhir_store_metrics", "get_fhir_resource", "fhir_patient_search", "fhir_patient_everything", "fhir_fetch_page"},
|
||||
},
|
||||
"cloud_healthcare_dicom_tools": tools.ToolsetConfig{
|
||||
Name: "cloud_healthcare_dicom_tools",
|
||||
ToolNames: []string{"get_dicom_store", "get_dicom_store_metrics", "search_dicom_studies", "search_dicom_series", "search_dicom_instances", "retrieve_rendered_dicom_instance"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Snowflake prebuilt tool",
|
||||
in: snowflake_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"snowflake_tools": tools.ToolsetConfig{
|
||||
Name: "snowflake_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Oracle prebuilt tool",
|
||||
in: oracle_config,
|
||||
wantToolset: server.ToolsetConfigs{
|
||||
"oracle_tools": tools.ToolsetConfig{
|
||||
Name: "oracle_tools",
|
||||
ToolNames: []string{"execute_sql", "list_tables","list_active_sessions","list_top_sql_by_resource","get_query_plan","list_tablespace_usage"},
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
toolsFile, err := parseToolsFile(ctx, tc.in)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse input: %v", err)
|
||||
}
|
||||
if diff := cmp.Diff(tc.wantToolset, toolsFile.Toolsets); diff != "" {
|
||||
t.Fatalf("incorrect tools parse: diff %v", diff)
|
||||
}
|
||||
// Prebuilt configs do not have prompts, so assert empty maps.
|
||||
if len(toolsFile.Prompts) != 0 {
|
||||
t.Fatalf("expected empty prompts map for prebuilt config, got: %v", toolsFile.Prompts)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutuallyExclusiveFlags(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
335
docs/en/how-to/connect-ide/oracle_mcp.md
Normal file
335
docs/en/how-to/connect-ide/oracle_mcp.md
Normal file
@@ -0,0 +1,335 @@
|
||||
---
|
||||
title: "Oracle using MCP"
|
||||
type: docs
|
||||
weight: 2
|
||||
description: >
|
||||
Connect your IDE to Oracle using Toolbox.
|
||||
---
|
||||
|
||||
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is
|
||||
an open protocol for connecting Large Language Models (LLMs) to data sources
|
||||
like Oracle. This guide covers how to use [MCP Toolbox for Databases][toolbox]
|
||||
to expose your developer assistant tools to an Oracle instance:
|
||||
|
||||
* [Cursor][cursor]
|
||||
* [Windsurf][windsurf] (Codium)
|
||||
* [Visual Studio Code][vscode] (Copilot)
|
||||
* [Cline][cline] (VS Code extension)
|
||||
* [Claude desktop][claudedesktop]
|
||||
* [Claude code][claudecode]
|
||||
* [Gemini CLI][geminicli]
|
||||
* [Gemini Code Assist][geminicodeassist]
|
||||
|
||||
[toolbox]: https://github.com/googleapis/genai-toolbox
|
||||
[cursor]: #configure-your-mcp-client
|
||||
[windsurf]: #configure-your-mcp-client
|
||||
[vscode]: #configure-your-mcp-client
|
||||
[cline]: #configure-your-mcp-client
|
||||
[claudedesktop]: #configure-your-mcp-client
|
||||
[claudecode]: #configure-your-mcp-client
|
||||
[geminicli]: #configure-your-mcp-client
|
||||
[geminicodeassist]: #configure-your-mcp-client
|
||||
|
||||
## Set up the database
|
||||
|
||||
1. Create or select an Oracle instance.
|
||||
|
||||
1. Create or reuse a database user and have the username and password ready.
|
||||
|
||||
## Install MCP Toolbox
|
||||
|
||||
1. Download the latest version of Toolbox as a binary. Select the [correct
|
||||
binary](https://github.com/googleapis/genai-toolbox/releases) corresponding
|
||||
to your OS and CPU architecture. You are required to use Toolbox version
|
||||
V0.26.0+:
|
||||
|
||||
<!-- {x-release-please-start-version} -->
|
||||
{{< tabpane persist=header >}}
|
||||
{{< tab header="linux/amd64" lang="bash" >}}
|
||||
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/linux/amd64/toolbox
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab header="darwin/arm64" lang="bash" >}}
|
||||
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/darwin/arm64/toolbox
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab header="darwin/amd64" lang="bash" >}}
|
||||
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/darwin/amd64/toolbox
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab header="windows/amd64" lang="bash" >}}
|
||||
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/windows/amd64/toolbox.exe
|
||||
{{< /tab >}}
|
||||
{{< /tabpane >}}
|
||||
<!-- {x-release-please-end} -->
|
||||
|
||||
1. Make the binary executable:
|
||||
|
||||
```bash
|
||||
chmod +x toolbox
|
||||
```
|
||||
|
||||
1. Verify the installation:
|
||||
|
||||
```bash
|
||||
./toolbox --version
|
||||
```
|
||||
|
||||
## Configure your MCP Client
|
||||
|
||||
{{< tabpane text=true >}}
|
||||
{{% tab header="Claude code" lang="en" %}}
|
||||
|
||||
1. Install [Claude
|
||||
Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
|
||||
1. Create a `.mcp.json` file in your project root if it doesn't exist.
|
||||
1. Add the following configuration, replace the environment variables with your
|
||||
values, and save:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracledb","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": "",
|
||||
"ORACLE_WALLET_LOCATION": "",
|
||||
"ORACLE_USE_OCI": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Restart Claude code to apply the new configuration.
|
||||
{{% /tab %}}
|
||||
|
||||
{{% tab header="Claude desktop" lang="en" %}}
|
||||
|
||||
1. Open Claude desktop and navigate to Settings.
|
||||
1. Under the Developer tab, tap Edit Config to open the configuration file.
|
||||
1. Add the following configuration, replace the environment variables with your
|
||||
values, and save:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracledb","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": "",
|
||||
"ORACLE_WALLET_LOCATION": "",
|
||||
"ORACLE_USE_OCI": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Restart Claude desktop.
|
||||
1. From the new chat screen, you should see a hammer (MCP) icon appear with the
|
||||
new MCP server available.
|
||||
{{% /tab %}}
|
||||
|
||||
{{% tab header="Cline" lang="en" %}}
|
||||
|
||||
1. Open the Cline extension in VS Code and tap
|
||||
the **MCP Servers** icon.
|
||||
1. Tap Configure MCP Servers to open the configuration file.
|
||||
1. Add the following configuration, replace the environment variables with your
|
||||
values, and save:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracledb","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": "",
|
||||
"ORACLE_WALLET_LOCATION": "",
|
||||
"ORACLE_USE_OCI": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. You should see a green active status after the server is successfully
|
||||
connected.
|
||||
{{% /tab %}}
|
||||
|
||||
{{% tab header="Cursor" lang="en" %}}
|
||||
|
||||
1. Create a `.cursor` directory in your project root if it doesn't exist.
|
||||
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
|
||||
1. Add the following configuration, replace the environment variables with your
|
||||
values, and save:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracledb","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": "",
|
||||
"ORACLE_WALLET_LOCATION": "",
|
||||
"ORACLE_USE_OCI": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Cursor and navigate to **Settings > Cursor
|
||||
Settings > MCP**. You should see a green active status after the server is
|
||||
successfully connected.
|
||||
{{% /tab %}}
|
||||
|
||||
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
|
||||
|
||||
1. Open VS Code and
|
||||
create a `.vscode` directory in your project root if it doesn't exist.
|
||||
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
|
||||
1. Add the following configuration, replace the environment variables with your
|
||||
values, and save:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracle","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{% /tab %}}
|
||||
|
||||
{{% tab header="Windsurf" lang="en" %}}
|
||||
|
||||
1. Open Windsurf and navigate to the
|
||||
Cascade assistant.
|
||||
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
|
||||
1. Add the following configuration, replace the environment variables with your
|
||||
values, and save:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracledb","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": "",
|
||||
"ORACLE_WALLET": "",
|
||||
"ORACLE_WALLET_PASSWORD": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
{{% /tab %}}
|
||||
|
||||
{{% tab header="Gemini CLI" lang="en" %}}
|
||||
|
||||
1. Install the Gemini CLI.
|
||||
1. In your working directory, create a folder named `.gemini`. Within it, create a `settings.json` file.
|
||||
1. Add the following configuration, replace the environment variables with your values, and then save:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracledb","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{% /tab %}}
|
||||
|
||||
{{% tab header="Gemini Code Assist" lang="en" %}}
|
||||
|
||||
1. Install the Gemini Code Assist extension in Visual Studio Code.
|
||||
1. Enable Agent Mode in Gemini Code Assist chat.
|
||||
1. In your working directory, create a folder named `.gemini`. Within it, create a `settings.json` file.
|
||||
1. Add the following configuration, replace the environment variables with your values, and then save:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"oracle": {
|
||||
"command": "./PATH/TO/toolbox",
|
||||
"args": ["--prebuilt","oracledb","--stdio"],
|
||||
"env": {
|
||||
"ORACLE_HOST": "",
|
||||
"ORACLE_PORT": "1521",
|
||||
"ORACLE_SERVICE": "",
|
||||
"ORACLE_USER": "",
|
||||
"ORACLE_PASSWORD": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{% /tab %}}
|
||||
{{< /tabpane >}}
|
||||
|
||||
## Use Tools
|
||||
|
||||
Your AI tool is now connected to Oracle using MCP. Try asking your AI
|
||||
assistant to list tables, create a table, or define and execute other SQL
|
||||
statements.
|
||||
|
||||
The following tools are available to the LLM:
|
||||
|
||||
1. **list_tables**: lists tables and descriptions
|
||||
1. **execute_sql**: execute any SQL statement
|
||||
|
||||
{{< notice note >}}
|
||||
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs
|
||||
will adapt to the tools available, so this shouldn't affect most users.
|
||||
{{< /notice >}}
|
||||
@@ -692,6 +692,34 @@ See [Usage Examples](../reference/cli.md#examples).
|
||||
* `execute_cypher`: Executes a Cypher query.
|
||||
* `get_schema`: Retrieves the schema of the Neo4j database.
|
||||
|
||||
|
||||
## Oracle
|
||||
|
||||
* `--prebuilt` value: `oracle`
|
||||
* **Environment Variables:**
|
||||
* `ORACLE_HOST`: The hostname or IP address of the Oracle server.
|
||||
* `ORACLE_PORT`: The port number for the Oracle server (Default: 1521).
|
||||
* `ORACLE_CONNECTION_STRING`: The
|
||||
* `ORACLE_USER`: The username for the Oracle DB instance.
|
||||
* `ORACLE_PASSWORD`: The password for the Oracle DB instance
|
||||
* `ORACLE_WALLET`: The path to Oracle DB Wallet file for the databases that support this authentication type
|
||||
* `ORACLE_USE_OCI`: true or false, The flag if the Oracle Database is deployed in cloud deployment. Default is false.
|
||||
* **Permissions:**
|
||||
* Database-level permissions (e.g., `SELECT`, `INSERT`) are required to execute queries.
|
||||
* **Tools:**
|
||||
* `execute_sql`: Executes a SQL query.
|
||||
* `list_tables`: Lists tables in the database.
|
||||
* `list_active_sessions`: Lists active database sessions.
|
||||
* `get_query_plan`: Gets the execution plan for a SQL statement.
|
||||
* `list_top_sql_by_resource`: Lists top SQL statements by resource usage.
|
||||
* `list_tablespace_usage`: Lists tablespace usage.
|
||||
* `list_invalid_objects`: Lists invalid objects.
|
||||
* `list_active_sessions`: Lists active database sessions.
|
||||
* `get_query_plan`: Gets the execution plan for a SQL statement.
|
||||
* `list_top_sql_by_resource`: Lists top SQL statements by resource usage.
|
||||
* `list_tablespace_usage`: Lists tablespace usage.
|
||||
* `list_invalid_objects`: Lists invalid objects.
|
||||
|
||||
## Google Cloud Healthcare API
|
||||
* `--prebuilt` value: `cloud-healthcare`
|
||||
* **Environment Variables:**
|
||||
|
||||
@@ -87,28 +87,41 @@ using a TNS (Transparent Network Substrate) alias.
|
||||
|
||||
## Examples
|
||||
|
||||
This example demonstrates the four connection methods you could choose from:
|
||||
### 1. Basic Connection (Host, Port, and Service Name)
|
||||
|
||||
```yaml
|
||||
kind: sources
|
||||
name: my-oracle-source
|
||||
type: oracle
|
||||
sources:
|
||||
my-oracle-source:
|
||||
kind: oracle
|
||||
host: 127.0.0.1
|
||||
port: 1521
|
||||
serviceName: XEPDB1
|
||||
user: ${USER_NAME}
|
||||
password: ${PASSWORD}
|
||||
```
|
||||
|
||||
# --- Choose one connection method ---
|
||||
# 1. Host, Port, and Service Name
|
||||
host: 127.0.0.1
|
||||
port: 1521
|
||||
serviceName: XEPDB1
|
||||
### 2. Direct Connection String
|
||||
|
||||
# 2. Direct Connection String
|
||||
connectionString: "127.0.0.1:1521/XEPDB1"
|
||||
```yaml
|
||||
sources:
|
||||
my-oracle-source:
|
||||
kind: oracle
|
||||
connectionString: "127.0.0.1:1521/XEPDB1"
|
||||
user: ${USER_NAME}
|
||||
password: ${PASSWORD}
|
||||
```
|
||||
|
||||
# 3. TNS Alias (requires tnsnames.ora)
|
||||
tnsAlias: "MY_DB_ALIAS"
|
||||
tnsAdmin: "/opt/oracle/network/admin" # Optional: overrides TNS_ADMIN env var
|
||||
### 3. TNS Alias (requires tnsnames.ora)
|
||||
|
||||
user: ${USER_NAME}
|
||||
password: ${PASSWORD}
|
||||
```yaml
|
||||
sources:
|
||||
my-oracle-source:
|
||||
kind: oracle
|
||||
tnsAlias: "MY_DB_ALIAS"
|
||||
tnsAdmin: "/opt/oracle/network/admin" # Optional: overrides TNS_ADMIN env var
|
||||
user: ${USER_NAME}
|
||||
password: ${PASSWORD}
|
||||
useOCI: true # tnsAlias requires useOCI to be true
|
||||
|
||||
# Optional: Set to true to use the OCI-based driver for advanced features (Requires Oracle Instant Client)
|
||||
```
|
||||
@@ -168,3 +181,4 @@ instead of hardcoding your secrets into the configuration file.
|
||||
| tnsAlias | string | false | A TNS alias from a `tnsnames.ora` file. Use as an alternative to `host`/`port` or `connectionString`. |
|
||||
| tnsAdmin | string | false | Path to the directory containing the `tnsnames.ora` file. This overrides the `TNS_ADMIN` environment variable if it is set. |
|
||||
| useOCI | bool | false | If true, uses the OCI-based driver (godror) which supports Oracle Wallet/Kerberos but requires the Oracle Instant Client libraries to be installed. Defaults to false (pure Go driver). |
|
||||
| walletLocation | string | false | Path to the directory containing the wallet files for the pure Go driver (`useOCI: false`). |
|
||||
|
||||
7
docs/en/samples/oracle/_index.md
Normal file
7
docs/en/samples/oracle/_index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: "OracleDB"
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
How to get started with Toolbox using Oracle Database.
|
||||
---
|
||||
@@ -47,6 +47,7 @@ var expectedToolSources = []string{
|
||||
"mysql",
|
||||
"neo4j",
|
||||
"oceanbase",
|
||||
"oracledb",
|
||||
"postgres",
|
||||
"serverless-spark",
|
||||
"singlestore",
|
||||
@@ -131,6 +132,8 @@ func TestGetPrebuiltTool(t *testing.T) {
|
||||
neo4jconfig := getOrFatal(t, "neo4j")
|
||||
healthcare_config := getOrFatal(t, "cloud-healthcare")
|
||||
snowflake_config := getOrFatal(t, "snowflake")
|
||||
oracle_config := getOrFatal(t,"oracledb")
|
||||
|
||||
if len(alloydb_omni_config) <= 0 {
|
||||
t.Fatalf("unexpected error: could not fetch alloydb omni prebuilt tools yaml")
|
||||
}
|
||||
@@ -230,6 +233,10 @@ func TestGetPrebuiltTool(t *testing.T) {
|
||||
if len(snowflake_config) <= 0 {
|
||||
t.Fatalf("unexpected error: could not fetch snowflake prebuilt tools yaml")
|
||||
}
|
||||
|
||||
if len(oracle_config) <= 0 {
|
||||
t.Fatalf("unexpected error: could not fetch oracle prebuilt tools yaml")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailGetPrebuiltTool(t *testing.T) {
|
||||
|
||||
121
internal/prebuiltconfigs/tools/oracledb.yaml
Normal file
121
internal/prebuiltconfigs/tools/oracledb.yaml
Normal file
@@ -0,0 +1,121 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
sources:
|
||||
oracle-source:
|
||||
kind: "oracle"
|
||||
connectionString: ${ORACLE_CONNECTION_STRING}
|
||||
walletLocation: ${ORACLE_WALLET:}
|
||||
user: ${ORACLE_USER}
|
||||
password: ${ORACLE_PASSWORD}
|
||||
useOCI: ${ORACLE_USE_OCI:false}
|
||||
|
||||
tools:
|
||||
|
||||
list_tables:
|
||||
kind: oracle-sql
|
||||
source: oracle-source
|
||||
description: "Lists all user tables in the connected schema, including segment size, row count, and last analyzed date. Filters by a comma-separated list of names. If names are omitted, lists all tables in the current user's schema."
|
||||
statement: SELECT table_name from user_tables;
|
||||
|
||||
list_active_sessions:
|
||||
kind: oracle-sql
|
||||
source: oracle-source
|
||||
description: "List the top N (default 50) currently running database sessions (STATUS='ACTIVE'), showing SID, OS User, Program, and the current SQL statement text."
|
||||
statement: SELECT
|
||||
s.sid,
|
||||
s.serial#,
|
||||
s.username,
|
||||
s.osuser,
|
||||
s.program,
|
||||
s.status,
|
||||
s.wait_class,
|
||||
s.event,
|
||||
sql.sql_text
|
||||
FROM
|
||||
v$session s,
|
||||
v$sql sql
|
||||
WHERE
|
||||
s.status = 'ACTIVE'
|
||||
AND s.sql_id = sql.sql_id (+)
|
||||
AND s.audsid != userenv('sessionid') -- Exclude current session
|
||||
ORDER BY s.last_call_et DESC
|
||||
FETCH FIRST COALESCE(10) ROWS ONLY;
|
||||
|
||||
get_query_plan:
|
||||
kind: oracle-sql
|
||||
source: oracle-source
|
||||
description: "Generate a full execution plan for a single SQL statement. This can be used to analyze query performance without execution. Requires the SQL statement as input. following is an example EXPLAIN PLAN FOR {{&query}};"
|
||||
statement: SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
|
||||
|
||||
list_top_sql_by_resource:
|
||||
kind: oracle-sql
|
||||
source: oracle-source
|
||||
description: "List the top N SQL statements from the library cache based on a chosen resource metric (CPU, I/O, or Elapsed Time), following is an example of the sql"
|
||||
statement: SELECT
|
||||
sql_id,
|
||||
executions,
|
||||
buffer_gets,
|
||||
disk_reads,
|
||||
cpu_time / 1000000 AS cpu_seconds,
|
||||
elapsed_time / 1000000 AS elapsed_seconds
|
||||
FROM
|
||||
v$sql
|
||||
FETCH FIRST 5 ROWS ONLY;
|
||||
|
||||
list_tablespace_usage:
|
||||
kind: oracle-sql
|
||||
source: oracle-source
|
||||
description: "List tablespace names, total size, free space, and used percentage to monitor storage utilization."
|
||||
statement: SELECT
|
||||
t.tablespace_name,
|
||||
TO_CHAR(t.total_bytes / 1024 / 1024, '99,999.00') AS total_mb,
|
||||
TO_CHAR(SUM(d.bytes) / 1024 / 1024, '99,999.00') AS free_mb,
|
||||
TO_CHAR((t.total_bytes - SUM(d.bytes)) / t.total_bytes * 100, '99.00') AS used_pct
|
||||
FROM
|
||||
(SELECT tablespace_name, SUM(bytes) AS total_bytes FROM dba_data_files GROUP BY tablespace_name) t,
|
||||
dba_free_space d
|
||||
WHERE
|
||||
t.tablespace_name = d.tablespace_name (+)
|
||||
GROUP BY
|
||||
t.tablespace_name, t.total_bytes
|
||||
ORDER BY
|
||||
used_pct DESC;
|
||||
|
||||
list_invalid_objects:
|
||||
kind: oracle-sql
|
||||
source: oracle-source
|
||||
description: "Lists all database objects that are in an invalid state, requiring recompilation (e.g., procedures, functions, views)."
|
||||
statement: SELECT
|
||||
owner,
|
||||
object_type,
|
||||
object_name,
|
||||
status
|
||||
FROM
|
||||
dba_objects
|
||||
WHERE
|
||||
status = 'INVALID'
|
||||
AND owner NOT IN ('SYS', 'SYSTEM') -- Exclude system schemas for clarity
|
||||
ORDER BY
|
||||
owner, object_type, object_name;
|
||||
|
||||
toolsets:
|
||||
oracle_database_tools:
|
||||
- execute_sql
|
||||
- list_tables
|
||||
- list_active_sessions
|
||||
- get_query_plan
|
||||
- list_top_sql_by_resource
|
||||
- list_tablespace_usage
|
||||
- list_invalid_objects
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright © 2025, Oracle and/or its affiliates.
|
||||
|
||||
package oracle
|
||||
|
||||
import (
|
||||
|
||||
@@ -190,4 +190,4 @@ func TestFailParseFromYaml(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,23 +9,31 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/googleapis/genai-toolbox/internal/sources/oracle"
|
||||
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||
"github.com/googleapis/genai-toolbox/tests"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
OracleSourceType = "oracle"
|
||||
OracleToolType = "oracle-sql"
|
||||
OracleHost = os.Getenv("ORACLE_HOST")
|
||||
OraclePort = os.Getenv("ORACLE_PORT")
|
||||
OracleUser = os.Getenv("ORACLE_USER")
|
||||
OraclePass = os.Getenv("ORACLE_PASS")
|
||||
OracleServerName = os.Getenv("ORACLE_SERVER_NAME")
|
||||
OracleUseOCI = os.Getenv("ORACLE_USE_OCI")
|
||||
OracleWalletLocation = os.Getenv("ORACLE_WALLET_LOCATION")
|
||||
OracleTnsAdmin = os.Getenv("ORACLE_TNS_ADMIN")
|
||||
|
||||
OracleConnStr = fmt.Sprintf(
|
||||
"%s:%s/%s", OracleHost, "1521", OracleServerName)
|
||||
"%s:%s/%s", OracleHost, "1521", OracleServerName) // Default port 1521??
|
||||
)
|
||||
|
||||
func getOracleVars(t *testing.T) map[string]any {
|
||||
@@ -43,12 +51,77 @@ func getOracleVars(t *testing.T) map[string]any {
|
||||
return map[string]any{
|
||||
"type": OracleSourceType,
|
||||
"connectionString": OracleConnStr,
|
||||
"useOCI": true,
|
||||
"useOCI": OracleUseOCI,
|
||||
"walletLocation": OracleWalletLocation,
|
||||
"tnsAdmin": OracleTnsAdmin,
|
||||
"host": OracleHost,
|
||||
"port": OraclePort,
|
||||
"service": OracleServerName,
|
||||
"user": OracleUser,
|
||||
"password": OraclePass,
|
||||
}
|
||||
}
|
||||
|
||||
// getOracleConfigFromEnv constructs an oracle.Config from environment variables.
|
||||
func getOracleConfigFromEnv(t *testing.T) oracle.Config {
|
||||
t.Helper()
|
||||
vars := getOracleVars(t)
|
||||
|
||||
port, err := strconv.Atoi(vars["port"].(string))
|
||||
if err != nil && vars["port"].(string) != "" {
|
||||
t.Fatalf("invalid ORACLE_PORT: %v", err)
|
||||
}
|
||||
|
||||
useOCI, err := strconv.ParseBool(vars["useOCI"].(string))
|
||||
if err != nil && vars["useOCI"].(string) != "" {
|
||||
useOCI = false
|
||||
}
|
||||
|
||||
return oracle.Config{
|
||||
Name: "test-oracle-instance",
|
||||
Kind: vars["type"].(string),
|
||||
User: vars["user"].(string),
|
||||
Password: vars["password"].(string),
|
||||
Host: vars["host"].(string),
|
||||
Port: port,
|
||||
ServiceName: vars["service"].(string),
|
||||
WalletLocation: vars["walletLocation"].(string),
|
||||
TnsAdmin: vars["tnsAdmin"].(string),
|
||||
UseOCI: useOCI,
|
||||
}
|
||||
}
|
||||
|
||||
// setOracleEnv sets Oracle-related environment variables for testing and returns a cleanup function.
|
||||
func setOracleEnv(t *testing.T, host, user, password, service, port, connStr, tnsAlias, tnsAdmin, walletLocation string, useOCI bool) func() {
|
||||
t.Helper()
|
||||
|
||||
original := map[string]string{
|
||||
"ORACLE_HOST": os.Getenv("ORACLE_HOST"),
|
||||
"ORACLE_USER": os.Getenv("ORACLE_USER"),
|
||||
"ORACLE_PASSWORD": os.Getenv("ORACLE_PASSWORD"),
|
||||
"ORACLE_SERVICE": os.Getenv("ORACLE_SERVICE"),
|
||||
"ORACLE_PORT": os.Getenv("ORACLE_PORT"),
|
||||
"ORACLE_TNS_ADMIN": os.Getenv("ORACLE_TNS_ADMIN"),
|
||||
"ORACLE_WALLET_LOCATION": os.Getenv("ORACLE_WALLET_LOCATION"),
|
||||
"ORACLE_USE_OCI": os.Getenv("ORACLE_USE_OCI"),
|
||||
}
|
||||
|
||||
os.Setenv("ORACLE_HOST", host)
|
||||
os.Setenv("ORACLE_USER", user)
|
||||
os.Setenv("ORACLE_PASSWORD", password)
|
||||
os.Setenv("ORACLE_SERVICE", service)
|
||||
os.Setenv("ORACLE_PORT", port)
|
||||
os.Setenv("ORACLE_TNS_ADMIN", tnsAdmin)
|
||||
os.Setenv("ORACLE_WALLET_LOCATION", walletLocation)
|
||||
os.Setenv("ORACLE_USE_OCI", fmt.Sprintf("%v", useOCI))
|
||||
|
||||
return func() {
|
||||
for k, v := range original {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copied over from oracle.go
|
||||
func initOracleConnection(ctx context.Context, user, pass, connStr string) (*sql.DB, error) {
|
||||
// Build the full Oracle connection string for godror driver
|
||||
@@ -68,7 +141,9 @@ func initOracleConnection(ctx context.Context, user, pass, connStr string) (*sql
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// TestOracleSimpleToolEndpoints tests Oracle SQL tool endpoints
|
||||
func TestOracleSimpleToolEndpoints(t *testing.T) {
|
||||
|
||||
sourceConfig := getOracleVars(t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
@@ -135,6 +210,130 @@ func TestOracleSimpleToolEndpoints(t *testing.T) {
|
||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam)
|
||||
}
|
||||
|
||||
|
||||
// TestOracleConnectionPureGoWithWallet tests pure Go driver connection with wallet
|
||||
func TestOracleConnectionPureGoWithWallet(t *testing.T) {
|
||||
t.Parallel()
|
||||
// This test expects the connection to fail because the wallet file won't exist.
|
||||
// It verifies that the walletLocation parameter is correctly passed to the pure Go driver.
|
||||
|
||||
// Save original env vars and restore them at the end
|
||||
cleanup := setOracleEnv(t,
|
||||
OracleHost, OracleUser, OraclePass, OracleServerName, OraclePort, // Use existing base connection details
|
||||
"", // connectionString
|
||||
"", // tnsAlias
|
||||
"", // tnsAdmin
|
||||
"/tmp/nonexistent_wallet", // walletLocation
|
||||
false, // useOCI
|
||||
)
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg := getOracleConfigFromEnv(t)
|
||||
_, err := cfg.Initialize(ctx, nil) // Pass nil for tracer as it's not critical for this test
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Expected connection to fail with non-existent wallet, but it succeeded")
|
||||
}
|
||||
|
||||
// Check for error message indicating wallet usage or connection failure related to wallet
|
||||
// The exact error message might vary depending on the go-ora version and OS.
|
||||
// We are looking for an error that suggests the wallet path was attempted.
|
||||
expectedErrorSubstring := "wallet"
|
||||
if !strings.Contains(strings.ToLower(err.Error()), expectedErrorSubstring) {
|
||||
t.Errorf("Expected error message to contain '%s' (case-insensitive) but got: %v", expectedErrorSubstring, err)
|
||||
}
|
||||
t.Logf("Connection failed as expected (Pure Go with Wallet): %v", err)
|
||||
}
|
||||
|
||||
// TestOracleConnectionOCI tests OCI driver connection without wallet
|
||||
func TestOracleConnectionOCI(t *testing.T) {
|
||||
t.Parallel()
|
||||
// This test verifies that the useOCI=true parameter is correctly passed to the OCI driver.
|
||||
// It will likely fail if Oracle Instant Client is not installed or configured.
|
||||
|
||||
// Save original env vars and restore them at the end
|
||||
cleanup := setOracleEnv(t,
|
||||
OracleHost, OracleUser, OraclePass, OracleServerName, OraclePort, // Use existing base connection details
|
||||
"", // connectionString
|
||||
"", // tnsAlias
|
||||
"", // tnsAdmin
|
||||
"", // walletLocation
|
||||
true, // useOCI
|
||||
)
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg := getOracleConfigFromEnv(t)
|
||||
_, err := cfg.Initialize(ctx, nil)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Expected connection to fail (OCI driver without Instant Client), but it succeeded")
|
||||
}
|
||||
|
||||
// Check for error message indicating OCI driver usage or connection failure related to OCI.
|
||||
// Common errors include "OCI environment not initialized", "driver: bad connection", etc.
|
||||
expectedErrorSubstrings := []string{"oci", "driver", "connection"}
|
||||
foundExpectedError := false
|
||||
for _, sub := range expectedErrorSubstrings {
|
||||
if strings.Contains(strings.ToLower(err.Error()), sub) {
|
||||
foundExpectedError = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundExpectedError {
|
||||
t.Errorf("Expected error message to contain one of %v (case-insensitive) but got: %v", expectedErrorSubstrings, err)
|
||||
}
|
||||
t.Logf("Connection failed as expected (OCI Driver): %v", err)
|
||||
}
|
||||
|
||||
// TestOracleConnectionOCIWithWallet tests OCI driver connection with TNS Admin and Wallet
|
||||
func TestOracleConnectionOCIWithWallet(t *testing.T) {
|
||||
t.Parallel()
|
||||
// This test verifies that useOCI=true and tnsAdmin parameters are correctly passed for OCI wallet.
|
||||
// It will likely fail due to missing tnsnames.ora and wallet files.
|
||||
|
||||
// Save original env vars and restore them at the end
|
||||
cleanup := setOracleEnv(t,
|
||||
"", OracleUser, OraclePass, "", "", // Unset host/port/service for TNS alias, but keep user/pass
|
||||
"", // connectionString
|
||||
"MY_TNS_ALIAS", // tnsAlias
|
||||
"/tmp/nonexistent_tns_admin", // tnsAdmin
|
||||
"", // walletLocation
|
||||
true, // useOCI
|
||||
)
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cfg := getOracleConfigFromEnv(t)
|
||||
_, err := cfg.Initialize(ctx, nil)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("Expected connection to fail (OCI driver with TNS Admin/Wallet), but it succeeded")
|
||||
}
|
||||
|
||||
// Check for error message indicating TNS Admin/Wallet usage or connection failure.
|
||||
expectedErrorSubstrings := []string{"tns", "wallet", "oci", "driver", "connection"}
|
||||
foundExpectedError := false
|
||||
for _, sub := range expectedErrorSubstrings {
|
||||
if strings.Contains(strings.ToLower(err.Error()), sub) {
|
||||
foundExpectedError = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundExpectedError {
|
||||
t.Errorf("Expected error message to contain one of %v (case-insensitive) but got: %v", expectedErrorSubstrings, err)
|
||||
}
|
||||
t.Logf("Connection failed as expected (OCI Driver with TNS Admin/Wallet): %v", err)
|
||||
}
|
||||
|
||||
//test utils
|
||||
func setupOracleTable(t *testing.T, ctx context.Context, pool *sql.DB, createStatement, insertStatement, tableName string, params []any) func(*testing.T) {
|
||||
err := pool.PingContext(ctx)
|
||||
if err != nil {
|
||||
@@ -241,4 +440,4 @@ func dropAllUserTables(t *testing.T, ctx context.Context, db *sql.DB) {
|
||||
t.Logf("failed to drop table %s: %v", tableName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
248
tests/oracle/oracle_test.go
Normal file
248
tests/oracle/oracle_test.go
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package oracle_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/googleapis/genai-toolbox/internal/server"
|
||||
"github.com/googleapis/genai-toolbox/internal/sources/oracle"
|
||||
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||
)
|
||||
|
||||
func TestParseFromYamlOracle(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
want server.SourceConfigs
|
||||
}{
|
||||
{
|
||||
desc: "connection string and useOCI=true",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-cs:
|
||||
kind: oracle
|
||||
connectionString: "my-host:1521/XEPDB1"
|
||||
user: my_user
|
||||
password: my_pass
|
||||
useOCI: true
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-oracle-cs": oracle.Config{
|
||||
Name: "my-oracle-cs",
|
||||
Kind: oracle.SourceKind,
|
||||
ConnectionString: "my-host:1521/XEPDB1",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
UseOCI: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "host/port/serviceName and default useOCI=false",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-host:
|
||||
kind: oracle
|
||||
host: my-host
|
||||
port: 1521
|
||||
serviceName: ORCLPDB
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-oracle-host": oracle.Config{
|
||||
Name: "my-oracle-host",
|
||||
Kind: oracle.SourceKind,
|
||||
Host: "my-host",
|
||||
Port: 1521,
|
||||
ServiceName: "ORCLPDB",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
UseOCI: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tnsAlias and TnsAdmin specified with explicit useOCI=true",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-tns-oci:
|
||||
kind: oracle
|
||||
tnsAlias: FINANCE_DB
|
||||
tnsAdmin: /opt/oracle/network/admin
|
||||
user: my_user
|
||||
password: my_pass
|
||||
useOCI: true
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-oracle-tns-oci": oracle.Config{
|
||||
Name: "my-oracle-tns-oci",
|
||||
Kind: oracle.SourceKind,
|
||||
TnsAlias: "FINANCE_DB",
|
||||
TnsAdmin: "/opt/oracle/network/admin",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
UseOCI: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "connection string and walletLocation",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-wallet:
|
||||
kind: oracle
|
||||
connectionString: "my-host:1521/XEPDB1"
|
||||
user: my_user
|
||||
password: my_pass
|
||||
walletLocation: "/path/to/wallet"
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-oracle-wallet": oracle.Config{
|
||||
Name: "my-oracle-wallet",
|
||||
Kind: oracle.SourceKind,
|
||||
ConnectionString: "my-host:1521/XEPDB1",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
WalletLocation: "/path/to/wallet",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to unmarshal: %s", err)
|
||||
}
|
||||
if !cmp.Equal(tc.want, got.Sources) {
|
||||
t.Fatalf("incorrect parse:\nwant: %v\ngot: %v\ndiff: %s", tc.want, got.Sources, cmp.Diff(tc.want, got.Sources))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailParseFromYamlOracle(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-instance:
|
||||
kind: oracle
|
||||
host: my-host
|
||||
serviceName: ORCL
|
||||
user: my_user
|
||||
password: my_pass
|
||||
extraField: value
|
||||
`,
|
||||
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": [1:1] unknown field \"extraField\"\n> 1 | extraField: value\n ^\n 2 | host: my-host\n 3 | kind: oracle\n 4 | password: my_pass\n 5 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required password field",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-instance:
|
||||
kind: oracle
|
||||
host: my-host
|
||||
serviceName: ORCL
|
||||
user: my_user
|
||||
`,
|
||||
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": Key: 'Config.Password' Error:Field validation for 'Password' failed on the 'required' tag",
|
||||
},
|
||||
{
|
||||
desc: "missing connection method fields (validate fails)",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-instance:
|
||||
kind: oracle
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": invalid Oracle configuration: must provide one of: 'tns_alias', 'connection_string', or both 'host' and 'service_name'",
|
||||
},
|
||||
{
|
||||
desc: "multiple connection methods provided (validate fails)",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-instance:
|
||||
kind: oracle
|
||||
host: my-host
|
||||
serviceName: ORCL
|
||||
connectionString: "my-host:1521/XEPDB1"
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": invalid Oracle configuration: provide only one connection method: 'tns_alias', 'connection_string', or 'host'+'service_name'",
|
||||
},
|
||||
{
|
||||
desc: "fail on tnsAdmin with useOCI=false",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-fail:
|
||||
kind: oracle
|
||||
tnsAlias: FINANCE_DB
|
||||
tnsAdmin: /opt/oracle/network/admin
|
||||
user: my_user
|
||||
password: my_pass
|
||||
useOCI: false
|
||||
`,
|
||||
err: "unable to parse source \"my-oracle-fail\" as \"oracle\": invalid Oracle configuration: `tnsAdmin` can only be used when `UseOCI` is true, or use `walletLocation` instead",
|
||||
},
|
||||
{
|
||||
desc: "fail on walletLocation with useOCI=true",
|
||||
in: `
|
||||
sources:
|
||||
my-oracle-fail:
|
||||
kind: oracle
|
||||
connectionString: "my-host:1521/XEPDB1"
|
||||
user: my_user
|
||||
password: my_pass
|
||||
walletLocation: "/path/to/wallet"
|
||||
useOCI: true
|
||||
`,
|
||||
err: "unable to parse source \"my-oracle-fail\" as \"oracle\": invalid Oracle configuration: when using an OCI driver, use `tnsAdmin` to specify credentials file location instead",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := strings.ReplaceAll(err.Error(), "\r", "")
|
||||
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error:\ngot:\n%q\nwant:\n%q\n", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user