#
tokens: 43582/50000 1/935 files (page 55/59)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 55 of 59. Use http://codebase.md/googleapis/genai-toolbox?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .ci
│   ├── continuous.release.cloudbuild.yaml
│   ├── generate_release_table.sh
│   ├── integration.cloudbuild.yaml
│   ├── quickstart_test
│   │   ├── go.integration.cloudbuild.yaml
│   │   ├── js.integration.cloudbuild.yaml
│   │   ├── py.integration.cloudbuild.yaml
│   │   ├── run_go_tests.sh
│   │   ├── run_js_tests.sh
│   │   ├── run_py_tests.sh
│   │   └── setup_hotels_sample.sql
│   ├── test_with_coverage.sh
│   └── versioned.release.cloudbuild.yaml
├── .github
│   ├── auto-label.yaml
│   ├── blunderbuss.yml
│   ├── CODEOWNERS
│   ├── header-checker-lint.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── label-sync.yml
│   ├── labels.yaml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── release-please.yml
│   ├── renovate.json5
│   ├── sync-repo-settings.yaml
│   └── workflows
│       ├── cloud_build_failure_reporter.yml
│       ├── deploy_dev_docs.yaml
│       ├── deploy_previous_version_docs.yaml
│       ├── deploy_versioned_docs.yaml
│       ├── docs_deploy.yaml
│       ├── docs_preview_clean.yaml
│       ├── docs_preview_deploy.yaml
│       ├── lint.yaml
│       ├── schedule_reporter.yml
│       ├── sync-labels.yaml
│       └── tests.yaml
├── .gitignore
├── .gitmodules
├── .golangci.yaml
├── .hugo
│   ├── archetypes
│   │   └── default.md
│   ├── assets
│   │   ├── icons
│   │   │   └── logo.svg
│   │   └── scss
│   │       ├── _styles_project.scss
│   │       └── _variables_project.scss
│   ├── go.mod
│   ├── go.sum
│   ├── hugo.toml
│   ├── layouts
│   │   ├── _default
│   │   │   └── home.releases.releases
│   │   ├── index.llms-full.txt
│   │   ├── index.llms.txt
│   │   ├── partials
│   │   │   ├── hooks
│   │   │   │   └── head-end.html
│   │   │   ├── navbar-version-selector.html
│   │   │   ├── page-meta-links.html
│   │   │   └── td
│   │   │       └── render-heading.html
│   │   ├── robot.txt
│   │   └── shortcodes
│   │       ├── include.html
│   │       ├── ipynb.html
│   │       └── regionInclude.html
│   ├── package-lock.json
│   ├── package.json
│   └── static
│       ├── favicons
│       │   ├── android-chrome-192x192.png
│       │   ├── android-chrome-512x512.png
│       │   ├── apple-touch-icon.png
│       │   ├── favicon-16x16.png
│       │   ├── favicon-32x32.png
│       │   └── favicon.ico
│       └── js
│           └── w3.js
├── CHANGELOG.md
├── cmd
│   ├── options_test.go
│   ├── options.go
│   ├── root_test.go
│   ├── root.go
│   └── version.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPER.md
├── Dockerfile
├── docs
│   └── en
│       ├── _index.md
│       ├── about
│       │   ├── _index.md
│       │   └── faq.md
│       ├── concepts
│       │   ├── _index.md
│       │   └── telemetry
│       │       ├── index.md
│       │       ├── telemetry_flow.png
│       │       └── telemetry_traces.png
│       ├── getting-started
│       │   ├── _index.md
│       │   ├── colab_quickstart.ipynb
│       │   ├── configure.md
│       │   ├── introduction
│       │   │   ├── _index.md
│       │   │   └── architecture.png
│       │   ├── local_quickstart_go.md
│       │   ├── local_quickstart_js.md
│       │   ├── local_quickstart.md
│       │   ├── mcp_quickstart
│       │   │   ├── _index.md
│       │   │   ├── inspector_tools.png
│       │   │   └── inspector.png
│       │   └── quickstart
│       │       ├── go
│       │       │   ├── adkgo
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── genAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── genkit
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── langchain
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── openAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   └── quickstart_test.go
│       │       ├── golden.txt
│       │       ├── js
│       │       │   ├── genAI
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── genkit
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── langchain
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── llamaindex
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   └── quickstart.test.js
│       │       ├── python
│       │       │   ├── __init__.py
│       │       │   ├── adk
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── core
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── langchain
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── llamaindex
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   └── quickstart_test.py
│       │       └── shared
│       │           ├── cloud_setup.md
│       │           ├── configure_toolbox.md
│       │           └── database_setup.md
│       ├── how-to
│       │   ├── _index.md
│       │   ├── connect_via_geminicli.md
│       │   ├── connect_via_mcp.md
│       │   ├── connect-ide
│       │   │   ├── _index.md
│       │   │   ├── alloydb_pg_admin_mcp.md
│       │   │   ├── alloydb_pg_mcp.md
│       │   │   ├── bigquery_mcp.md
│       │   │   ├── cloud_sql_mssql_admin_mcp.md
│       │   │   ├── cloud_sql_mssql_mcp.md
│       │   │   ├── cloud_sql_mysql_admin_mcp.md
│       │   │   ├── cloud_sql_mysql_mcp.md
│       │   │   ├── cloud_sql_pg_admin_mcp.md
│       │   │   ├── cloud_sql_pg_mcp.md
│       │   │   ├── firestore_mcp.md
│       │   │   ├── looker_mcp.md
│       │   │   ├── mssql_mcp.md
│       │   │   ├── mysql_mcp.md
│       │   │   ├── neo4j_mcp.md
│       │   │   ├── postgres_mcp.md
│       │   │   ├── spanner_mcp.md
│       │   │   └── sqlite_mcp.md
│       │   ├── deploy_docker.md
│       │   ├── deploy_gke.md
│       │   ├── deploy_toolbox.md
│       │   ├── export_telemetry.md
│       │   └── toolbox-ui
│       │       ├── edit-headers.gif
│       │       ├── edit-headers.png
│       │       ├── index.md
│       │       ├── optional-param-checked.png
│       │       ├── optional-param-unchecked.png
│       │       ├── run-tool.gif
│       │       ├── tools.png
│       │       └── toolsets.png
│       ├── reference
│       │   ├── _index.md
│       │   ├── cli.md
│       │   └── prebuilt-tools.md
│       ├── resources
│       │   ├── _index.md
│       │   ├── authServices
│       │   │   ├── _index.md
│       │   │   └── google.md
│       │   ├── sources
│       │   │   ├── _index.md
│       │   │   ├── alloydb-admin.md
│       │   │   ├── alloydb-pg.md
│       │   │   ├── bigquery.md
│       │   │   ├── bigtable.md
│       │   │   ├── cassandra.md
│       │   │   ├── clickhouse.md
│       │   │   ├── cloud-healthcare.md
│       │   │   ├── cloud-monitoring.md
│       │   │   ├── cloud-sql-admin.md
│       │   │   ├── cloud-sql-mssql.md
│       │   │   ├── cloud-sql-mysql.md
│       │   │   ├── cloud-sql-pg.md
│       │   │   ├── couchbase.md
│       │   │   ├── dataplex.md
│       │   │   ├── dgraph.md
│       │   │   ├── elasticsearch.md
│       │   │   ├── firebird.md
│       │   │   ├── firestore.md
│       │   │   ├── http.md
│       │   │   ├── looker.md
│       │   │   ├── mindsdb.md
│       │   │   ├── mongodb.md
│       │   │   ├── mssql.md
│       │   │   ├── mysql.md
│       │   │   ├── neo4j.md
│       │   │   ├── oceanbase.md
│       │   │   ├── oracle.md
│       │   │   ├── postgres.md
│       │   │   ├── redis.md
│       │   │   ├── serverless-spark.md
│       │   │   ├── singlestore.md
│       │   │   ├── spanner.md
│       │   │   ├── sqlite.md
│       │   │   ├── tidb.md
│       │   │   ├── trino.md
│       │   │   ├── valkey.md
│       │   │   └── yugabytedb.md
│       │   └── tools
│       │       ├── _index.md
│       │       ├── alloydb
│       │       │   ├── _index.md
│       │       │   ├── alloydb-create-cluster.md
│       │       │   ├── alloydb-create-instance.md
│       │       │   ├── alloydb-create-user.md
│       │       │   ├── alloydb-get-cluster.md
│       │       │   ├── alloydb-get-instance.md
│       │       │   ├── alloydb-get-user.md
│       │       │   ├── alloydb-list-clusters.md
│       │       │   ├── alloydb-list-instances.md
│       │       │   ├── alloydb-list-users.md
│       │       │   └── alloydb-wait-for-operation.md
│       │       ├── alloydbainl
│       │       │   ├── _index.md
│       │       │   └── alloydb-ai-nl.md
│       │       ├── bigquery
│       │       │   ├── _index.md
│       │       │   ├── bigquery-analyze-contribution.md
│       │       │   ├── bigquery-conversational-analytics.md
│       │       │   ├── bigquery-execute-sql.md
│       │       │   ├── bigquery-forecast.md
│       │       │   ├── bigquery-get-dataset-info.md
│       │       │   ├── bigquery-get-table-info.md
│       │       │   ├── bigquery-list-dataset-ids.md
│       │       │   ├── bigquery-list-table-ids.md
│       │       │   ├── bigquery-search-catalog.md
│       │       │   └── bigquery-sql.md
│       │       ├── bigtable
│       │       │   ├── _index.md
│       │       │   └── bigtable-sql.md
│       │       ├── cassandra
│       │       │   ├── _index.md
│       │       │   └── cassandra-cql.md
│       │       ├── clickhouse
│       │       │   ├── _index.md
│       │       │   ├── clickhouse-execute-sql.md
│       │       │   ├── clickhouse-list-databases.md
│       │       │   ├── clickhouse-list-tables.md
│       │       │   └── clickhouse-sql.md
│       │       ├── cloudhealthcare
│       │       │   ├── _index.md
│       │       │   ├── cloud-healthcare-fhir-fetch-page.md
│       │       │   ├── cloud-healthcare-fhir-patient-everything.md
│       │       │   ├── cloud-healthcare-fhir-patient-search.md
│       │       │   ├── cloud-healthcare-get-dataset.md
│       │       │   ├── cloud-healthcare-get-dicom-store-metrics.md
│       │       │   ├── cloud-healthcare-get-dicom-store.md
│       │       │   ├── cloud-healthcare-get-fhir-resource.md
│       │       │   ├── cloud-healthcare-get-fhir-store-metrics.md
│       │       │   ├── cloud-healthcare-get-fhir-store.md
│       │       │   ├── cloud-healthcare-list-dicom-stores.md
│       │       │   ├── cloud-healthcare-list-fhir-stores.md
│       │       │   ├── cloud-healthcare-retrieve-rendered-dicom-instance.md
│       │       │   ├── cloud-healthcare-search-dicom-instances.md
│       │       │   ├── cloud-healthcare-search-dicom-series.md
│       │       │   └── cloud-healthcare-search-dicom-studies.md
│       │       ├── cloudmonitoring
│       │       │   ├── _index.md
│       │       │   └── cloud-monitoring-query-prometheus.md
│       │       ├── cloudsql
│       │       │   ├── _index.md
│       │       │   ├── cloudsqlcreatedatabase.md
│       │       │   ├── cloudsqlcreateusers.md
│       │       │   ├── cloudsqlgetinstances.md
│       │       │   ├── cloudsqllistdatabases.md
│       │       │   ├── cloudsqllistinstances.md
│       │       │   ├── cloudsqlmssqlcreateinstance.md
│       │       │   ├── cloudsqlmysqlcreateinstance.md
│       │       │   ├── cloudsqlpgcreateinstances.md
│       │       │   └── cloudsqlwaitforoperation.md
│       │       ├── couchbase
│       │       │   ├── _index.md
│       │       │   └── couchbase-sql.md
│       │       ├── dataform
│       │       │   ├── _index.md
│       │       │   └── dataform-compile-local.md
│       │       ├── dataplex
│       │       │   ├── _index.md
│       │       │   ├── dataplex-lookup-entry.md
│       │       │   ├── dataplex-search-aspect-types.md
│       │       │   └── dataplex-search-entries.md
│       │       ├── dgraph
│       │       │   ├── _index.md
│       │       │   └── dgraph-dql.md
│       │       ├── elasticsearch
│       │       │   ├── _index.md
│       │       │   └── elasticsearch-esql.md
│       │       ├── firebird
│       │       │   ├── _index.md
│       │       │   ├── firebird-execute-sql.md
│       │       │   └── firebird-sql.md
│       │       ├── firestore
│       │       │   ├── _index.md
│       │       │   ├── firestore-add-documents.md
│       │       │   ├── firestore-delete-documents.md
│       │       │   ├── firestore-get-documents.md
│       │       │   ├── firestore-get-rules.md
│       │       │   ├── firestore-list-collections.md
│       │       │   ├── firestore-query-collection.md
│       │       │   ├── firestore-query.md
│       │       │   ├── firestore-update-document.md
│       │       │   └── firestore-validate-rules.md
│       │       ├── http
│       │       │   ├── _index.md
│       │       │   └── http.md
│       │       ├── looker
│       │       │   ├── _index.md
│       │       │   ├── looker-add-dashboard-element.md
│       │       │   ├── looker-conversational-analytics.md
│       │       │   ├── looker-create-project-file.md
│       │       │   ├── looker-delete-project-file.md
│       │       │   ├── looker-dev-mode.md
│       │       │   ├── looker-get-connection-databases.md
│       │       │   ├── looker-get-connection-schemas.md
│       │       │   ├── looker-get-connection-table-columns.md
│       │       │   ├── looker-get-connection-tables.md
│       │       │   ├── looker-get-connections.md
│       │       │   ├── looker-get-dashboards.md
│       │       │   ├── looker-get-dimensions.md
│       │       │   ├── looker-get-explores.md
│       │       │   ├── looker-get-filters.md
│       │       │   ├── looker-get-looks.md
│       │       │   ├── looker-get-measures.md
│       │       │   ├── looker-get-models.md
│       │       │   ├── looker-get-parameters.md
│       │       │   ├── looker-get-project-file.md
│       │       │   ├── looker-get-project-files.md
│       │       │   ├── looker-get-projects.md
│       │       │   ├── looker-health-analyze.md
│       │       │   ├── looker-health-pulse.md
│       │       │   ├── looker-health-vacuum.md
│       │       │   ├── looker-make-dashboard.md
│       │       │   ├── looker-make-look.md
│       │       │   ├── looker-query-sql.md
│       │       │   ├── looker-query-url.md
│       │       │   ├── looker-query.md
│       │       │   ├── looker-run-dashboard.md
│       │       │   ├── looker-run-look.md
│       │       │   └── looker-update-project-file.md
│       │       ├── mindsdb
│       │       │   ├── _index.md
│       │       │   ├── mindsdb-execute-sql.md
│       │       │   └── mindsdb-sql.md
│       │       ├── mongodb
│       │       │   ├── _index.md
│       │       │   ├── mongodb-aggregate.md
│       │       │   ├── mongodb-delete-many.md
│       │       │   ├── mongodb-delete-one.md
│       │       │   ├── mongodb-find-one.md
│       │       │   ├── mongodb-find.md
│       │       │   ├── mongodb-insert-many.md
│       │       │   ├── mongodb-insert-one.md
│       │       │   ├── mongodb-update-many.md
│       │       │   └── mongodb-update-one.md
│       │       ├── mssql
│       │       │   ├── _index.md
│       │       │   ├── mssql-execute-sql.md
│       │       │   ├── mssql-list-tables.md
│       │       │   └── mssql-sql.md
│       │       ├── mysql
│       │       │   ├── _index.md
│       │       │   ├── mysql-execute-sql.md
│       │       │   ├── mysql-list-active-queries.md
│       │       │   ├── mysql-list-table-fragmentation.md
│       │       │   ├── mysql-list-tables-missing-unique-indexes.md
│       │       │   ├── mysql-list-tables.md
│       │       │   └── mysql-sql.md
│       │       ├── neo4j
│       │       │   ├── _index.md
│       │       │   ├── neo4j-cypher.md
│       │       │   ├── neo4j-execute-cypher.md
│       │       │   └── neo4j-schema.md
│       │       ├── oceanbase
│       │       │   ├── _index.md
│       │       │   ├── oceanbase-execute-sql.md
│       │       │   └── oceanbase-sql.md
│       │       ├── oracle
│       │       │   ├── _index.md
│       │       │   ├── oracle-execute-sql.md
│       │       │   └── oracle-sql.md
│       │       ├── postgres
│       │       │   ├── _index.md
│       │       │   ├── postgres-execute-sql.md
│       │       │   ├── postgres-list-active-queries.md
│       │       │   ├── postgres-list-available-extensions.md
│       │       │   ├── postgres-list-installed-extensions.md
│       │       │   ├── postgres-list-schemas.md
│       │       │   ├── postgres-list-tables.md
│       │       │   ├── postgres-list-views.md
│       │       │   └── postgres-sql.md
│       │       ├── redis
│       │       │   ├── _index.md
│       │       │   └── redis.md
│       │       ├── serverless-spark
│       │       │   ├── _index.md
│       │       │   ├── serverless-spark-cancel-batch.md
│       │       │   ├── serverless-spark-get-batch.md
│       │       │   └── serverless-spark-list-batches.md
│       │       ├── singlestore
│       │       │   ├── _index.md
│       │       │   ├── singlestore-execute-sql.md
│       │       │   └── singlestore-sql.md
│       │       ├── spanner
│       │       │   ├── _index.md
│       │       │   ├── spanner-execute-sql.md
│       │       │   ├── spanner-list-tables.md
│       │       │   └── spanner-sql.md
│       │       ├── sqlite
│       │       │   ├── _index.md
│       │       │   ├── sqlite-execute-sql.md
│       │       │   └── sqlite-sql.md
│       │       ├── tidb
│       │       │   ├── _index.md
│       │       │   ├── tidb-execute-sql.md
│       │       │   └── tidb-sql.md
│       │       ├── trino
│       │       │   ├── _index.md
│       │       │   ├── trino-execute-sql.md
│       │       │   └── trino-sql.md
│       │       ├── utility
│       │       │   ├── _index.md
│       │       │   └── wait.md
│       │       ├── valkey
│       │       │   ├── _index.md
│       │       │   └── valkey.md
│       │       └── yuagbytedb
│       │           ├── _index.md
│       │           └── yugabytedb-sql.md
│       ├── samples
│       │   ├── _index.md
│       │   ├── alloydb
│       │   │   ├── _index.md
│       │   │   ├── ai-nl
│       │   │   │   ├── alloydb_ai_nl.ipynb
│       │   │   │   └── index.md
│       │   │   └── mcp_quickstart.md
│       │   ├── bigquery
│       │   │   ├── _index.md
│       │   │   ├── colab_quickstart_bigquery.ipynb
│       │   │   ├── local_quickstart.md
│       │   │   └── mcp_quickstart
│       │   │       ├── _index.md
│       │   │       ├── inspector_tools.png
│       │   │       └── inspector.png
│       │   └── looker
│       │       ├── _index.md
│       │       ├── looker_gemini_oauth
│       │       │   ├── _index.md
│       │       │   ├── authenticated.png
│       │       │   ├── authorize.png
│       │       │   └── registration.png
│       │       ├── looker_gemini.md
│       │       └── looker_mcp_inspector
│       │           ├── _index.md
│       │           ├── inspector_tools.png
│       │           └── inspector.png
│       └── sdks
│           ├── _index.md
│           ├── go-sdk.md
│           ├── js-sdk.md
│           └── python-sdk.md
├── gemini-extension.json
├── go.mod
├── go.sum
├── internal
│   ├── auth
│   │   ├── auth.go
│   │   └── google
│   │       └── google.go
│   ├── log
│   │   ├── handler.go
│   │   ├── log_test.go
│   │   ├── log.go
│   │   └── logger.go
│   ├── prebuiltconfigs
│   │   ├── prebuiltconfigs_test.go
│   │   ├── prebuiltconfigs.go
│   │   └── tools
│   │       ├── alloydb-postgres-admin.yaml
│   │       ├── alloydb-postgres-observability.yaml
│   │       ├── alloydb-postgres.yaml
│   │       ├── bigquery.yaml
│   │       ├── clickhouse.yaml
│   │       ├── cloud-healthcare.yaml
│   │       ├── cloud-sql-mssql-admin.yaml
│   │       ├── cloud-sql-mssql-observability.yaml
│   │       ├── cloud-sql-mssql.yaml
│   │       ├── cloud-sql-mysql-admin.yaml
│   │       ├── cloud-sql-mysql-observability.yaml
│   │       ├── cloud-sql-mysql.yaml
│   │       ├── cloud-sql-postgres-admin.yaml
│   │       ├── cloud-sql-postgres-observability.yaml
│   │       ├── cloud-sql-postgres.yaml
│   │       ├── dataplex.yaml
│   │       ├── elasticsearch.yaml
│   │       ├── firestore.yaml
│   │       ├── looker-conversational-analytics.yaml
│   │       ├── looker.yaml
│   │       ├── mindsdb.yaml
│   │       ├── mssql.yaml
│   │       ├── mysql.yaml
│   │       ├── neo4j.yaml
│   │       ├── oceanbase.yaml
│   │       ├── postgres.yaml
│   │       ├── serverless-spark.yaml
│   │       ├── singlestore.yaml
│   │       ├── spanner-postgres.yaml
│   │       ├── spanner.yaml
│   │       └── sqlite.yaml
│   ├── server
│   │   ├── api_test.go
│   │   ├── api.go
│   │   ├── common_test.go
│   │   ├── config.go
│   │   ├── mcp
│   │   │   ├── jsonrpc
│   │   │   │   ├── jsonrpc_test.go
│   │   │   │   └── jsonrpc.go
│   │   │   ├── mcp.go
│   │   │   ├── util
│   │   │   │   └── lifecycle.go
│   │   │   ├── v20241105
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   ├── v20250326
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   └── v20250618
│   │   │       ├── method.go
│   │   │       └── types.go
│   │   ├── mcp_test.go
│   │   ├── mcp.go
│   │   ├── server_test.go
│   │   ├── server.go
│   │   ├── static
│   │   │   ├── assets
│   │   │   │   └── mcptoolboxlogo.png
│   │   │   ├── css
│   │   │   │   └── style.css
│   │   │   ├── index.html
│   │   │   ├── js
│   │   │   │   ├── auth.js
│   │   │   │   ├── loadTools.js
│   │   │   │   ├── mainContent.js
│   │   │   │   ├── navbar.js
│   │   │   │   ├── runTool.js
│   │   │   │   ├── toolDisplay.js
│   │   │   │   ├── tools.js
│   │   │   │   └── toolsets.js
│   │   │   ├── tools.html
│   │   │   └── toolsets.html
│   │   ├── web_test.go
│   │   └── web.go
│   ├── sources
│   │   ├── alloydbadmin
│   │   │   ├── alloydbadmin_test.go
│   │   │   └── alloydbadmin.go
│   │   ├── alloydbpg
│   │   │   ├── alloydb_pg_test.go
│   │   │   └── alloydb_pg.go
│   │   ├── bigquery
│   │   │   ├── bigquery_test.go
│   │   │   ├── bigquery.go
│   │   │   └── cache.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   ├── cassandra_test.go
│   │   │   └── cassandra.go
│   │   ├── clickhouse
│   │   │   ├── clickhouse_test.go
│   │   │   └── clickhouse.go
│   │   ├── cloudhealthcare
│   │   │   ├── cloud_healthcare_test.go
│   │   │   └── cloud_healthcare.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloud_monitoring_test.go
│   │   │   └── cloud_monitoring.go
│   │   ├── cloudsqladmin
│   │   │   ├── cloud_sql_admin_test.go
│   │   │   └── cloud_sql_admin.go
│   │   ├── cloudsqlmssql
│   │   │   ├── cloud_sql_mssql_test.go
│   │   │   └── cloud_sql_mssql.go
│   │   ├── cloudsqlmysql
│   │   │   ├── cloud_sql_mysql_test.go
│   │   │   └── cloud_sql_mysql.go
│   │   ├── cloudsqlpg
│   │   │   ├── cloud_sql_pg_test.go
│   │   │   └── cloud_sql_pg.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataplex
│   │   │   ├── dataplex_test.go
│   │   │   └── dataplex.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── dialect.go
│   │   ├── elasticsearch
│   │   │   ├── elasticsearch_test.go
│   │   │   └── elasticsearch.go
│   │   ├── firebird
│   │   │   ├── firebird_test.go
│   │   │   └── firebird.go
│   │   ├── firestore
│   │   │   ├── firestore_test.go
│   │   │   └── firestore.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── ip_type.go
│   │   ├── looker
│   │   │   ├── looker_test.go
│   │   │   └── looker.go
│   │   ├── mindsdb
│   │   │   ├── mindsdb_test.go
│   │   │   └── mindsdb.go
│   │   ├── mongodb
│   │   │   ├── mongodb_test.go
│   │   │   └── mongodb.go
│   │   ├── mssql
│   │   │   ├── mssql_test.go
│   │   │   └── mssql.go
│   │   ├── mysql
│   │   │   ├── mysql_test.go
│   │   │   └── mysql.go
│   │   ├── neo4j
│   │   │   ├── neo4j_test.go
│   │   │   └── neo4j.go
│   │   ├── oceanbase
│   │   │   ├── oceanbase_test.go
│   │   │   └── oceanbase.go
│   │   ├── oracle
│   │   │   └── oracle.go
│   │   ├── postgres
│   │   │   ├── postgres_test.go
│   │   │   └── postgres.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── serverlessspark
│   │   │   ├── serverlessspark_test.go
│   │   │   └── serverlessspark.go
│   │   ├── singlestore
│   │   │   ├── singlestore_test.go
│   │   │   └── singlestore.go
│   │   ├── sources.go
│   │   ├── spanner
│   │   │   ├── spanner_test.go
│   │   │   └── spanner.go
│   │   ├── sqlite
│   │   │   ├── sqlite_test.go
│   │   │   └── sqlite.go
│   │   ├── tidb
│   │   │   ├── tidb_test.go
│   │   │   └── tidb.go
│   │   ├── trino
│   │   │   ├── trino_test.go
│   │   │   └── trino.go
│   │   ├── util.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedb
│   │       ├── yugabytedb_test.go
│   │       └── yugabytedb.go
│   ├── telemetry
│   │   ├── instrumentation.go
│   │   └── telemetry.go
│   ├── testutils
│   │   └── testutils.go
│   ├── tools
│   │   ├── alloydb
│   │   │   ├── alloydbcreatecluster
│   │   │   │   ├── alloydbcreatecluster_test.go
│   │   │   │   └── alloydbcreatecluster.go
│   │   │   ├── alloydbcreateinstance
│   │   │   │   ├── alloydbcreateinstance_test.go
│   │   │   │   └── alloydbcreateinstance.go
│   │   │   ├── alloydbcreateuser
│   │   │   │   ├── alloydbcreateuser_test.go
│   │   │   │   └── alloydbcreateuser.go
│   │   │   ├── alloydbgetcluster
│   │   │   │   ├── alloydbgetcluster_test.go
│   │   │   │   └── alloydbgetcluster.go
│   │   │   ├── alloydbgetinstance
│   │   │   │   ├── alloydbgetinstance_test.go
│   │   │   │   └── alloydbgetinstance.go
│   │   │   ├── alloydbgetuser
│   │   │   │   ├── alloydbgetuser_test.go
│   │   │   │   └── alloydbgetuser.go
│   │   │   ├── alloydblistclusters
│   │   │   │   ├── alloydblistclusters_test.go
│   │   │   │   └── alloydblistclusters.go
│   │   │   ├── alloydblistinstances
│   │   │   │   ├── alloydblistinstances_test.go
│   │   │   │   └── alloydblistinstances.go
│   │   │   ├── alloydblistusers
│   │   │   │   ├── alloydblistusers_test.go
│   │   │   │   └── alloydblistusers.go
│   │   │   └── alloydbwaitforoperation
│   │   │       ├── alloydbwaitforoperation_test.go
│   │   │       └── alloydbwaitforoperation.go
│   │   ├── alloydbainl
│   │   │   ├── alloydbainl_test.go
│   │   │   └── alloydbainl.go
│   │   ├── bigquery
│   │   │   ├── bigqueryanalyzecontribution
│   │   │   │   ├── bigqueryanalyzecontribution_test.go
│   │   │   │   └── bigqueryanalyzecontribution.go
│   │   │   ├── bigquerycommon
│   │   │   │   ├── table_name_parser_test.go
│   │   │   │   ├── table_name_parser.go
│   │   │   │   └── util.go
│   │   │   ├── bigqueryconversationalanalytics
│   │   │   │   ├── bigqueryconversationalanalytics_test.go
│   │   │   │   └── bigqueryconversationalanalytics.go
│   │   │   ├── bigqueryexecutesql
│   │   │   │   ├── bigqueryexecutesql_test.go
│   │   │   │   └── bigqueryexecutesql.go
│   │   │   ├── bigqueryforecast
│   │   │   │   ├── bigqueryforecast_test.go
│   │   │   │   └── bigqueryforecast.go
│   │   │   ├── bigquerygetdatasetinfo
│   │   │   │   ├── bigquerygetdatasetinfo_test.go
│   │   │   │   └── bigquerygetdatasetinfo.go
│   │   │   ├── bigquerygettableinfo
│   │   │   │   ├── bigquerygettableinfo_test.go
│   │   │   │   └── bigquerygettableinfo.go
│   │   │   ├── bigquerylistdatasetids
│   │   │   │   ├── bigquerylistdatasetids_test.go
│   │   │   │   └── bigquerylistdatasetids.go
│   │   │   ├── bigquerylisttableids
│   │   │   │   ├── bigquerylisttableids_test.go
│   │   │   │   └── bigquerylisttableids.go
│   │   │   ├── bigquerysearchcatalog
│   │   │   │   ├── bigquerysearchcatalog_test.go
│   │   │   │   └── bigquerysearchcatalog.go
│   │   │   └── bigquerysql
│   │   │       ├── bigquerysql_test.go
│   │   │       └── bigquerysql.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   └── cassandracql
│   │   │       ├── cassandracql_test.go
│   │   │       └── cassandracql.go
│   │   ├── clickhouse
│   │   │   ├── clickhouseexecutesql
│   │   │   │   ├── clickhouseexecutesql_test.go
│   │   │   │   └── clickhouseexecutesql.go
│   │   │   ├── clickhouselistdatabases
│   │   │   │   ├── clickhouselistdatabases_test.go
│   │   │   │   └── clickhouselistdatabases.go
│   │   │   ├── clickhouselisttables
│   │   │   │   ├── clickhouselisttables_test.go
│   │   │   │   └── clickhouselisttables.go
│   │   │   └── clickhousesql
│   │   │       ├── clickhousesql_test.go
│   │   │       └── clickhousesql.go
│   │   ├── cloudhealthcare
│   │   │   ├── cloudhealthcarefhirfetchpage
│   │   │   │   ├── cloudhealthcarefhirfetchpage_test.go
│   │   │   │   └── cloudhealthcarefhirfetchpage.go
│   │   │   ├── cloudhealthcarefhirpatienteverything
│   │   │   │   ├── cloudhealthcarefhirpatienteverything_test.go
│   │   │   │   └── cloudhealthcarefhirpatienteverything.go
│   │   │   ├── cloudhealthcarefhirpatientsearch
│   │   │   │   ├── cloudhealthcarefhirpatientsearch_test.go
│   │   │   │   └── cloudhealthcarefhirpatientsearch.go
│   │   │   ├── cloudhealthcaregetdataset
│   │   │   │   ├── cloudhealthcaregetdataset_test.go
│   │   │   │   └── cloudhealthcaregetdataset.go
│   │   │   ├── cloudhealthcaregetdicomstore
│   │   │   │   ├── cloudhealthcaregetdicomstore_test.go
│   │   │   │   └── cloudhealthcaregetdicomstore.go
│   │   │   ├── cloudhealthcaregetdicomstoremetrics
│   │   │   │   ├── cloudhealthcaregetdicomstoremetrics_test.go
│   │   │   │   └── cloudhealthcaregetdicomstoremetrics.go
│   │   │   ├── cloudhealthcaregetfhirresource
│   │   │   │   ├── cloudhealthcaregetfhirresource_test.go
│   │   │   │   └── cloudhealthcaregetfhirresource.go
│   │   │   ├── cloudhealthcaregetfhirstore
│   │   │   │   ├── cloudhealthcaregetfhirstore_test.go
│   │   │   │   └── cloudhealthcaregetfhirstore.go
│   │   │   ├── cloudhealthcaregetfhirstoremetrics
│   │   │   │   ├── cloudhealthcaregetfhirstoremetrics_test.go
│   │   │   │   └── cloudhealthcaregetfhirstoremetrics.go
│   │   │   ├── cloudhealthcarelistdicomstores
│   │   │   │   ├── cloudhealthcarelistdicomstores_test.go
│   │   │   │   └── cloudhealthcarelistdicomstores.go
│   │   │   ├── cloudhealthcarelistfhirstores
│   │   │   │   ├── cloudhealthcarelistfhirstores_test.go
│   │   │   │   └── cloudhealthcarelistfhirstores.go
│   │   │   ├── cloudhealthcareretrieverendereddicominstance
│   │   │   │   ├── cloudhealthcareretrieverendereddicominstance_test.go
│   │   │   │   └── cloudhealthcareretrieverendereddicominstance.go
│   │   │   ├── cloudhealthcaresearchdicominstances
│   │   │   │   ├── cloudhealthcaresearchdicominstances_test.go
│   │   │   │   └── cloudhealthcaresearchdicominstances.go
│   │   │   ├── cloudhealthcaresearchdicomseries
│   │   │   │   ├── cloudhealthcaresearchdicomseries_test.go
│   │   │   │   └── cloudhealthcaresearchdicomseries.go
│   │   │   ├── cloudhealthcaresearchdicomstudies
│   │   │   │   ├── cloudhealthcaresearchdicomstudies_test.go
│   │   │   │   └── cloudhealthcaresearchdicomstudies.go
│   │   │   └── common
│   │   │       └── util.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloudmonitoring_test.go
│   │   │   └── cloudmonitoring.go
│   │   ├── cloudsql
│   │   │   ├── cloudsqlcreatedatabase
│   │   │   │   ├── cloudsqlcreatedatabase_test.go
│   │   │   │   └── cloudsqlcreatedatabase.go
│   │   │   ├── cloudsqlcreateusers
│   │   │   │   ├── cloudsqlcreateusers_test.go
│   │   │   │   └── cloudsqlcreateusers.go
│   │   │   ├── cloudsqlgetinstances
│   │   │   │   ├── cloudsqlgetinstances_test.go
│   │   │   │   └── cloudsqlgetinstances.go
│   │   │   ├── cloudsqllistdatabases
│   │   │   │   ├── cloudsqllistdatabases_test.go
│   │   │   │   └── cloudsqllistdatabases.go
│   │   │   ├── cloudsqllistinstances
│   │   │   │   ├── cloudsqllistinstances_test.go
│   │   │   │   └── cloudsqllistinstances.go
│   │   │   └── cloudsqlwaitforoperation
│   │   │       ├── cloudsqlwaitforoperation_test.go
│   │   │       └── cloudsqlwaitforoperation.go
│   │   ├── cloudsqlmssql
│   │   │   └── cloudsqlmssqlcreateinstance
│   │   │       ├── cloudsqlmssqlcreateinstance_test.go
│   │   │       └── cloudsqlmssqlcreateinstance.go
│   │   ├── cloudsqlmysql
│   │   │   └── cloudsqlmysqlcreateinstance
│   │   │       ├── cloudsqlmysqlcreateinstance_test.go
│   │   │       └── cloudsqlmysqlcreateinstance.go
│   │   ├── cloudsqlpg
│   │   │   └── cloudsqlpgcreateinstances
│   │   │       ├── cloudsqlpgcreateinstances_test.go
│   │   │       └── cloudsqlpgcreateinstances.go
│   │   ├── common_test.go
│   │   ├── common.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataform
│   │   │   └── dataformcompilelocal
│   │   │       ├── dataformcompilelocal_test.go
│   │   │       └── dataformcompilelocal.go
│   │   ├── dataplex
│   │   │   ├── dataplexlookupentry
│   │   │   │   ├── dataplexlookupentry_test.go
│   │   │   │   └── dataplexlookupentry.go
│   │   │   ├── dataplexsearchaspecttypes
│   │   │   │   ├── dataplexsearchaspecttypes_test.go
│   │   │   │   └── dataplexsearchaspecttypes.go
│   │   │   └── dataplexsearchentries
│   │   │       ├── dataplexsearchentries_test.go
│   │   │       └── dataplexsearchentries.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── elasticsearch
│   │   │   └── elasticsearchesql
│   │   │       ├── elasticsearchesql_test.go
│   │   │       └── elasticsearchesql.go
│   │   ├── firebird
│   │   │   ├── firebirdexecutesql
│   │   │   │   ├── firebirdexecutesql_test.go
│   │   │   │   └── firebirdexecutesql.go
│   │   │   └── firebirdsql
│   │   │       ├── firebirdsql_test.go
│   │   │       └── firebirdsql.go
│   │   ├── firestore
│   │   │   ├── firestoreadddocuments
│   │   │   │   ├── firestoreadddocuments_test.go
│   │   │   │   └── firestoreadddocuments.go
│   │   │   ├── firestoredeletedocuments
│   │   │   │   ├── firestoredeletedocuments_test.go
│   │   │   │   └── firestoredeletedocuments.go
│   │   │   ├── firestoregetdocuments
│   │   │   │   ├── firestoregetdocuments_test.go
│   │   │   │   └── firestoregetdocuments.go
│   │   │   ├── firestoregetrules
│   │   │   │   ├── firestoregetrules_test.go
│   │   │   │   └── firestoregetrules.go
│   │   │   ├── firestorelistcollections
│   │   │   │   ├── firestorelistcollections_test.go
│   │   │   │   └── firestorelistcollections.go
│   │   │   ├── firestorequery
│   │   │   │   ├── firestorequery_test.go
│   │   │   │   └── firestorequery.go
│   │   │   ├── firestorequerycollection
│   │   │   │   ├── firestorequerycollection_test.go
│   │   │   │   └── firestorequerycollection.go
│   │   │   ├── firestoreupdatedocument
│   │   │   │   ├── firestoreupdatedocument_test.go
│   │   │   │   └── firestoreupdatedocument.go
│   │   │   ├── firestorevalidaterules
│   │   │   │   ├── firestorevalidaterules_test.go
│   │   │   │   └── firestorevalidaterules.go
│   │   │   └── util
│   │   │       ├── converter_test.go
│   │   │       ├── converter.go
│   │   │       ├── validator_test.go
│   │   │       └── validator.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── http_method.go
│   │   ├── looker
│   │   │   ├── lookeradddashboardelement
│   │   │   │   ├── lookeradddashboardelement_test.go
│   │   │   │   └── lookeradddashboardelement.go
│   │   │   ├── lookercommon
│   │   │   │   ├── lookercommon_test.go
│   │   │   │   └── lookercommon.go
│   │   │   ├── lookerconversationalanalytics
│   │   │   │   ├── lookerconversationalanalytics_test.go
│   │   │   │   └── lookerconversationalanalytics.go
│   │   │   ├── lookercreateprojectfile
│   │   │   │   ├── lookercreateprojectfile_test.go
│   │   │   │   └── lookercreateprojectfile.go
│   │   │   ├── lookerdeleteprojectfile
│   │   │   │   ├── lookerdeleteprojectfile_test.go
│   │   │   │   └── lookerdeleteprojectfile.go
│   │   │   ├── lookerdevmode
│   │   │   │   ├── lookerdevmode_test.go
│   │   │   │   └── lookerdevmode.go
│   │   │   ├── lookergetconnectiondatabases
│   │   │   │   ├── lookergetconnectiondatabases_test.go
│   │   │   │   └── lookergetconnectiondatabases.go
│   │   │   ├── lookergetconnections
│   │   │   │   ├── lookergetconnections_test.go
│   │   │   │   └── lookergetconnections.go
│   │   │   ├── lookergetconnectionschemas
│   │   │   │   ├── lookergetconnectionschemas_test.go
│   │   │   │   └── lookergetconnectionschemas.go
│   │   │   ├── lookergetconnectiontablecolumns
│   │   │   │   ├── lookergetconnectiontablecolumns_test.go
│   │   │   │   └── lookergetconnectiontablecolumns.go
│   │   │   ├── lookergetconnectiontables
│   │   │   │   ├── lookergetconnectiontables_test.go
│   │   │   │   └── lookergetconnectiontables.go
│   │   │   ├── lookergetdashboards
│   │   │   │   ├── lookergetdashboards_test.go
│   │   │   │   └── lookergetdashboards.go
│   │   │   ├── lookergetdimensions
│   │   │   │   ├── lookergetdimensions_test.go
│   │   │   │   └── lookergetdimensions.go
│   │   │   ├── lookergetexplores
│   │   │   │   ├── lookergetexplores_test.go
│   │   │   │   └── lookergetexplores.go
│   │   │   ├── lookergetfilters
│   │   │   │   ├── lookergetfilters_test.go
│   │   │   │   └── lookergetfilters.go
│   │   │   ├── lookergetlooks
│   │   │   │   ├── lookergetlooks_test.go
│   │   │   │   └── lookergetlooks.go
│   │   │   ├── lookergetmeasures
│   │   │   │   ├── lookergetmeasures_test.go
│   │   │   │   └── lookergetmeasures.go
│   │   │   ├── lookergetmodels
│   │   │   │   ├── lookergetmodels_test.go
│   │   │   │   └── lookergetmodels.go
│   │   │   ├── lookergetparameters
│   │   │   │   ├── lookergetparameters_test.go
│   │   │   │   └── lookergetparameters.go
│   │   │   ├── lookergetprojectfile
│   │   │   │   ├── lookergetprojectfile_test.go
│   │   │   │   └── lookergetprojectfile.go
│   │   │   ├── lookergetprojectfiles
│   │   │   │   ├── lookergetprojectfiles_test.go
│   │   │   │   └── lookergetprojectfiles.go
│   │   │   ├── lookergetprojects
│   │   │   │   ├── lookergetprojects_test.go
│   │   │   │   └── lookergetprojects.go
│   │   │   ├── lookerhealthanalyze
│   │   │   │   ├── lookerhealthanalyze_test.go
│   │   │   │   └── lookerhealthanalyze.go
│   │   │   ├── lookerhealthpulse
│   │   │   │   ├── lookerhealthpulse_test.go
│   │   │   │   └── lookerhealthpulse.go
│   │   │   ├── lookerhealthvacuum
│   │   │   │   ├── lookerhealthvacuum_test.go
│   │   │   │   └── lookerhealthvacuum.go
│   │   │   ├── lookermakedashboard
│   │   │   │   ├── lookermakedashboard_test.go
│   │   │   │   └── lookermakedashboard.go
│   │   │   ├── lookermakelook
│   │   │   │   ├── lookermakelook_test.go
│   │   │   │   └── lookermakelook.go
│   │   │   ├── lookerquery
│   │   │   │   ├── lookerquery_test.go
│   │   │   │   └── lookerquery.go
│   │   │   ├── lookerquerysql
│   │   │   │   ├── lookerquerysql_test.go
│   │   │   │   └── lookerquerysql.go
│   │   │   ├── lookerqueryurl
│   │   │   │   ├── lookerqueryurl_test.go
│   │   │   │   └── lookerqueryurl.go
│   │   │   ├── lookerrundashboard
│   │   │   │   ├── lookerrundashboard_test.go
│   │   │   │   └── lookerrundashboard.go
│   │   │   ├── lookerrunlook
│   │   │   │   ├── lookerrunlook_test.go
│   │   │   │   └── lookerrunlook.go
│   │   │   └── lookerupdateprojectfile
│   │   │       ├── lookerupdateprojectfile_test.go
│   │   │       └── lookerupdateprojectfile.go
│   │   ├── mindsdb
│   │   │   ├── mindsdbexecutesql
│   │   │   │   ├── mindsdbexecutesql_test.go
│   │   │   │   └── mindsdbexecutesql.go
│   │   │   └── mindsdbsql
│   │   │       ├── mindsdbsql_test.go
│   │   │       └── mindsdbsql.go
│   │   ├── mongodb
│   │   │   ├── mongodbaggregate
│   │   │   │   ├── mongodbaggregate_test.go
│   │   │   │   └── mongodbaggregate.go
│   │   │   ├── mongodbdeletemany
│   │   │   │   ├── mongodbdeletemany_test.go
│   │   │   │   └── mongodbdeletemany.go
│   │   │   ├── mongodbdeleteone
│   │   │   │   ├── mongodbdeleteone_test.go
│   │   │   │   └── mongodbdeleteone.go
│   │   │   ├── mongodbfind
│   │   │   │   ├── mongodbfind_test.go
│   │   │   │   └── mongodbfind.go
│   │   │   ├── mongodbfindone
│   │   │   │   ├── mongodbfindone_test.go
│   │   │   │   └── mongodbfindone.go
│   │   │   ├── mongodbinsertmany
│   │   │   │   ├── mongodbinsertmany_test.go
│   │   │   │   └── mongodbinsertmany.go
│   │   │   ├── mongodbinsertone
│   │   │   │   ├── mongodbinsertone_test.go
│   │   │   │   └── mongodbinsertone.go
│   │   │   ├── mongodbupdatemany
│   │   │   │   ├── mongodbupdatemany_test.go
│   │   │   │   └── mongodbupdatemany.go
│   │   │   └── mongodbupdateone
│   │   │       ├── mongodbupdateone_test.go
│   │   │       └── mongodbupdateone.go
│   │   ├── mssql
│   │   │   ├── mssqlexecutesql
│   │   │   │   ├── mssqlexecutesql_test.go
│   │   │   │   └── mssqlexecutesql.go
│   │   │   ├── mssqllisttables
│   │   │   │   ├── mssqllisttables_test.go
│   │   │   │   └── mssqllisttables.go
│   │   │   └── mssqlsql
│   │   │       ├── mssqlsql_test.go
│   │   │       └── mssqlsql.go
│   │   ├── mysql
│   │   │   ├── mysqlcommon
│   │   │   │   └── mysqlcommon.go
│   │   │   ├── mysqlexecutesql
│   │   │   │   ├── mysqlexecutesql_test.go
│   │   │   │   └── mysqlexecutesql.go
│   │   │   ├── mysqllistactivequeries
│   │   │   │   ├── mysqllistactivequeries_test.go
│   │   │   │   └── mysqllistactivequeries.go
│   │   │   ├── mysqllisttablefragmentation
│   │   │   │   ├── mysqllisttablefragmentation_test.go
│   │   │   │   └── mysqllisttablefragmentation.go
│   │   │   ├── mysqllisttables
│   │   │   │   ├── mysqllisttables_test.go
│   │   │   │   └── mysqllisttables.go
│   │   │   ├── mysqllisttablesmissinguniqueindexes
│   │   │   │   ├── mysqllisttablesmissinguniqueindexes_test.go
│   │   │   │   └── mysqllisttablesmissinguniqueindexes.go
│   │   │   └── mysqlsql
│   │   │       ├── mysqlsql_test.go
│   │   │       └── mysqlsql.go
│   │   ├── neo4j
│   │   │   ├── neo4jcypher
│   │   │   │   ├── neo4jcypher_test.go
│   │   │   │   └── neo4jcypher.go
│   │   │   ├── neo4jexecutecypher
│   │   │   │   ├── classifier
│   │   │   │   │   ├── classifier_test.go
│   │   │   │   │   └── classifier.go
│   │   │   │   ├── neo4jexecutecypher_test.go
│   │   │   │   └── neo4jexecutecypher.go
│   │   │   └── neo4jschema
│   │   │       ├── cache
│   │   │       │   ├── cache_test.go
│   │   │       │   └── cache.go
│   │   │       ├── helpers
│   │   │       │   ├── helpers_test.go
│   │   │       │   └── helpers.go
│   │   │       ├── neo4jschema_test.go
│   │   │       ├── neo4jschema.go
│   │   │       └── types
│   │   │           └── types.go
│   │   ├── oceanbase
│   │   │   ├── oceanbaseexecutesql
│   │   │   │   ├── oceanbaseexecutesql_test.go
│   │   │   │   └── oceanbaseexecutesql.go
│   │   │   └── oceanbasesql
│   │   │       ├── oceanbasesql_test.go
│   │   │       └── oceanbasesql.go
│   │   ├── oracle
│   │   │   ├── oracleexecutesql
│   │   │   │   └── oracleexecutesql.go
│   │   │   └── oraclesql
│   │   │       └── oraclesql.go
│   │   ├── parameters_test.go
│   │   ├── parameters.go
│   │   ├── postgres
│   │   │   ├── postgresexecutesql
│   │   │   │   ├── postgresexecutesql_test.go
│   │   │   │   └── postgresexecutesql.go
│   │   │   ├── postgreslistactivequeries
│   │   │   │   ├── postgreslistactivequeries_test.go
│   │   │   │   └── postgreslistactivequeries.go
│   │   │   ├── postgreslistavailableextensions
│   │   │   │   ├── postgreslistavailableextensions_test.go
│   │   │   │   └── postgreslistavailableextensions.go
│   │   │   ├── postgreslistinstalledextensions
│   │   │   │   ├── postgreslistinstalledextensions_test.go
│   │   │   │   └── postgreslistinstalledextensions.go
│   │   │   ├── postgreslistschemas
│   │   │   │   ├── postgreslistschemas_test.go
│   │   │   │   └── postgreslistschemas.go
│   │   │   ├── postgreslisttables
│   │   │   │   ├── postgreslisttables_test.go
│   │   │   │   └── postgreslisttables.go
│   │   │   ├── postgreslistviews
│   │   │   │   ├── postgreslistviews_test.go
│   │   │   │   └── postgreslistviews.go
│   │   │   └── postgressql
│   │   │       ├── postgressql_test.go
│   │   │       └── postgressql.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── serverlessspark
│   │   │   ├── serverlesssparkcancelbatch
│   │   │   │   ├── serverlesssparkcancelbatch_test.go
│   │   │   │   └── serverlesssparkcancelbatch.go
│   │   │   ├── serverlesssparkgetbatch
│   │   │   │   ├── serverlesssparkgetbatch_test.go
│   │   │   │   └── serverlesssparkgetbatch.go
│   │   │   └── serverlesssparklistbatches
│   │   │       ├── serverlesssparklistbatches_test.go
│   │   │       └── serverlesssparklistbatches.go
│   │   ├── singlestore
│   │   │   ├── singlestoreexecutesql
│   │   │   │   ├── singlestoreexecutesql_test.go
│   │   │   │   └── singlestoreexecutesql.go
│   │   │   └── singlestoresql
│   │   │       ├── singlestoresql_test.go
│   │   │       └── singlestoresql.go
│   │   ├── spanner
│   │   │   ├── spannerexecutesql
│   │   │   │   ├── spannerexecutesql_test.go
│   │   │   │   └── spannerexecutesql.go
│   │   │   ├── spannerlisttables
│   │   │   │   ├── spannerlisttables_test.go
│   │   │   │   └── spannerlisttables.go
│   │   │   └── spannersql
│   │   │       ├── spanner_test.go
│   │   │       └── spannersql.go
│   │   ├── sqlite
│   │   │   ├── sqliteexecutesql
│   │   │   │   ├── sqliteexecutesql_test.go
│   │   │   │   └── sqliteexecutesql.go
│   │   │   └── sqlitesql
│   │   │       ├── sqlitesql_test.go
│   │   │       └── sqlitesql.go
│   │   ├── tidb
│   │   │   ├── tidbexecutesql
│   │   │   │   ├── tidbexecutesql_test.go
│   │   │   │   └── tidbexecutesql.go
│   │   │   └── tidbsql
│   │   │       ├── tidbsql_test.go
│   │   │       └── tidbsql.go
│   │   ├── tools_test.go
│   │   ├── tools.go
│   │   ├── toolsets.go
│   │   ├── trino
│   │   │   ├── trinoexecutesql
│   │   │   │   ├── trinoexecutesql_test.go
│   │   │   │   └── trinoexecutesql.go
│   │   │   └── trinosql
│   │   │       ├── trinosql_test.go
│   │   │       └── trinosql.go
│   │   ├── utility
│   │   │   └── wait
│   │   │       ├── wait_test.go
│   │   │       └── wait.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedbsql
│   │       ├── yugabytedbsql_test.go
│   │       └── yugabytedbsql.go
│   └── util
│       ├── orderedmap
│       │   ├── orderedmap_test.go
│       │   └── orderedmap.go
│       └── util.go
├── LICENSE
├── logo.png
├── main.go
├── MCP-TOOLBOX-EXTENSION.md
├── README.md
└── tests
    ├── alloydb
    │   ├── alloydb_integration_test.go
    │   └── alloydb_wait_for_operation_test.go
    ├── alloydbainl
    │   └── alloydb_ai_nl_integration_test.go
    ├── alloydbpg
    │   └── alloydb_pg_integration_test.go
    ├── auth.go
    ├── bigquery
    │   └── bigquery_integration_test.go
    ├── bigtable
    │   └── bigtable_integration_test.go
    ├── cassandra
    │   └── cassandra_integration_test.go
    ├── clickhouse
    │   └── clickhouse_integration_test.go
    ├── cloudhealthcare
    │   └── cloud_healthcare_integration_test.go
    ├── cloudmonitoring
    │   └── cloud_monitoring_integration_test.go
    ├── cloudsql
    │   ├── cloud_sql_create_database_test.go
    │   ├── cloud_sql_create_users_test.go
    │   ├── cloud_sql_get_instances_test.go
    │   ├── cloud_sql_list_databases_test.go
    │   ├── cloudsql_list_instances_test.go
    │   └── cloudsql_wait_for_operation_test.go
    ├── cloudsqlmssql
    │   ├── cloud_sql_mssql_create_instance_integration_test.go
    │   └── cloud_sql_mssql_integration_test.go
    ├── cloudsqlmysql
    │   ├── cloud_sql_mysql_create_instance_integration_test.go
    │   └── cloud_sql_mysql_integration_test.go
    ├── cloudsqlpg
    │   ├── cloud_sql_pg_create_instances_test.go
    │   └── cloud_sql_pg_integration_test.go
    ├── common.go
    ├── couchbase
    │   └── couchbase_integration_test.go
    ├── dataform
    │   └── dataform_integration_test.go
    ├── dataplex
    │   └── dataplex_integration_test.go
    ├── dgraph
    │   └── dgraph_integration_test.go
    ├── elasticsearch
    │   └── elasticsearch_integration_test.go
    ├── firebird
    │   └── firebird_integration_test.go
    ├── firestore
    │   └── firestore_integration_test.go
    ├── http
    │   └── http_integration_test.go
    ├── looker
    │   └── looker_integration_test.go
    ├── mindsdb
    │   └── mindsdb_integration_test.go
    ├── mongodb
    │   └── mongodb_integration_test.go
    ├── mssql
    │   └── mssql_integration_test.go
    ├── mysql
    │   └── mysql_integration_test.go
    ├── neo4j
    │   └── neo4j_integration_test.go
    ├── oceanbase
    │   └── oceanbase_integration_test.go
    ├── option.go
    ├── oracle
    │   └── oracle_integration_test.go
    ├── postgres
    │   └── postgres_integration_test.go
    ├── redis
    │   └── redis_test.go
    ├── server.go
    ├── serverlessspark
    │   └── serverless_spark_integration_test.go
    ├── singlestore
    │   └── singlestore_integration_test.go
    ├── source.go
    ├── spanner
    │   └── spanner_integration_test.go
    ├── sqlite
    │   └── sqlite_integration_test.go
    ├── tidb
    │   └── tidb_integration_test.go
    ├── tool.go
    ├── trino
    │   └── trino_integration_test.go
    ├── utility
    │   └── wait_integration_test.go
    ├── valkey
    │   └── valkey_test.go
    └── yugabytedb
        └── yugabytedb_integration_test.go
```

# Files

--------------------------------------------------------------------------------
/internal/tools/parameters_test.go:
--------------------------------------------------------------------------------

```go
   1 | // Copyright 2024 Google LLC
   2 | //
   3 | // Licensed under the Apache License, Version 2.0 (the "License");
   4 | // you may not use this file except in compliance with the License.
   5 | // You may obtain a copy of the License at
   6 | //
   7 | //     http://www.apache.org/licenses/LICENSE-2.0
   8 | //
   9 | // Unless required by applicable law or agreed to in writing, software
  10 | // distributed under the License is distributed on an "AS IS" BASIS,
  11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 | // See the License for the specific language governing permissions and
  13 | // limitations under the License.
  14 | 
  15 | package tools_test
  16 | 
  17 | import (
  18 | 	"bytes"
  19 | 	"encoding/json"
  20 | 	"math"
  21 | 	"reflect"
  22 | 	"slices"
  23 | 	"strings"
  24 | 	"testing"
  25 | 
  26 | 	"github.com/goccy/go-yaml"
  27 | 	"github.com/google/go-cmp/cmp"
  28 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
  29 | 	"github.com/googleapis/genai-toolbox/internal/tools"
  30 | )
  31 | 
  32 | func TestParametersMarshal(t *testing.T) {
  33 | 	ctx, err := testutils.ContextWithNewLogger()
  34 | 	if err != nil {
  35 | 		t.Fatalf("unexpected error: %s", err)
  36 | 	}
  37 | 	tcs := []struct {
  38 | 		name string
  39 | 		in   []map[string]any
  40 | 		want tools.Parameters
  41 | 	}{
  42 | 		{
  43 | 			name: "string",
  44 | 			in: []map[string]any{
  45 | 				{
  46 | 					"name":        "my_string",
  47 | 					"type":        "string",
  48 | 					"description": "this param is a string",
  49 | 				},
  50 | 			},
  51 | 			want: tools.Parameters{
  52 | 				tools.NewStringParameter("my_string", "this param is a string"),
  53 | 			},
  54 | 		},
  55 | 		{
  56 | 			name: "string not required",
  57 | 			in: []map[string]any{
  58 | 				{
  59 | 					"name":        "my_string",
  60 | 					"type":        "string",
  61 | 					"description": "this param is a string",
  62 | 					"required":    false,
  63 | 				},
  64 | 			},
  65 | 			want: tools.Parameters{
  66 | 				tools.NewStringParameterWithRequired("my_string", "this param is a string", false),
  67 | 			},
  68 | 		},
  69 | 		{
  70 | 			name: "int",
  71 | 			in: []map[string]any{
  72 | 				{
  73 | 					"name":        "my_integer",
  74 | 					"type":        "integer",
  75 | 					"description": "this param is an int",
  76 | 				},
  77 | 			},
  78 | 			want: tools.Parameters{
  79 | 				tools.NewIntParameter("my_integer", "this param is an int"),
  80 | 			},
  81 | 		},
  82 | 		{
  83 | 			name: "int not required",
  84 | 			in: []map[string]any{
  85 | 				{
  86 | 					"name":        "my_integer",
  87 | 					"type":        "integer",
  88 | 					"description": "this param is an int",
  89 | 					"required":    false,
  90 | 				},
  91 | 			},
  92 | 			want: tools.Parameters{
  93 | 				tools.NewIntParameterWithRequired("my_integer", "this param is an int", false),
  94 | 			},
  95 | 		},
  96 | 		{
  97 | 			name: "float",
  98 | 			in: []map[string]any{
  99 | 				{
 100 | 					"name":        "my_float",
 101 | 					"type":        "float",
 102 | 					"description": "my param is a float",
 103 | 				},
 104 | 			},
 105 | 			want: tools.Parameters{
 106 | 				tools.NewFloatParameter("my_float", "my param is a float"),
 107 | 			},
 108 | 		},
 109 | 		{
 110 | 			name: "float not required",
 111 | 			in: []map[string]any{
 112 | 				{
 113 | 					"name":        "my_float",
 114 | 					"type":        "float",
 115 | 					"description": "my param is a float",
 116 | 					"required":    false,
 117 | 				},
 118 | 			},
 119 | 			want: tools.Parameters{
 120 | 				tools.NewFloatParameterWithRequired("my_float", "my param is a float", false),
 121 | 			},
 122 | 		},
 123 | 		{
 124 | 			name: "bool",
 125 | 			in: []map[string]any{
 126 | 				{
 127 | 					"name":        "my_bool",
 128 | 					"type":        "boolean",
 129 | 					"description": "this param is a boolean",
 130 | 				},
 131 | 			},
 132 | 			want: tools.Parameters{
 133 | 				tools.NewBooleanParameter("my_bool", "this param is a boolean"),
 134 | 			},
 135 | 		},
 136 | 		{
 137 | 			name: "bool not required",
 138 | 			in: []map[string]any{
 139 | 				{
 140 | 					"name":        "my_bool",
 141 | 					"type":        "boolean",
 142 | 					"description": "this param is a boolean",
 143 | 					"required":    false,
 144 | 				},
 145 | 			},
 146 | 			want: tools.Parameters{
 147 | 				tools.NewBooleanParameterWithRequired("my_bool", "this param is a boolean", false),
 148 | 			},
 149 | 		},
 150 | 		{
 151 | 			name: "string array",
 152 | 			in: []map[string]any{
 153 | 				{
 154 | 					"name":        "my_array",
 155 | 					"type":        "array",
 156 | 					"description": "this param is an array of strings",
 157 | 					"items": map[string]string{
 158 | 						"name":        "my_string",
 159 | 						"type":        "string",
 160 | 						"description": "string item",
 161 | 					},
 162 | 				},
 163 | 			},
 164 | 			want: tools.Parameters{
 165 | 				tools.NewArrayParameter("my_array", "this param is an array of strings", tools.NewStringParameter("my_string", "string item")),
 166 | 			},
 167 | 		},
 168 | 		{
 169 | 			name: "string array not required",
 170 | 			in: []map[string]any{
 171 | 				{
 172 | 					"name":        "my_array",
 173 | 					"type":        "array",
 174 | 					"description": "this param is an array of strings",
 175 | 					"required":    false,
 176 | 					"items": map[string]string{
 177 | 						"name":        "my_string",
 178 | 						"type":        "string",
 179 | 						"description": "string item",
 180 | 					},
 181 | 				},
 182 | 			},
 183 | 			want: tools.Parameters{
 184 | 				tools.NewArrayParameterWithRequired("my_array", "this param is an array of strings", false, tools.NewStringParameter("my_string", "string item")),
 185 | 			},
 186 | 		},
 187 | 		{
 188 | 			name: "float array",
 189 | 			in: []map[string]any{
 190 | 				{
 191 | 					"name":        "my_array",
 192 | 					"type":        "array",
 193 | 					"description": "this param is an array of floats",
 194 | 					"items": map[string]string{
 195 | 						"name":        "my_float",
 196 | 						"type":        "float",
 197 | 						"description": "float item",
 198 | 					},
 199 | 				},
 200 | 			},
 201 | 			want: tools.Parameters{
 202 | 				tools.NewArrayParameter("my_array", "this param is an array of floats", tools.NewFloatParameter("my_float", "float item")),
 203 | 			},
 204 | 		},
 205 | 		{
 206 | 			name: "string default",
 207 | 			in: []map[string]any{
 208 | 				{
 209 | 					"name":        "my_string",
 210 | 					"type":        "string",
 211 | 					"default":     "foo",
 212 | 					"description": "this param is a string",
 213 | 				},
 214 | 			},
 215 | 			want: tools.Parameters{
 216 | 				tools.NewStringParameterWithDefault("my_string", "foo", "this param is a string"),
 217 | 			},
 218 | 		},
 219 | 		{
 220 | 			name: "int default",
 221 | 			in: []map[string]any{
 222 | 				{
 223 | 					"name":        "my_integer",
 224 | 					"type":        "integer",
 225 | 					"default":     5,
 226 | 					"description": "this param is an int",
 227 | 				},
 228 | 			},
 229 | 			want: tools.Parameters{
 230 | 				tools.NewIntParameterWithDefault("my_integer", 5, "this param is an int"),
 231 | 			},
 232 | 		},
 233 | 		{
 234 | 			name: "float default",
 235 | 			in: []map[string]any{
 236 | 				{
 237 | 					"name":        "my_float",
 238 | 					"type":        "float",
 239 | 					"default":     1.1,
 240 | 					"description": "my param is a float",
 241 | 				},
 242 | 			},
 243 | 			want: tools.Parameters{
 244 | 				tools.NewFloatParameterWithDefault("my_float", 1.1, "my param is a float"),
 245 | 			},
 246 | 		},
 247 | 		{
 248 | 			name: "bool default",
 249 | 			in: []map[string]any{
 250 | 				{
 251 | 					"name":        "my_bool",
 252 | 					"type":        "boolean",
 253 | 					"default":     true,
 254 | 					"description": "this param is a boolean",
 255 | 				},
 256 | 			},
 257 | 			want: tools.Parameters{
 258 | 				tools.NewBooleanParameterWithDefault("my_bool", true, "this param is a boolean"),
 259 | 			},
 260 | 		},
 261 | 		{
 262 | 			name: "string array default",
 263 | 			in: []map[string]any{
 264 | 				{
 265 | 					"name":        "my_array",
 266 | 					"type":        "array",
 267 | 					"default":     []any{"foo", "bar"},
 268 | 					"description": "this param is an array of strings",
 269 | 					"items": map[string]string{
 270 | 						"name":        "my_string",
 271 | 						"type":        "string",
 272 | 						"description": "string item",
 273 | 					},
 274 | 				},
 275 | 			},
 276 | 			want: tools.Parameters{
 277 | 				tools.NewArrayParameterWithDefault("my_array", []any{"foo", "bar"}, "this param is an array of strings", tools.NewStringParameter("my_string", "string item")),
 278 | 			},
 279 | 		},
 280 | 		{
 281 | 			name: "float array default",
 282 | 			in: []map[string]any{
 283 | 				{
 284 | 					"name":        "my_array",
 285 | 					"type":        "array",
 286 | 					"default":     []any{1.0, 1.1},
 287 | 					"description": "this param is an array of floats",
 288 | 					"items": map[string]string{
 289 | 						"name":        "my_float",
 290 | 						"type":        "float",
 291 | 						"description": "float item",
 292 | 					},
 293 | 				},
 294 | 			},
 295 | 			want: tools.Parameters{
 296 | 				tools.NewArrayParameterWithDefault("my_array", []any{1.0, 1.1}, "this param is an array of floats", tools.NewFloatParameter("my_float", "float item")),
 297 | 			},
 298 | 		},
 299 | 		{
 300 | 			name: "map with string values",
 301 | 			in: []map[string]any{
 302 | 				{
 303 | 					"name":        "my_map",
 304 | 					"type":        "map",
 305 | 					"description": "this param is a map of strings",
 306 | 					"valueType":   "string",
 307 | 				},
 308 | 			},
 309 | 			want: tools.Parameters{
 310 | 				tools.NewMapParameter("my_map", "this param is a map of strings", "string"),
 311 | 			},
 312 | 		},
 313 | 		{
 314 | 			name: "map not required",
 315 | 			in: []map[string]any{
 316 | 				{
 317 | 					"name":        "my_map",
 318 | 					"type":        "map",
 319 | 					"description": "this param is a map of strings",
 320 | 					"required":    false,
 321 | 					"valueType":   "string",
 322 | 				},
 323 | 			},
 324 | 			want: tools.Parameters{
 325 | 				tools.NewMapParameterWithRequired("my_map", "this param is a map of strings", false, "string"),
 326 | 			},
 327 | 		},
 328 | 		{
 329 | 			name: "map with default",
 330 | 			in: []map[string]any{
 331 | 				{
 332 | 					"name":        "my_map",
 333 | 					"type":        "map",
 334 | 					"description": "this param is a map of strings",
 335 | 					"default":     map[string]any{"key1": "val1"},
 336 | 					"valueType":   "string",
 337 | 				},
 338 | 			},
 339 | 			want: tools.Parameters{
 340 | 				tools.NewMapParameterWithDefault("my_map", map[string]any{"key1": "val1"}, "this param is a map of strings", "string"),
 341 | 			},
 342 | 		},
 343 | 		{
 344 | 			name: "generic map (no valueType)",
 345 | 			in: []map[string]any{
 346 | 				{
 347 | 					"name":        "my_generic_map",
 348 | 					"type":        "map",
 349 | 					"description": "this param is a generic map",
 350 | 				},
 351 | 			},
 352 | 			want: tools.Parameters{
 353 | 				tools.NewMapParameter("my_generic_map", "this param is a generic map", ""),
 354 | 			},
 355 | 		},
 356 | 	}
 357 | 	for _, tc := range tcs {
 358 | 		t.Run(tc.name, func(t *testing.T) {
 359 | 			var got tools.Parameters
 360 | 			// parse map to bytes
 361 | 			data, err := yaml.Marshal(tc.in)
 362 | 			if err != nil {
 363 | 				t.Fatalf("unable to marshal input to yaml: %s", err)
 364 | 			}
 365 | 			// parse bytes to object
 366 | 			err = yaml.UnmarshalContext(ctx, data, &got)
 367 | 			if err != nil {
 368 | 				t.Fatalf("unable to unmarshal: %s", err)
 369 | 			}
 370 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
 371 | 				t.Fatalf("incorrect parse: diff %v", diff)
 372 | 			}
 373 | 		})
 374 | 	}
 375 | }
 376 | 
 377 | func TestAuthParametersMarshal(t *testing.T) {
 378 | 	ctx, err := testutils.ContextWithNewLogger()
 379 | 	if err != nil {
 380 | 		t.Fatalf("unexpected error: %s", err)
 381 | 	}
 382 | 	authServices := []tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"}, {Name: "other-auth-service", Field: "user_id"}}
 383 | 	tcs := []struct {
 384 | 		name string
 385 | 		in   []map[string]any
 386 | 		want tools.Parameters
 387 | 	}{
 388 | 		{
 389 | 			name: "string",
 390 | 			in: []map[string]any{
 391 | 				{
 392 | 					"name":        "my_string",
 393 | 					"type":        "string",
 394 | 					"description": "this param is a string",
 395 | 					"authServices": []map[string]string{
 396 | 						{
 397 | 							"name":  "my-google-auth-service",
 398 | 							"field": "user_id",
 399 | 						},
 400 | 						{
 401 | 							"name":  "other-auth-service",
 402 | 							"field": "user_id",
 403 | 						},
 404 | 					},
 405 | 				},
 406 | 			},
 407 | 			want: tools.Parameters{
 408 | 				tools.NewStringParameterWithAuth("my_string", "this param is a string", authServices),
 409 | 			},
 410 | 		},
 411 | 		{
 412 | 			name: "string with authServices",
 413 | 			in: []map[string]any{
 414 | 				{
 415 | 					"name":        "my_string",
 416 | 					"type":        "string",
 417 | 					"description": "this param is a string",
 418 | 					"authServices": []map[string]string{
 419 | 						{
 420 | 							"name":  "my-google-auth-service",
 421 | 							"field": "user_id",
 422 | 						},
 423 | 						{
 424 | 							"name":  "other-auth-service",
 425 | 							"field": "user_id",
 426 | 						},
 427 | 					},
 428 | 				},
 429 | 			},
 430 | 			want: tools.Parameters{
 431 | 				tools.NewStringParameterWithAuth("my_string", "this param is a string", authServices),
 432 | 			},
 433 | 		},
 434 | 		{
 435 | 			name: "int",
 436 | 			in: []map[string]any{
 437 | 				{
 438 | 					"name":        "my_integer",
 439 | 					"type":        "integer",
 440 | 					"description": "this param is an int",
 441 | 					"authServices": []map[string]string{
 442 | 						{
 443 | 							"name":  "my-google-auth-service",
 444 | 							"field": "user_id",
 445 | 						},
 446 | 						{
 447 | 							"name":  "other-auth-service",
 448 | 							"field": "user_id",
 449 | 						},
 450 | 					},
 451 | 				},
 452 | 			},
 453 | 			want: tools.Parameters{
 454 | 				tools.NewIntParameterWithAuth("my_integer", "this param is an int", authServices),
 455 | 			},
 456 | 		},
 457 | 		{
 458 | 			name: "int with authServices",
 459 | 			in: []map[string]any{
 460 | 				{
 461 | 					"name":        "my_integer",
 462 | 					"type":        "integer",
 463 | 					"description": "this param is an int",
 464 | 					"authServices": []map[string]string{
 465 | 						{
 466 | 							"name":  "my-google-auth-service",
 467 | 							"field": "user_id",
 468 | 						},
 469 | 						{
 470 | 							"name":  "other-auth-service",
 471 | 							"field": "user_id",
 472 | 						},
 473 | 					},
 474 | 				},
 475 | 			},
 476 | 			want: tools.Parameters{
 477 | 				tools.NewIntParameterWithAuth("my_integer", "this param is an int", authServices),
 478 | 			},
 479 | 		},
 480 | 		{
 481 | 			name: "float",
 482 | 			in: []map[string]any{
 483 | 				{
 484 | 					"name":        "my_float",
 485 | 					"type":        "float",
 486 | 					"description": "my param is a float",
 487 | 					"authServices": []map[string]string{
 488 | 						{
 489 | 							"name":  "my-google-auth-service",
 490 | 							"field": "user_id",
 491 | 						},
 492 | 						{
 493 | 							"name":  "other-auth-service",
 494 | 							"field": "user_id",
 495 | 						},
 496 | 					},
 497 | 				},
 498 | 			},
 499 | 			want: tools.Parameters{
 500 | 				tools.NewFloatParameterWithAuth("my_float", "my param is a float", authServices),
 501 | 			},
 502 | 		},
 503 | 		{
 504 | 			name: "float with authServices",
 505 | 			in: []map[string]any{
 506 | 				{
 507 | 					"name":        "my_float",
 508 | 					"type":        "float",
 509 | 					"description": "my param is a float",
 510 | 					"authServices": []map[string]string{
 511 | 						{
 512 | 							"name":  "my-google-auth-service",
 513 | 							"field": "user_id",
 514 | 						},
 515 | 						{
 516 | 							"name":  "other-auth-service",
 517 | 							"field": "user_id",
 518 | 						},
 519 | 					},
 520 | 				},
 521 | 			},
 522 | 			want: tools.Parameters{
 523 | 				tools.NewFloatParameterWithAuth("my_float", "my param is a float", authServices),
 524 | 			},
 525 | 		},
 526 | 		{
 527 | 			name: "bool",
 528 | 			in: []map[string]any{
 529 | 				{
 530 | 					"name":        "my_bool",
 531 | 					"type":        "boolean",
 532 | 					"description": "this param is a boolean",
 533 | 					"authServices": []map[string]string{
 534 | 						{
 535 | 							"name":  "my-google-auth-service",
 536 | 							"field": "user_id",
 537 | 						},
 538 | 						{
 539 | 							"name":  "other-auth-service",
 540 | 							"field": "user_id",
 541 | 						},
 542 | 					},
 543 | 				},
 544 | 			},
 545 | 			want: tools.Parameters{
 546 | 				tools.NewBooleanParameterWithAuth("my_bool", "this param is a boolean", authServices),
 547 | 			},
 548 | 		},
 549 | 		{
 550 | 			name: "bool with authServices",
 551 | 			in: []map[string]any{
 552 | 				{
 553 | 					"name":        "my_bool",
 554 | 					"type":        "boolean",
 555 | 					"description": "this param is a boolean",
 556 | 					"authServices": []map[string]string{
 557 | 						{
 558 | 							"name":  "my-google-auth-service",
 559 | 							"field": "user_id",
 560 | 						},
 561 | 						{
 562 | 							"name":  "other-auth-service",
 563 | 							"field": "user_id",
 564 | 						},
 565 | 					},
 566 | 				},
 567 | 			},
 568 | 			want: tools.Parameters{
 569 | 				tools.NewBooleanParameterWithAuth("my_bool", "this param is a boolean", authServices),
 570 | 			},
 571 | 		},
 572 | 		{
 573 | 			name: "string array",
 574 | 			in: []map[string]any{
 575 | 				{
 576 | 					"name":        "my_array",
 577 | 					"type":        "array",
 578 | 					"description": "this param is an array of strings",
 579 | 					"items": map[string]string{
 580 | 						"name":        "my_string",
 581 | 						"type":        "string",
 582 | 						"description": "string item",
 583 | 					},
 584 | 					"authServices": []map[string]string{
 585 | 						{
 586 | 							"name":  "my-google-auth-service",
 587 | 							"field": "user_id",
 588 | 						},
 589 | 						{
 590 | 							"name":  "other-auth-service",
 591 | 							"field": "user_id",
 592 | 						},
 593 | 					},
 594 | 				},
 595 | 			},
 596 | 			want: tools.Parameters{
 597 | 				tools.NewArrayParameterWithAuth("my_array", "this param is an array of strings", tools.NewStringParameter("my_string", "string item"), authServices),
 598 | 			},
 599 | 		},
 600 | 		{
 601 | 			name: "string array with authServices",
 602 | 			in: []map[string]any{
 603 | 				{
 604 | 					"name":        "my_array",
 605 | 					"type":        "array",
 606 | 					"description": "this param is an array of strings",
 607 | 					"items": map[string]string{
 608 | 						"name":        "my_string",
 609 | 						"type":        "string",
 610 | 						"description": "string item",
 611 | 					},
 612 | 					"authServices": []map[string]string{
 613 | 						{
 614 | 							"name":  "my-google-auth-service",
 615 | 							"field": "user_id",
 616 | 						},
 617 | 						{
 618 | 							"name":  "other-auth-service",
 619 | 							"field": "user_id",
 620 | 						},
 621 | 					},
 622 | 				},
 623 | 			},
 624 | 			want: tools.Parameters{
 625 | 				tools.NewArrayParameterWithAuth("my_array", "this param is an array of strings", tools.NewStringParameter("my_string", "string item"), authServices),
 626 | 			},
 627 | 		},
 628 | 		{
 629 | 			name: "float array",
 630 | 			in: []map[string]any{
 631 | 				{
 632 | 					"name":        "my_array",
 633 | 					"type":        "array",
 634 | 					"description": "this param is an array of floats",
 635 | 					"items": map[string]string{
 636 | 						"name":        "my_float",
 637 | 						"type":        "float",
 638 | 						"description": "float item",
 639 | 					},
 640 | 					"authServices": []map[string]string{
 641 | 						{
 642 | 							"name":  "my-google-auth-service",
 643 | 							"field": "user_id",
 644 | 						},
 645 | 						{
 646 | 							"name":  "other-auth-service",
 647 | 							"field": "user_id",
 648 | 						},
 649 | 					},
 650 | 				},
 651 | 			},
 652 | 			want: tools.Parameters{
 653 | 				tools.NewArrayParameterWithAuth("my_array", "this param is an array of floats", tools.NewFloatParameter("my_float", "float item"), authServices),
 654 | 			},
 655 | 		},
 656 | 		{
 657 | 			name: "map",
 658 | 			in: []map[string]any{
 659 | 				{
 660 | 					"name":        "my_map",
 661 | 					"type":        "map",
 662 | 					"description": "this param is a map of strings",
 663 | 					"valueType":   "string",
 664 | 					"authServices": []map[string]string{
 665 | 						{"name": "my-google-auth-service", "field": "user_id"},
 666 | 						{"name": "other-auth-service", "field": "user_id"},
 667 | 					},
 668 | 				},
 669 | 			},
 670 | 			want: tools.Parameters{
 671 | 				tools.NewMapParameterWithAuth("my_map", "this param is a map of strings", "string", authServices),
 672 | 			},
 673 | 		},
 674 | 	}
 675 | 	for _, tc := range tcs {
 676 | 		t.Run(tc.name, func(t *testing.T) {
 677 | 			var got tools.Parameters
 678 | 			// parse map to bytes
 679 | 			data, err := yaml.Marshal(tc.in)
 680 | 			if err != nil {
 681 | 				t.Fatalf("unable to marshal input to yaml: %s", err)
 682 | 			}
 683 | 			// parse bytes to object
 684 | 			err = yaml.UnmarshalContext(ctx, data, &got)
 685 | 			if err != nil {
 686 | 				t.Fatalf("unable to unmarshal: %s", err)
 687 | 			}
 688 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
 689 | 				t.Fatalf("incorrect parse: diff %v", diff)
 690 | 			}
 691 | 		})
 692 | 	}
 693 | }
 694 | 
 695 | func TestParametersParse(t *testing.T) {
 696 | 	intValue := 2
 697 | 	floatValue := 1.5
 698 | 	tcs := []struct {
 699 | 		name   string
 700 | 		params tools.Parameters
 701 | 		in     map[string]any
 702 | 		want   tools.ParamValues
 703 | 	}{
 704 | 		// ... (primitive type tests are unchanged)
 705 | 		{
 706 | 			name: "string",
 707 | 			params: tools.Parameters{
 708 | 				tools.NewStringParameter("my_string", "this param is a string"),
 709 | 			},
 710 | 			in: map[string]any{
 711 | 				"my_string": "hello world",
 712 | 			},
 713 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "hello world"}},
 714 | 		},
 715 | 		{
 716 | 			name: "not string",
 717 | 			params: tools.Parameters{
 718 | 				tools.NewStringParameter("my_string", "this param is a string"),
 719 | 			},
 720 | 			in: map[string]any{
 721 | 				"my_string": 4,
 722 | 			},
 723 | 		},
 724 | 		{
 725 | 			name: "string allowed",
 726 | 			params: tools.Parameters{
 727 | 				tools.NewStringParameterWithAllowedValues("my_string", "this param is a string", []any{"foo"}),
 728 | 			},
 729 | 			in: map[string]any{
 730 | 				"my_string": "foo",
 731 | 			},
 732 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "foo"}},
 733 | 		},
 734 | 		{
 735 | 			name: "string allowed regex",
 736 | 			params: tools.Parameters{
 737 | 				tools.NewStringParameterWithAllowedValues("my_string", "this param is a string", []any{"^f.*"}),
 738 | 			},
 739 | 			in: map[string]any{
 740 | 				"my_string": "foo",
 741 | 			},
 742 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "foo"}},
 743 | 		},
 744 | 		{
 745 | 			name: "string not allowed",
 746 | 			params: tools.Parameters{
 747 | 				tools.NewStringParameterWithAllowedValues("my_string", "this param is a string", []any{"foo"}),
 748 | 			},
 749 | 			in: map[string]any{
 750 | 				"my_string": "bar",
 751 | 			},
 752 | 		},
 753 | 		{
 754 | 			name: "string not allowed regex",
 755 | 			params: tools.Parameters{
 756 | 				tools.NewStringParameterWithAllowedValues("my_string", "this param is a string", []any{"^f.*"}),
 757 | 			},
 758 | 			in: map[string]any{
 759 | 				"my_string": "bar",
 760 | 			},
 761 | 		},
 762 | 		{
 763 | 			name: "string excluded",
 764 | 			params: tools.Parameters{
 765 | 				tools.NewStringParameterWithExcludedValues("my_string", "this param is a string", []any{"foo"}),
 766 | 			},
 767 | 			in: map[string]any{
 768 | 				"my_string": "foo",
 769 | 			},
 770 | 		},
 771 | 		{
 772 | 			name: "string excluded regex",
 773 | 			params: tools.Parameters{
 774 | 				tools.NewStringParameterWithExcludedValues("my_string", "this param is a string", []any{"^f.*"}),
 775 | 			},
 776 | 			in: map[string]any{
 777 | 				"my_string": "foo",
 778 | 			},
 779 | 		},
 780 | 		{
 781 | 			name: "string not excluded",
 782 | 			params: tools.Parameters{
 783 | 				tools.NewStringParameterWithExcludedValues("my_string", "this param is a string", []any{"foo"}),
 784 | 			},
 785 | 			in: map[string]any{
 786 | 				"my_string": "bar",
 787 | 			},
 788 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "bar"}},
 789 | 		},
 790 | 		{
 791 | 			name: "string with escape backticks",
 792 | 			params: tools.Parameters{
 793 | 				tools.NewStringParameterWithEscape("my_string", "this param is a string", "backticks"),
 794 | 			},
 795 | 			in: map[string]any{
 796 | 				"my_string": "foo",
 797 | 			},
 798 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "`foo`"}},
 799 | 		},
 800 | 		{
 801 | 			name: "string with escape double quotes",
 802 | 			params: tools.Parameters{
 803 | 				tools.NewStringParameterWithEscape("my_string", "this param is a string", "double-quotes"),
 804 | 			},
 805 | 			in: map[string]any{
 806 | 				"my_string": "foo",
 807 | 			},
 808 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: `"foo"`}},
 809 | 		},
 810 | 		{
 811 | 			name: "string with escape single quotes",
 812 | 			params: tools.Parameters{
 813 | 				tools.NewStringParameterWithEscape("my_string", "this param is a string", "single-quotes"),
 814 | 			},
 815 | 			in: map[string]any{
 816 | 				"my_string": "foo",
 817 | 			},
 818 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: `'foo'`}},
 819 | 		},
 820 | 		{
 821 | 			name: "string with escape square brackets",
 822 | 			params: tools.Parameters{
 823 | 				tools.NewStringParameterWithEscape("my_string", "this param is a string", "square-brackets"),
 824 | 			},
 825 | 			in: map[string]any{
 826 | 				"my_string": "foo",
 827 | 			},
 828 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "[foo]"}},
 829 | 		},
 830 | 		{
 831 | 			name: "int",
 832 | 			params: tools.Parameters{
 833 | 				tools.NewIntParameter("my_int", "this param is an int"),
 834 | 			},
 835 | 			in: map[string]any{
 836 | 				"my_int": 100,
 837 | 			},
 838 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 100}},
 839 | 		},
 840 | 		{
 841 | 			name: "not int",
 842 | 			params: tools.Parameters{
 843 | 				tools.NewIntParameter("my_int", "this param is an int"),
 844 | 			},
 845 | 			in: map[string]any{
 846 | 				"my_int": 14.5,
 847 | 			},
 848 | 		},
 849 | 		{
 850 | 			name: "not int (big)",
 851 | 			params: tools.Parameters{
 852 | 				tools.NewIntParameter("my_int", "this param is an int"),
 853 | 			},
 854 | 			in: map[string]any{
 855 | 				"my_int": math.MaxInt64,
 856 | 			},
 857 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: math.MaxInt64}},
 858 | 		},
 859 | 		{
 860 | 			name: "int allowed",
 861 | 			params: tools.Parameters{
 862 | 				tools.NewIntParameterWithAllowedValues("my_int", "this param is an int", []any{1}),
 863 | 			},
 864 | 			in: map[string]any{
 865 | 				"my_int": 1,
 866 | 			},
 867 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 1}},
 868 | 		},
 869 | 		{
 870 | 			name: "int allowed regex",
 871 | 			params: tools.Parameters{
 872 | 				tools.NewIntParameterWithAllowedValues("my_int", "this param is an int", []any{"^\\d{2}$"}),
 873 | 			},
 874 | 			in: map[string]any{
 875 | 				"my_int": 10,
 876 | 			},
 877 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 10}},
 878 | 		},
 879 | 		{
 880 | 			name: "int not allowed",
 881 | 			params: tools.Parameters{
 882 | 				tools.NewIntParameterWithAllowedValues("my_int", "this param is an int", []any{1}),
 883 | 			},
 884 | 			in: map[string]any{
 885 | 				"my_int": 2,
 886 | 			},
 887 | 		},
 888 | 		{
 889 | 			name: "int not allowed regex",
 890 | 			params: tools.Parameters{
 891 | 				tools.NewIntParameterWithAllowedValues("my_int", "this param is an int", []any{"^\\d{2}$"}),
 892 | 			},
 893 | 			in: map[string]any{
 894 | 				"my_int": 100,
 895 | 			},
 896 | 		},
 897 | 		{
 898 | 			name: "int excluded",
 899 | 			params: tools.Parameters{
 900 | 				tools.NewIntParameterWithExcludedValues("my_int", "this param is an int", []any{1}),
 901 | 			},
 902 | 			in: map[string]any{
 903 | 				"my_int": 1,
 904 | 			},
 905 | 		},
 906 | 		{
 907 | 			name: "int excluded regex",
 908 | 			params: tools.Parameters{
 909 | 				tools.NewIntParameterWithExcludedValues("my_int", "this param is an int", []any{"^\\d{2}$"}),
 910 | 			},
 911 | 			in: map[string]any{
 912 | 				"my_int": 10,
 913 | 			},
 914 | 		},
 915 | 		{
 916 | 			name: "int not excluded",
 917 | 			params: tools.Parameters{
 918 | 				tools.NewIntParameterWithExcludedValues("my_int", "this param is an int", []any{1}),
 919 | 			},
 920 | 			in: map[string]any{
 921 | 				"my_int": 2,
 922 | 			},
 923 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 2}},
 924 | 		},
 925 | 		{
 926 | 			name: "int not excluded regex",
 927 | 			params: tools.Parameters{
 928 | 				tools.NewIntParameterWithExcludedValues("my_int", "this param is an int", []any{"^\\d{2}$"}),
 929 | 			},
 930 | 			in: map[string]any{
 931 | 				"my_int": 2,
 932 | 			},
 933 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 2}},
 934 | 		},
 935 | 		{
 936 | 			name: "int minValue",
 937 | 			params: tools.Parameters{
 938 | 				tools.NewIntParameterWithRange("my_int", "this param is an int", &intValue, nil),
 939 | 			},
 940 | 			in: map[string]any{
 941 | 				"my_int": 3,
 942 | 			},
 943 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 3}},
 944 | 		},
 945 | 		{
 946 | 			name: "int minValue disallow",
 947 | 			params: tools.Parameters{
 948 | 				tools.NewIntParameterWithRange("my_int", "this param is an int", &intValue, nil),
 949 | 			},
 950 | 			in: map[string]any{
 951 | 				"my_int": 1,
 952 | 			},
 953 | 		},
 954 | 		{
 955 | 			name: "int maxValue",
 956 | 			params: tools.Parameters{
 957 | 				tools.NewIntParameterWithRange("my_int", "this param is an int", nil, &intValue),
 958 | 			},
 959 | 			in: map[string]any{
 960 | 				"my_int": 1,
 961 | 			},
 962 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 1}},
 963 | 		},
 964 | 		{
 965 | 			name: "int maxValue disallow",
 966 | 			params: tools.Parameters{
 967 | 				tools.NewIntParameterWithRange("my_int", "this param is an int", nil, &intValue),
 968 | 			},
 969 | 			in: map[string]any{
 970 | 				"my_int": 3,
 971 | 			},
 972 | 		},
 973 | 		{
 974 | 			name: "float",
 975 | 			params: tools.Parameters{
 976 | 				tools.NewFloatParameter("my_float", "this param is a float"),
 977 | 			},
 978 | 			in: map[string]any{
 979 | 				"my_float": 1.5,
 980 | 			},
 981 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.5}},
 982 | 		},
 983 | 		{
 984 | 			name: "not float",
 985 | 			params: tools.Parameters{
 986 | 				tools.NewFloatParameter("my_float", "this param is a float"),
 987 | 			},
 988 | 			in: map[string]any{
 989 | 				"my_float": true,
 990 | 			},
 991 | 		},
 992 | 		{
 993 | 			name: "float allowed",
 994 | 			params: tools.Parameters{
 995 | 				tools.NewFloatParameterWithAllowedValues("my_float", "this param is a float", []any{1.1}),
 996 | 			},
 997 | 			in: map[string]any{
 998 | 				"my_float": 1.1,
 999 | 			},
1000 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.1}},
1001 | 		},
1002 | 		{
1003 | 			name: "float allowed regex",
1004 | 			params: tools.Parameters{
1005 | 				tools.NewFloatParameterWithAllowedValues("my_float", "this param is a float", []any{"^0\\.\\d+$"}),
1006 | 			},
1007 | 			in: map[string]any{
1008 | 				"my_float": 0.99,
1009 | 			},
1010 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 0.99}},
1011 | 		},
1012 | 		{
1013 | 			name: "float not allowed",
1014 | 			params: tools.Parameters{
1015 | 				tools.NewFloatParameterWithAllowedValues("my_float", "this param is a float", []any{1.1}),
1016 | 			},
1017 | 			in: map[string]any{
1018 | 				"my_float": 1.2,
1019 | 			},
1020 | 		},
1021 | 		{
1022 | 			name: "float not allowed regex",
1023 | 			params: tools.Parameters{
1024 | 				tools.NewFloatParameterWithAllowedValues("my_float", "this param is a float", []any{"^0\\.\\d+$"}),
1025 | 			},
1026 | 			in: map[string]any{
1027 | 				"my_float": 1.99,
1028 | 			},
1029 | 		},
1030 | 		{
1031 | 			name: "float excluded",
1032 | 			params: tools.Parameters{
1033 | 				tools.NewFloatParameterWithExcludedValues("my_float", "this param is a float", []any{1.1}),
1034 | 			},
1035 | 			in: map[string]any{
1036 | 				"my_float": 1.1,
1037 | 			},
1038 | 		},
1039 | 		{
1040 | 			name: "float excluded regex",
1041 | 			params: tools.Parameters{
1042 | 				tools.NewFloatParameterWithExcludedValues("my_float", "this param is a float", []any{"^0\\.\\d+$"}),
1043 | 			},
1044 | 			in: map[string]any{
1045 | 				"my_float": 0.99,
1046 | 			},
1047 | 		},
1048 | 		{
1049 | 			name: "float not excluded",
1050 | 			params: tools.Parameters{
1051 | 				tools.NewFloatParameterWithExcludedValues("my_float", "this param is a float", []any{1.1}),
1052 | 			},
1053 | 			in: map[string]any{
1054 | 				"my_float": 1.2,
1055 | 			},
1056 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.2}},
1057 | 		},
1058 | 		{
1059 | 			name: "float not excluded regex",
1060 | 			params: tools.Parameters{
1061 | 				tools.NewFloatParameterWithExcludedValues("my_float", "this param is a float", []any{"^0\\.\\d+$"}),
1062 | 			},
1063 | 			in: map[string]any{
1064 | 				"my_float": 1.99,
1065 | 			},
1066 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.99}},
1067 | 		},
1068 | 
1069 | 		{
1070 | 			name: "float minValue",
1071 | 			params: tools.Parameters{
1072 | 				tools.NewFloatParameterWithRange("my_float", "this param is a float", &floatValue, nil),
1073 | 			},
1074 | 			in: map[string]any{
1075 | 				"my_float": 1.8,
1076 | 			},
1077 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.8}},
1078 | 		},
1079 | 		{
1080 | 			name: "float minValue disallow",
1081 | 			params: tools.Parameters{
1082 | 				tools.NewFloatParameterWithRange("my_float", "this param is a float", &floatValue, nil),
1083 | 			},
1084 | 			in: map[string]any{
1085 | 				"my_float": 1.2,
1086 | 			},
1087 | 		},
1088 | 		{
1089 | 			name: "float maxValue",
1090 | 			params: tools.Parameters{
1091 | 				tools.NewFloatParameterWithRange("my_float", "this param is a float", nil, &floatValue),
1092 | 			},
1093 | 			in: map[string]any{
1094 | 				"my_float": 1.2,
1095 | 			},
1096 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.2}},
1097 | 		},
1098 | 		{
1099 | 			name: "float maxValue disallow",
1100 | 			params: tools.Parameters{
1101 | 				tools.NewFloatParameterWithRange("my_float", "this param is a float", nil, &floatValue),
1102 | 			},
1103 | 			in: map[string]any{
1104 | 				"my_float": 1.8,
1105 | 			},
1106 | 		},
1107 | 		{
1108 | 			name: "bool",
1109 | 			params: tools.Parameters{
1110 | 				tools.NewBooleanParameter("my_bool", "this param is a bool"),
1111 | 			},
1112 | 			in: map[string]any{
1113 | 				"my_bool": true,
1114 | 			},
1115 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: true}},
1116 | 		},
1117 | 		{
1118 | 			name: "not bool",
1119 | 			params: tools.Parameters{
1120 | 				tools.NewBooleanParameter("my_bool", "this param is a bool"),
1121 | 			},
1122 | 			in: map[string]any{
1123 | 				"my_bool": 1.5,
1124 | 			},
1125 | 		},
1126 | 		{
1127 | 			name: "bool allowed",
1128 | 			params: tools.Parameters{
1129 | 				tools.NewBooleanParameterWithAllowedValues("my_bool", "this param is a bool", []any{false}),
1130 | 			},
1131 | 			in: map[string]any{
1132 | 				"my_bool": false,
1133 | 			},
1134 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: false}},
1135 | 		},
1136 | 		{
1137 | 			name: "bool not allowed",
1138 | 			params: tools.Parameters{
1139 | 				tools.NewBooleanParameterWithAllowedValues("my_bool", "this param is a bool", []any{false}),
1140 | 			},
1141 | 			in: map[string]any{
1142 | 				"my_bool": true,
1143 | 			},
1144 | 		},
1145 | 		{
1146 | 			name: "bool excluded",
1147 | 			params: tools.Parameters{
1148 | 				tools.NewBooleanParameterWithExcludedValues("my_bool", "this param is a bool", []any{true}),
1149 | 			},
1150 | 			in: map[string]any{
1151 | 				"my_bool": true,
1152 | 			},
1153 | 		},
1154 | 		{
1155 | 			name: "bool not excluded",
1156 | 			params: tools.Parameters{
1157 | 				tools.NewBooleanParameterWithExcludedValues("my_bool", "this param is a bool", []any{false}),
1158 | 			},
1159 | 			in: map[string]any{
1160 | 				"my_bool": true,
1161 | 			},
1162 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: true}},
1163 | 		},
1164 | 		{
1165 | 			name: "string default",
1166 | 			params: tools.Parameters{
1167 | 				tools.NewStringParameterWithDefault("my_string", "foo", "this param is a string"),
1168 | 			},
1169 | 			in:   map[string]any{},
1170 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "foo"}},
1171 | 		},
1172 | 		{
1173 | 			name: "int default",
1174 | 			params: tools.Parameters{
1175 | 				tools.NewIntParameterWithDefault("my_int", 100, "this param is an int"),
1176 | 			},
1177 | 			in:   map[string]any{},
1178 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 100}},
1179 | 		},
1180 | 		{
1181 | 			name: "int (big)",
1182 | 			params: tools.Parameters{
1183 | 				tools.NewIntParameterWithDefault("my_big_int", math.MaxInt64, "this param is an int"),
1184 | 			},
1185 | 			in:   map[string]any{},
1186 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_big_int", Value: math.MaxInt64}},
1187 | 		},
1188 | 		{
1189 | 			name: "float default",
1190 | 			params: tools.Parameters{
1191 | 				tools.NewFloatParameterWithDefault("my_float", 1.1, "this param is a float"),
1192 | 			},
1193 | 			in:   map[string]any{},
1194 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 1.1}},
1195 | 		},
1196 | 		{
1197 | 			name: "bool default",
1198 | 			params: tools.Parameters{
1199 | 				tools.NewBooleanParameterWithDefault("my_bool", true, "this param is a bool"),
1200 | 			},
1201 | 			in:   map[string]any{},
1202 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: true}},
1203 | 		},
1204 | 		{
1205 | 			name: "string not required",
1206 | 			params: tools.Parameters{
1207 | 				tools.NewStringParameterWithRequired("my_string", "this param is a string", false),
1208 | 			},
1209 | 			in:   map[string]any{},
1210 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_string", Value: nil}},
1211 | 		},
1212 | 		{
1213 | 			name: "int not required",
1214 | 			params: tools.Parameters{
1215 | 				tools.NewIntParameterWithRequired("my_int", "this param is an int", false),
1216 | 			},
1217 | 			in:   map[string]any{},
1218 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_int", Value: nil}},
1219 | 		},
1220 | 		{
1221 | 			name: "float not required",
1222 | 			params: tools.Parameters{
1223 | 				tools.NewFloatParameterWithRequired("my_float", "this param is a float", false),
1224 | 			},
1225 | 			in:   map[string]any{},
1226 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_float", Value: nil}},
1227 | 		},
1228 | 		{
1229 | 			name: "bool not required",
1230 | 			params: tools.Parameters{
1231 | 				tools.NewBooleanParameterWithRequired("my_bool", "this param is a bool", false),
1232 | 			},
1233 | 			in:   map[string]any{},
1234 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: nil}},
1235 | 		},
1236 | 		{
1237 | 			name: "array with string escape",
1238 | 			params: tools.Parameters{
1239 | 				tools.NewArrayParameter("my_array", "an array", tools.NewStringParameterWithEscape("my_string", "string item", "backticks")),
1240 | 			},
1241 | 			in: map[string]any{
1242 | 				"my_array": []string{"val1", "val2"},
1243 | 			},
1244 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_array", Value: []any{string("`val1`"), string("`val2`")}}},
1245 | 		},
1246 | 		{
1247 | 			name: "map",
1248 | 			params: tools.Parameters{
1249 | 				tools.NewMapParameter("my_map", "a map", "string"),
1250 | 			},
1251 | 			in: map[string]any{
1252 | 				"my_map": map[string]any{"key1": "val1", "key2": "val2"},
1253 | 			},
1254 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_map", Value: map[string]any{"key1": "val1", "key2": "val2"}}},
1255 | 		},
1256 | 		{
1257 | 			name: "generic map",
1258 | 			params: tools.Parameters{
1259 | 				tools.NewMapParameter("my_map_generic_type", "a generic map", ""),
1260 | 			},
1261 | 			in: map[string]any{
1262 | 				"my_map_generic_type": map[string]any{"key1": "val1", "key2": 123, "key3": true},
1263 | 			},
1264 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_map_generic_type", Value: map[string]any{"key1": "val1", "key2": int64(123), "key3": true}}},
1265 | 		},
1266 | 		{
1267 | 			name: "not map (value type mismatch)",
1268 | 			params: tools.Parameters{
1269 | 				tools.NewMapParameter("my_map", "a map", "string"),
1270 | 			},
1271 | 			in: map[string]any{
1272 | 				"my_map": map[string]any{"key1": 123},
1273 | 			},
1274 | 		},
1275 | 		{
1276 | 			name: "map default",
1277 | 			params: tools.Parameters{
1278 | 				tools.NewMapParameterWithDefault("my_map_default", map[string]any{"default_key": "default_val"}, "a map", "string"),
1279 | 			},
1280 | 			in:   map[string]any{},
1281 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_map_default", Value: map[string]any{"default_key": "default_val"}}},
1282 | 		},
1283 | 		{
1284 | 			name: "map not required",
1285 | 			params: tools.Parameters{
1286 | 				tools.NewMapParameterWithRequired("my_map_not_required", "a map", false, "string"),
1287 | 			},
1288 | 			in:   map[string]any{},
1289 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_map_not_required", Value: nil}},
1290 | 		},
1291 | 		{
1292 | 			name: "map allowed",
1293 | 			params: tools.Parameters{
1294 | 				tools.NewMapParameterWithAllowedValues("my_map", "a map", []any{map[string]any{"key1": "val1"}}, "string"),
1295 | 			},
1296 | 			in: map[string]any{
1297 | 				"my_map": map[string]any{"key1": "val1"},
1298 | 			},
1299 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_map", Value: map[string]any{"key1": "val1"}}},
1300 | 		},
1301 | 		{
1302 | 			name: "map not allowed",
1303 | 			params: tools.Parameters{
1304 | 				tools.NewMapParameterWithAllowedValues("my_map", "a map", []any{map[string]any{"key1": "val1"}}, "string"),
1305 | 			},
1306 | 			in: map[string]any{
1307 | 				"my_map": map[string]any{"key1": "val2"},
1308 | 			},
1309 | 		},
1310 | 		{
1311 | 			name: "map excluded",
1312 | 			params: tools.Parameters{
1313 | 				tools.NewMapParameterWithExcludedValues("my_map", "a map", []any{map[string]any{"key1": "val1"}}, "string"),
1314 | 			},
1315 | 			in: map[string]any{
1316 | 				"my_map": map[string]any{"key1": "val1"},
1317 | 			},
1318 | 		},
1319 | 		{
1320 | 			name: "map not excluded",
1321 | 			params: tools.Parameters{
1322 | 				tools.NewMapParameterWithExcludedValues("my_map", "a map", []any{map[string]any{"key1": "val1"}}, "string"),
1323 | 			},
1324 | 			in: map[string]any{
1325 | 				"my_map": map[string]any{"key1": "val2"},
1326 | 			},
1327 | 			want: tools.ParamValues{tools.ParamValue{Name: "my_map", Value: map[string]any{"key1": "val2"}}},
1328 | 		},
1329 | 	}
1330 | 	for _, tc := range tcs {
1331 | 		t.Run(tc.name, func(t *testing.T) {
1332 | 			// parse map to bytes
1333 | 			data, err := json.Marshal(tc.in)
1334 | 			if err != nil {
1335 | 				t.Fatalf("unable to marshal input to yaml: %s", err)
1336 | 			}
1337 | 			// parse bytes to object
1338 | 			var m map[string]any
1339 | 
1340 | 			d := json.NewDecoder(bytes.NewReader(data))
1341 | 			d.UseNumber()
1342 | 			err = d.Decode(&m)
1343 | 			if err != nil {
1344 | 				t.Fatalf("unable to unmarshal: %s", err)
1345 | 			}
1346 | 
1347 | 			wantErr := len(tc.want) == 0 // error is expected if no items in want
1348 | 			gotAll, err := tools.ParseParams(tc.params, m, make(map[string]map[string]any))
1349 | 			if err != nil {
1350 | 				if wantErr {
1351 | 					return
1352 | 				}
1353 | 				t.Fatalf("unexpected error from ParseParams: %s", err)
1354 | 			}
1355 | 			if wantErr {
1356 | 				t.Fatalf("expected error but Param parsed successfully: %s", gotAll)
1357 | 			}
1358 | 
1359 | 			// Use cmp.Diff for robust comparison
1360 | 			if diff := cmp.Diff(tc.want, gotAll); diff != "" {
1361 | 				t.Fatalf("ParseParams() mismatch (-want +got):\n%s", diff)
1362 | 			}
1363 | 		})
1364 | 	}
1365 | }
1366 | 
1367 | func TestAuthParametersParse(t *testing.T) {
1368 | 	authServices := []tools.ParamAuthService{
1369 | 		{
1370 | 			Name:  "my-google-auth-service",
1371 | 			Field: "auth_field",
1372 | 		},
1373 | 		{
1374 | 			Name:  "other-auth-service",
1375 | 			Field: "other_auth_field",
1376 | 		}}
1377 | 	tcs := []struct {
1378 | 		name      string
1379 | 		params    tools.Parameters
1380 | 		in        map[string]any
1381 | 		claimsMap map[string]map[string]any
1382 | 		want      tools.ParamValues
1383 | 	}{
1384 | 		{
1385 | 			name: "string",
1386 | 			params: tools.Parameters{
1387 | 				tools.NewStringParameterWithAuth("my_string", "this param is a string", authServices),
1388 | 			},
1389 | 			in: map[string]any{
1390 | 				"my_string": "hello world",
1391 | 			},
1392 | 			claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": "hello"}},
1393 | 			want:      tools.ParamValues{tools.ParamValue{Name: "my_string", Value: "hello"}},
1394 | 		},
1395 | 		{
1396 | 			name: "not string",
1397 | 			params: tools.Parameters{
1398 | 				tools.NewStringParameterWithAuth("my_string", "this param is a string", authServices),
1399 | 			},
1400 | 			in: map[string]any{
1401 | 				"my_string": 4,
1402 | 			},
1403 | 			claimsMap: map[string]map[string]any{},
1404 | 		},
1405 | 		{
1406 | 			name: "int",
1407 | 			params: tools.Parameters{
1408 | 				tools.NewIntParameterWithAuth("my_int", "this param is an int", authServices),
1409 | 			},
1410 | 			in: map[string]any{
1411 | 				"my_int": 100,
1412 | 			},
1413 | 			claimsMap: map[string]map[string]any{"other-auth-service": {"other_auth_field": 120}},
1414 | 			want:      tools.ParamValues{tools.ParamValue{Name: "my_int", Value: 120}},
1415 | 		},
1416 | 		{
1417 | 			name: "not int",
1418 | 			params: tools.Parameters{
1419 | 				tools.NewIntParameterWithAuth("my_int", "this param is an int", authServices),
1420 | 			},
1421 | 			in: map[string]any{
1422 | 				"my_int": 14.5,
1423 | 			},
1424 | 			claimsMap: map[string]map[string]any{},
1425 | 		},
1426 | 		{
1427 | 			name: "float",
1428 | 			params: tools.Parameters{
1429 | 				tools.NewFloatParameterWithAuth("my_float", "this param is a float", authServices),
1430 | 			},
1431 | 			in: map[string]any{
1432 | 				"my_float": 1.5,
1433 | 			},
1434 | 			claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": 2.1}},
1435 | 			want:      tools.ParamValues{tools.ParamValue{Name: "my_float", Value: 2.1}},
1436 | 		},
1437 | 		{
1438 | 			name: "not float",
1439 | 			params: tools.Parameters{
1440 | 				tools.NewFloatParameterWithAuth("my_float", "this param is a float", authServices),
1441 | 			},
1442 | 			in: map[string]any{
1443 | 				"my_float": true,
1444 | 			},
1445 | 			claimsMap: map[string]map[string]any{},
1446 | 		},
1447 | 		{
1448 | 			name: "bool",
1449 | 			params: tools.Parameters{
1450 | 				tools.NewBooleanParameterWithAuth("my_bool", "this param is a bool", authServices),
1451 | 			},
1452 | 			in: map[string]any{
1453 | 				"my_bool": true,
1454 | 			},
1455 | 			claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": false}},
1456 | 			want:      tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: false}},
1457 | 		},
1458 | 		{
1459 | 			name: "not bool",
1460 | 			params: tools.Parameters{
1461 | 				tools.NewBooleanParameterWithAuth("my_bool", "this param is a bool", authServices),
1462 | 			},
1463 | 			in: map[string]any{
1464 | 				"my_bool": 1.5,
1465 | 			},
1466 | 			claimsMap: map[string]map[string]any{},
1467 | 		},
1468 | 		{
1469 | 			name: "username",
1470 | 			params: tools.Parameters{
1471 | 				tools.NewStringParameterWithAuth("username", "username string", authServices),
1472 | 			},
1473 | 			in: map[string]any{
1474 | 				"username": "Violet",
1475 | 			},
1476 | 			claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": "Alice"}},
1477 | 			want:      tools.ParamValues{tools.ParamValue{Name: "username", Value: "Alice"}},
1478 | 		},
1479 | 		{
1480 | 			name: "expect claim error",
1481 | 			params: tools.Parameters{
1482 | 				tools.NewStringParameterWithAuth("username", "username string", authServices),
1483 | 			},
1484 | 			in: map[string]any{
1485 | 				"username": "Violet",
1486 | 			},
1487 | 			claimsMap: map[string]map[string]any{"my-google-auth-service": {"not_an_auth_field": "Alice"}},
1488 | 		},
1489 | 		{
1490 | 			name: "map",
1491 | 			params: tools.Parameters{
1492 | 				tools.NewMapParameterWithAuth("my_map", "a map", "string", authServices),
1493 | 			},
1494 | 			in:        map[string]any{"my_map": map[string]any{"key1": "val1"}},
1495 | 			claimsMap: map[string]map[string]any{"my-google-auth-service": {"auth_field": map[string]any{"authed_key": "authed_val"}}},
1496 | 			want:      tools.ParamValues{tools.ParamValue{Name: "my_map", Value: map[string]any{"authed_key": "authed_val"}}},
1497 | 		},
1498 | 	}
1499 | 	for _, tc := range tcs {
1500 | 		t.Run(tc.name, func(t *testing.T) {
1501 | 			// parse map to bytes
1502 | 			data, err := json.Marshal(tc.in)
1503 | 			if err != nil {
1504 | 				t.Fatalf("unable to marshal input to yaml: %s", err)
1505 | 			}
1506 | 			// parse bytes to object
1507 | 			var m map[string]any
1508 | 			d := json.NewDecoder(bytes.NewReader(data))
1509 | 			d.UseNumber()
1510 | 			err = d.Decode(&m)
1511 | 			if err != nil {
1512 | 				t.Fatalf("unable to unmarshal: %s", err)
1513 | 			}
1514 | 
1515 | 			gotAll, err := tools.ParseParams(tc.params, m, tc.claimsMap)
1516 | 			if err != nil {
1517 | 				if len(tc.want) == 0 {
1518 | 					// error is expected if no items in want
1519 | 					return
1520 | 				}
1521 | 				t.Fatalf("unexpected error from ParseParams: %s", err)
1522 | 			}
1523 | 
1524 | 			if diff := cmp.Diff(tc.want, gotAll); diff != "" {
1525 | 				t.Fatalf("ParseParams() mismatch (-want +got):\n%s", diff)
1526 | 			}
1527 | 		})
1528 | 	}
1529 | }
1530 | 
1531 | func TestParamValues(t *testing.T) {
1532 | 	tcs := []struct {
1533 | 		name              string
1534 | 		in                tools.ParamValues
1535 | 		wantSlice         []any
1536 | 		wantMap           map[string]interface{}
1537 | 		wantMapOrdered    map[string]interface{}
1538 | 		wantMapWithDollar map[string]interface{}
1539 | 	}{
1540 | 		{
1541 | 			name:           "string",
1542 | 			in:             tools.ParamValues{tools.ParamValue{Name: "my_bool", Value: true}, tools.ParamValue{Name: "my_string", Value: "hello world"}},
1543 | 			wantSlice:      []any{true, "hello world"},
1544 | 			wantMap:        map[string]interface{}{"my_bool": true, "my_string": "hello world"},
1545 | 			wantMapOrdered: map[string]interface{}{"p1": true, "p2": "hello world"},
1546 | 			wantMapWithDollar: map[string]interface{}{
1547 | 				"$my_bool":   true,
1548 | 				"$my_string": "hello world",
1549 | 			},
1550 | 		},
1551 | 	}
1552 | 	for _, tc := range tcs {
1553 | 		t.Run(tc.name, func(t *testing.T) {
1554 | 			gotSlice := tc.in.AsSlice()
1555 | 			gotMap := tc.in.AsMap()
1556 | 			gotMapOrdered := tc.in.AsMapByOrderedKeys()
1557 | 			gotMapWithDollar := tc.in.AsMapWithDollarPrefix()
1558 | 
1559 | 			for i, got := range gotSlice {
1560 | 				want := tc.wantSlice[i]
1561 | 				if got != want {
1562 | 					t.Fatalf("unexpected value: got %q, want %q", got, want)
1563 | 				}
1564 | 			}
1565 | 			for i, got := range gotMap {
1566 | 				want := tc.wantMap[i]
1567 | 				if got != want {
1568 | 					t.Fatalf("unexpected value: got %q, want %q", got, want)
1569 | 				}
1570 | 			}
1571 | 			for i, got := range gotMapOrdered {
1572 | 				want := tc.wantMapOrdered[i]
1573 | 				if got != want {
1574 | 					t.Fatalf("unexpected value: got %q, want %q", got, want)
1575 | 				}
1576 | 			}
1577 | 			for key, got := range gotMapWithDollar {
1578 | 				want := tc.wantMapWithDollar[key]
1579 | 				if got != want {
1580 | 					t.Fatalf("unexpected value in AsMapWithDollarPrefix: got %q, want %q", got, want)
1581 | 				}
1582 | 			}
1583 | 		})
1584 | 	}
1585 | }
1586 | 
1587 | func TestParamManifest(t *testing.T) {
1588 | 	tcs := []struct {
1589 | 		name string
1590 | 		in   tools.Parameter
1591 | 		want tools.ParameterManifest
1592 | 	}{
1593 | 		{
1594 | 			name: "string",
1595 | 			in:   tools.NewStringParameter("foo-string", "bar"),
1596 | 			want: tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}},
1597 | 		},
1598 | 		{
1599 | 			name: "int",
1600 | 			in:   tools.NewIntParameter("foo-int", "bar"),
1601 | 			want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Required: true, Description: "bar", AuthServices: []string{}},
1602 | 		},
1603 | 		{
1604 | 			name: "float",
1605 | 			in:   tools.NewFloatParameter("foo-float", "bar"),
1606 | 			want: tools.ParameterManifest{Name: "foo-float", Type: "float", Required: true, Description: "bar", AuthServices: []string{}},
1607 | 		},
1608 | 		{
1609 | 			name: "boolean",
1610 | 			in:   tools.NewBooleanParameter("foo-bool", "bar"),
1611 | 			want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: true, Description: "bar", AuthServices: []string{}},
1612 | 		},
1613 | 		{
1614 | 			name: "array",
1615 | 			in:   tools.NewArrayParameter("foo-array", "bar", tools.NewStringParameter("foo-string", "bar")),
1616 | 			want: tools.ParameterManifest{
1617 | 				Name:         "foo-array",
1618 | 				Type:         "array",
1619 | 				Required:     true,
1620 | 				Description:  "bar",
1621 | 				AuthServices: []string{},
1622 | 				Items:        &tools.ParameterManifest{Name: "foo-string", Type: "string", Required: true, Description: "bar", AuthServices: []string{}},
1623 | 			},
1624 | 		},
1625 | 		{
1626 | 			name: "string default",
1627 | 			in:   tools.NewStringParameterWithDefault("foo-string", "foo", "bar"),
1628 | 			want: tools.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
1629 | 		},
1630 | 		{
1631 | 			name: "int default",
1632 | 			in:   tools.NewIntParameterWithDefault("foo-int", 1, "bar"),
1633 | 			want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", AuthServices: []string{}},
1634 | 		},
1635 | 		{
1636 | 			name: "float default",
1637 | 			in:   tools.NewFloatParameterWithDefault("foo-float", 1.1, "bar"),
1638 | 			want: tools.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", AuthServices: []string{}},
1639 | 		},
1640 | 		{
1641 | 			name: "boolean default",
1642 | 			in:   tools.NewBooleanParameterWithDefault("foo-bool", true, "bar"),
1643 | 			want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", AuthServices: []string{}},
1644 | 		},
1645 | 		{
1646 | 			name: "array default",
1647 | 			in:   tools.NewArrayParameterWithDefault("foo-array", []any{"foo", "bar"}, "bar", tools.NewStringParameter("foo-string", "bar")),
1648 | 			want: tools.ParameterManifest{
1649 | 				Name:         "foo-array",
1650 | 				Type:         "array",
1651 | 				Required:     false,
1652 | 				Description:  "bar",
1653 | 				AuthServices: []string{},
1654 | 				Items:        &tools.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
1655 | 			},
1656 | 		},
1657 | 		{
1658 | 			name: "string not required",
1659 | 			in:   tools.NewStringParameterWithRequired("foo-string", "bar", false),
1660 | 			want: tools.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
1661 | 		},
1662 | 		{
1663 | 			name: "int not required",
1664 | 			in:   tools.NewIntParameterWithRequired("foo-int", "bar", false),
1665 | 			want: tools.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", AuthServices: []string{}},
1666 | 		},
1667 | 		{
1668 | 			name: "float not required",
1669 | 			in:   tools.NewFloatParameterWithRequired("foo-float", "bar", false),
1670 | 			want: tools.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", AuthServices: []string{}},
1671 | 		},
1672 | 		{
1673 | 			name: "boolean not required",
1674 | 			in:   tools.NewBooleanParameterWithRequired("foo-bool", "bar", false),
1675 | 			want: tools.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", AuthServices: []string{}},
1676 | 		},
1677 | 		{
1678 | 			name: "array not required",
1679 | 			in:   tools.NewArrayParameterWithRequired("foo-array", "bar", false, tools.NewStringParameter("foo-string", "bar")),
1680 | 			want: tools.ParameterManifest{
1681 | 				Name:         "foo-array",
1682 | 				Type:         "array",
1683 | 				Required:     false,
1684 | 				Description:  "bar",
1685 | 				AuthServices: []string{},
1686 | 				Items:        &tools.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
1687 | 			},
1688 | 		},
1689 | 		{
1690 | 			name: "map with string values",
1691 | 			in:   tools.NewMapParameter("foo-map", "bar", "string"),
1692 | 			want: tools.ParameterManifest{
1693 | 				Name:                 "foo-map",
1694 | 				Type:                 "object",
1695 | 				Required:             true,
1696 | 				Description:          "bar",
1697 | 				AuthServices:         []string{},
1698 | 				AdditionalProperties: map[string]any{"type": "string"},
1699 | 			},
1700 | 		},
1701 | 		{
1702 | 			name: "map not required",
1703 | 			in:   tools.NewMapParameterWithRequired("foo-map", "bar", false, "string"),
1704 | 			want: tools.ParameterManifest{
1705 | 				Name:                 "foo-map",
1706 | 				Type:                 "object",
1707 | 				Required:             false,
1708 | 				Description:          "bar",
1709 | 				AuthServices:         []string{},
1710 | 				AdditionalProperties: map[string]any{"type": "string"},
1711 | 			},
1712 | 		},
1713 | 		{
1714 | 			name: "generic map (additionalProperties true)",
1715 | 			in:   tools.NewMapParameter("foo-map", "bar", ""),
1716 | 			want: tools.ParameterManifest{
1717 | 				Name:                 "foo-map",
1718 | 				Type:                 "object",
1719 | 				Required:             true,
1720 | 				Description:          "bar",
1721 | 				AuthServices:         []string{},
1722 | 				AdditionalProperties: true,
1723 | 			},
1724 | 		},
1725 | 	}
1726 | 	for _, tc := range tcs {
1727 | 		t.Run(tc.name, func(t *testing.T) {
1728 | 			got := tc.in.Manifest()
1729 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
1730 | 				t.Fatalf("unexpected manifest (-want +got):\n%s", diff)
1731 | 			}
1732 | 		})
1733 | 	}
1734 | }
1735 | 
1736 | func TestParamMcpManifest(t *testing.T) {
1737 | 	tcs := []struct {
1738 | 		name          string
1739 | 		in            tools.Parameter
1740 | 		want          tools.ParameterMcpManifest
1741 | 		wantAuthParam []string
1742 | 	}{
1743 | 		{
1744 | 			name:          "string",
1745 | 			in:            tools.NewStringParameter("foo-string", "bar"),
1746 | 			want:          tools.ParameterMcpManifest{Type: "string", Description: "bar"},
1747 | 			wantAuthParam: []string{},
1748 | 		},
1749 | 		{
1750 | 			name:          "int",
1751 | 			in:            tools.NewIntParameter("foo-int", "bar"),
1752 | 			want:          tools.ParameterMcpManifest{Type: "integer", Description: "bar"},
1753 | 			wantAuthParam: []string{},
1754 | 		},
1755 | 		{
1756 | 			name:          "float",
1757 | 			in:            tools.NewFloatParameter("foo-float", "bar"),
1758 | 			want:          tools.ParameterMcpManifest{Type: "number", Description: "bar"},
1759 | 			wantAuthParam: []string{},
1760 | 		},
1761 | 		{
1762 | 			name:          "boolean",
1763 | 			in:            tools.NewBooleanParameter("foo-bool", "bar"),
1764 | 			want:          tools.ParameterMcpManifest{Type: "boolean", Description: "bar"},
1765 | 			wantAuthParam: []string{},
1766 | 		},
1767 | 		{
1768 | 			name: "array",
1769 | 			in:   tools.NewArrayParameter("foo-array", "bar", tools.NewStringParameter("foo-string", "bar")),
1770 | 			want: tools.ParameterMcpManifest{
1771 | 				Type:        "array",
1772 | 				Description: "bar",
1773 | 				Items:       &tools.ParameterMcpManifest{Type: "string", Description: "bar"},
1774 | 			},
1775 | 			wantAuthParam: []string{},
1776 | 		},
1777 | 
1778 | 		{
1779 | 			name: "map with string values",
1780 | 			in:   tools.NewMapParameter("foo-map", "bar", "string"),
1781 | 			want: tools.ParameterMcpManifest{
1782 | 				Type:                 "object",
1783 | 				Description:          "bar",
1784 | 				AdditionalProperties: map[string]any{"type": "string"},
1785 | 			},
1786 | 			wantAuthParam: []string{},
1787 | 		},
1788 | 		{
1789 | 			name: "generic map (additionalProperties true)",
1790 | 			in:   tools.NewMapParameter("foo-map", "bar", ""),
1791 | 			want: tools.ParameterMcpManifest{
1792 | 				Type:                 "object",
1793 | 				Description:          "bar",
1794 | 				AdditionalProperties: true,
1795 | 			},
1796 | 			wantAuthParam: []string{},
1797 | 		},
1798 | 	}
1799 | 	for _, tc := range tcs {
1800 | 		t.Run(tc.name, func(t *testing.T) {
1801 | 			got, gotAuthParam := tc.in.McpManifest()
1802 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
1803 | 				t.Fatalf("unexpected manifest (-want +got):\n%s", diff)
1804 | 			}
1805 | 			slices.Sort(gotAuthParam)
1806 | 			if !reflect.DeepEqual(gotAuthParam, tc.wantAuthParam) {
1807 | 				t.Fatalf("unexpected auth param list: got %s, want %s", gotAuthParam, tc.wantAuthParam)
1808 | 			}
1809 | 		})
1810 | 	}
1811 | }
1812 | 
1813 | func TestMcpManifest(t *testing.T) {
1814 | 	authServices := []tools.ParamAuthService{
1815 | 		{
1816 | 			Name:  "my-google-auth-service",
1817 | 			Field: "auth_field",
1818 | 		},
1819 | 		{
1820 | 			Name:  "other-auth-service",
1821 | 			Field: "other_auth_field",
1822 | 		}}
1823 | 	tcs := []struct {
1824 | 		name          string
1825 | 		in            tools.Parameters
1826 | 		wantSchema    tools.McpToolsSchema
1827 | 		wantAuthParam map[string][]string
1828 | 	}{
1829 | 		{
1830 | 			name: "all types",
1831 | 			in: tools.Parameters{
1832 | 				tools.NewStringParameterWithDefault("foo-string", "foo", "bar"),
1833 | 				tools.NewStringParameter("foo-string2", "bar"),
1834 | 				tools.NewStringParameterWithAuth("foo-string3-auth", "bar", authServices),
1835 | 				tools.NewIntParameter("foo-int2", "bar"),
1836 | 				tools.NewFloatParameter("foo-float", "bar"),
1837 | 				tools.NewArrayParameter("foo-array2", "bar", tools.NewStringParameter("foo-string", "bar")),
1838 | 				tools.NewMapParameter("foo-map-int", "a map of ints", "integer"),
1839 | 				tools.NewMapParameter("foo-map-any", "a map of any", ""),
1840 | 			},
1841 | 			wantSchema: tools.McpToolsSchema{
1842 | 				Type: "object",
1843 | 				Properties: map[string]tools.ParameterMcpManifest{
1844 | 					"foo-string":       {Type: "string", Description: "bar"},
1845 | 					"foo-string2":      {Type: "string", Description: "bar"},
1846 | 					"foo-string3-auth": {Type: "string", Description: "bar"},
1847 | 					"foo-int2":         {Type: "integer", Description: "bar"},
1848 | 					"foo-float":        {Type: "number", Description: "bar"},
1849 | 					"foo-array2": {
1850 | 						Type:        "array",
1851 | 						Description: "bar",
1852 | 						Items:       &tools.ParameterMcpManifest{Type: "string", Description: "bar"},
1853 | 					},
1854 | 					"foo-map-int": {
1855 | 						Type:                 "object",
1856 | 						Description:          "a map of ints",
1857 | 						AdditionalProperties: map[string]any{"type": "integer"},
1858 | 					},
1859 | 					"foo-map-any": {
1860 | 						Type:                 "object",
1861 | 						Description:          "a map of any",
1862 | 						AdditionalProperties: true,
1863 | 					},
1864 | 				},
1865 | 				Required: []string{"foo-string2", "foo-string3-auth", "foo-int2", "foo-float", "foo-array2", "foo-map-int", "foo-map-any"},
1866 | 			},
1867 | 			wantAuthParam: map[string][]string{
1868 | 				"foo-string3-auth": []string{"my-google-auth-service", "other-auth-service"},
1869 | 			},
1870 | 		},
1871 | 	}
1872 | 	for _, tc := range tcs {
1873 | 		t.Run(tc.name, func(t *testing.T) {
1874 | 			gotSchema, gotAuthParam := tc.in.McpManifest()
1875 | 			if diff := cmp.Diff(tc.wantSchema, gotSchema); diff != "" {
1876 | 				t.Fatalf("unexpected manifest (-want +got):\n%s", diff)
1877 | 			}
1878 | 			if len(gotAuthParam) != len(tc.wantAuthParam) {
1879 | 				t.Fatalf("got %d items in auth param map, want %d", len(gotAuthParam), len(tc.wantAuthParam))
1880 | 			}
1881 | 			for k, want := range tc.wantAuthParam {
1882 | 				got, ok := gotAuthParam[k]
1883 | 				if !ok {
1884 | 					t.Fatalf("missing auth param: %s", k)
1885 | 				}
1886 | 				slices.Sort(got)
1887 | 				if !reflect.DeepEqual(got, want) {
1888 | 					t.Fatalf("unexpected auth param, got %s, want %s", got, want)
1889 | 				}
1890 | 			}
1891 | 		})
1892 | 	}
1893 | }
1894 | 
1895 | func TestFailParametersUnmarshal(t *testing.T) {
1896 | 	ctx, err := testutils.ContextWithNewLogger()
1897 | 	if err != nil {
1898 | 		t.Fatalf("unexpected error: %s", err)
1899 | 	}
1900 | 	tcs := []struct {
1901 | 		name string
1902 | 		in   []map[string]any
1903 | 		err  string
1904 | 	}{
1905 | 		{
1906 | 			name: "common parameter missing name",
1907 | 			in: []map[string]any{
1908 | 				{
1909 | 					"type":        "string",
1910 | 					"description": "this is a param for string",
1911 | 				},
1912 | 			},
1913 | 			err: "unable to parse as \"string\": Key: 'CommonParameter.Name' Error:Field validation for 'Name' failed on the 'required' tag",
1914 | 		},
1915 | 		{
1916 | 			name: "common parameter missing type",
1917 | 			in: []map[string]any{
1918 | 				{
1919 | 					"name":        "string",
1920 | 					"description": "this is a param for string",
1921 | 				},
1922 | 			},
1923 | 			err: "parameter is missing 'type' field",
1924 | 		},
1925 | 		{
1926 | 			name: "common parameter missing description",
1927 | 			in: []map[string]any{
1928 | 				{
1929 | 					"name": "my_string",
1930 | 					"type": "string",
1931 | 				},
1932 | 			},
1933 | 			err: "unable to parse as \"string\": Key: 'CommonParameter.Desc' Error:Field validation for 'Desc' failed on the 'required' tag",
1934 | 		},
1935 | 		{
1936 | 			name: "array parameter missing items",
1937 | 			in: []map[string]any{
1938 | 				{
1939 | 					"name":        "my_array",
1940 | 					"type":        "array",
1941 | 					"description": "this param is an array of strings",
1942 | 				},
1943 | 			},
1944 | 			err: "unable to parse as \"array\": unable to parse 'items' field: error parsing parameters: nothing to unmarshal",
1945 | 		},
1946 | 		{
1947 | 			name: "array parameter missing items' name",
1948 | 			in: []map[string]any{
1949 | 				{
1950 | 					"name":        "my_array",
1951 | 					"type":        "array",
1952 | 					"description": "this param is an array of strings",
1953 | 					"items": map[string]string{
1954 | 						"type":        "string",
1955 | 						"description": "string item",
1956 | 					},
1957 | 				},
1958 | 			},
1959 | 			err: "unable to parse as \"array\": unable to parse 'items' field: unable to parse as \"string\": Key: 'CommonParameter.Name' Error:Field validation for 'Name' failed on the 'required' tag",
1960 | 		},
1961 | 		// --- MODIFIED MAP PARAMETER TEST ---
1962 | 		{
1963 | 			name: "map with invalid valueType",
1964 | 			in: []map[string]any{
1965 | 				{
1966 | 					"name":        "my_map",
1967 | 					"type":        "map",
1968 | 					"description": "this param is a map",
1969 | 					"valueType":   "not-a-real-type",
1970 | 				},
1971 | 			},
1972 | 			err: "unsupported valueType \"not-a-real-type\" for map parameter",
1973 | 		},
1974 | 	}
1975 | 	for _, tc := range tcs {
1976 | 		t.Run(tc.name, func(t *testing.T) {
1977 | 			var got tools.Parameters
1978 | 			// parse map to bytes
1979 | 			data, err := yaml.Marshal(tc.in)
1980 | 			if err != nil {
1981 | 				t.Fatalf("unable to marshal input to yaml: %s", err)
1982 | 			}
1983 | 			// parse bytes to object
1984 | 			err = yaml.UnmarshalContext(ctx, data, &got)
1985 | 			if err == nil {
1986 | 				t.Fatalf("expect parsing to fail")
1987 | 			}
1988 | 			errStr := err.Error()
1989 | 
1990 | 			if !strings.Contains(errStr, tc.err) {
1991 | 				t.Fatalf("unexpected error: got %q, want to contain %q", errStr, tc.err)
1992 | 			}
1993 | 		})
1994 | 	}
1995 | }
1996 | 
1997 | // ... (Remaining test functions do not involve parameter definitions and need no changes)
1998 | 
1999 | func TestConvertArrayParamToString(t *testing.T) {
2000 | 
2001 | 	tcs := []struct {
2002 | 		name string
2003 | 		in   []any
2004 | 		want string
2005 | 	}{
2006 | 		{
2007 | 			in: []any{
2008 | 				"id",
2009 | 				"name",
2010 | 				"location",
2011 | 			},
2012 | 			want: "id, name, location",
2013 | 		},
2014 | 		{
2015 | 			in: []any{
2016 | 				"id",
2017 | 			},
2018 | 			want: "id",
2019 | 		},
2020 | 		{
2021 | 			in: []any{
2022 | 				"id",
2023 | 				"5",
2024 | 				"false",
2025 | 			},
2026 | 			want: "id, 5, false",
2027 | 		},
2028 | 		{
2029 | 			in:   []any{},
2030 | 			want: "",
2031 | 		},
2032 | 		{
2033 | 			in:   []any{},
2034 | 			want: "",
2035 | 		},
2036 | 	}
2037 | 	for _, tc := range tcs {
2038 | 		t.Run(tc.name, func(t *testing.T) {
2039 | 			got, _ := tools.ConvertArrayParamToString(tc.in)
2040 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
2041 | 				t.Fatalf("incorrect array param conversion: diff %v", diff)
2042 | 			}
2043 | 		})
2044 | 	}
2045 | }
2046 | 
2047 | func TestFailConvertArrayParamToString(t *testing.T) {
2048 | 	tcs := []struct {
2049 | 		name string
2050 | 		in   []any
2051 | 		err  string
2052 | 	}{
2053 | 		{
2054 | 			in:  []any{5, 10, 15},
2055 | 			err: "templateParameter only supports string arrays",
2056 | 		},
2057 | 		{
2058 | 			in:  []any{"id", "name", 15},
2059 | 			err: "templateParameter only supports string arrays",
2060 | 		},
2061 | 		{
2062 | 			in:  []any{false},
2063 | 			err: "templateParameter only supports string arrays",
2064 | 		},
2065 | 		{
2066 | 			in:  []any{10, true},
2067 | 			err: "templateParameter only supports string arrays",
2068 | 		},
2069 | 	}
2070 | 	for _, tc := range tcs {
2071 | 		t.Run(tc.name, func(t *testing.T) {
2072 | 			_, err := tools.ConvertArrayParamToString(tc.in)
2073 | 			errStr := err.Error()
2074 | 			if errStr != tc.err {
2075 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
2076 | 			}
2077 | 		})
2078 | 	}
2079 | }
2080 | 
2081 | func TestGetParams(t *testing.T) {
2082 | 	tcs := []struct {
2083 | 		name   string
2084 | 		in     map[string]any
2085 | 		params tools.Parameters
2086 | 		want   tools.ParamValues
2087 | 	}{
2088 | 		{
2089 | 			name: "parameters to include and exclude",
2090 | 			params: tools.Parameters{
2091 | 				tools.NewStringParameter("my_string_inc", "this should be included"),
2092 | 				tools.NewStringParameter("my_string_inc2", "this should be included"),
2093 | 			},
2094 | 			in: map[string]any{
2095 | 				"my_string_inc":  "hello world A",
2096 | 				"my_string_inc2": "hello world B",
2097 | 				"my_string_exc":  "hello world C",
2098 | 			},
2099 | 			want: tools.ParamValues{
2100 | 				tools.ParamValue{Name: "my_string_inc", Value: "hello world A"},
2101 | 				tools.ParamValue{Name: "my_string_inc2", Value: "hello world B"},
2102 | 			},
2103 | 		},
2104 | 		{
2105 | 			name: "include all",
2106 | 			params: tools.Parameters{
2107 | 				tools.NewStringParameter("my_string_inc", "this should be included"),
2108 | 			},
2109 | 			in: map[string]any{
2110 | 				"my_string_inc": "hello world A",
2111 | 			},
2112 | 			want: tools.ParamValues{
2113 | 				tools.ParamValue{Name: "my_string_inc", Value: "hello world A"},
2114 | 			},
2115 | 		},
2116 | 		{
2117 | 			name:   "exclude all",
2118 | 			params: tools.Parameters{},
2119 | 			in: map[string]any{
2120 | 				"my_string_exc":  "hello world A",
2121 | 				"my_string_exc2": "hello world B",
2122 | 			},
2123 | 			want: tools.ParamValues{},
2124 | 		},
2125 | 		{
2126 | 			name:   "empty",
2127 | 			params: tools.Parameters{},
2128 | 			in:     map[string]any{},
2129 | 			want:   tools.ParamValues{},
2130 | 		},
2131 | 	}
2132 | 	for _, tc := range tcs {
2133 | 		t.Run(tc.name, func(t *testing.T) {
2134 | 			got, _ := tools.GetParams(tc.params, tc.in)
2135 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
2136 | 				t.Fatalf("incorrect get params: diff %v", diff)
2137 | 			}
2138 | 		})
2139 | 	}
2140 | }
2141 | 
2142 | func TestFailGetParams(t *testing.T) {
2143 | 
2144 | 	tcs := []struct {
2145 | 		name   string
2146 | 		params tools.Parameters
2147 | 		in     map[string]any
2148 | 		err    string
2149 | 	}{
2150 | 		{
2151 | 			name:   "missing the only parameter",
2152 | 			params: tools.Parameters{tools.NewStringParameter("my_string", "this was missing")},
2153 | 			in:     map[string]any{},
2154 | 			err:    "missing parameter my_string",
2155 | 		},
2156 | 		{
2157 | 			name: "missing one parameter of multiple",
2158 | 			params: tools.Parameters{
2159 | 				tools.NewStringParameter("my_string_inc", "this should be included"),
2160 | 				tools.NewStringParameter("my_string_exc", "this was missing"),
2161 | 			},
2162 | 			in: map[string]any{
2163 | 				"my_string_inc": "hello world A",
2164 | 			},
2165 | 			err: "missing parameter my_string_exc",
2166 | 		},
2167 | 	}
2168 | 	for _, tc := range tcs {
2169 | 		t.Run(tc.name, func(t *testing.T) {
2170 | 			_, err := tools.GetParams(tc.params, tc.in)
2171 | 			errStr := err.Error()
2172 | 			if errStr != tc.err {
2173 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
2174 | 			}
2175 | 		})
2176 | 	}
2177 | }
2178 | 
2179 | func TestResolveTemplateParameters(t *testing.T) {
2180 | 	tcs := []struct {
2181 | 		name           string
2182 | 		templateParams tools.Parameters
2183 | 		statement      string
2184 | 		in             map[string]any
2185 | 		want           string
2186 | 	}{
2187 | 		{
2188 | 			name: "single template parameter",
2189 | 			templateParams: tools.Parameters{
2190 | 				tools.NewStringParameter("tableName", "this is a string template parameter"),
2191 | 			},
2192 | 			statement: "SELECT * FROM {{.tableName}}",
2193 | 			in: map[string]any{
2194 | 				"tableName": "hotels",
2195 | 			},
2196 | 			want: "SELECT * FROM hotels",
2197 | 		},
2198 | 		{
2199 | 			name: "multiple template parameters",
2200 | 			templateParams: tools.Parameters{
2201 | 				tools.NewStringParameter("tableName", "this is a string template parameter"),
2202 | 				tools.NewStringParameter("columnName", "this is a string template parameter"),
2203 | 			},
2204 | 			statement: "SELECT * FROM {{.tableName}} WHERE {{.columnName}} = 'Hilton'",
2205 | 			in: map[string]any{
2206 | 				"tableName":  "hotels",
2207 | 				"columnName": "name",
2208 | 			},
2209 | 			want: "SELECT * FROM hotels WHERE name = 'Hilton'",
2210 | 		},
2211 | 		{
2212 | 			name: "standard and template parameter",
2213 | 			templateParams: tools.Parameters{
2214 | 				tools.NewStringParameter("tableName", "this is a string template parameter"),
2215 | 				tools.NewStringParameter("hotelName", "this is a string parameter"),
2216 | 			},
2217 | 			statement: "SELECT * FROM {{.tableName}} WHERE name = $1",
2218 | 			in: map[string]any{
2219 | 				"tableName": "hotels",
2220 | 				"hotelName": "name",
2221 | 			},
2222 | 			want: "SELECT * FROM hotels WHERE name = $1",
2223 | 		},
2224 | 		{
2225 | 			name: "standard parameter",
2226 | 			templateParams: tools.Parameters{
2227 | 				tools.NewStringParameter("hotelName", "this is a string parameter"),
2228 | 			},
2229 | 			statement: "SELECT * FROM hotels WHERE name = $1",
2230 | 			in: map[string]any{
2231 | 				"hotelName": "hotels",
2232 | 			},
2233 | 			want: "SELECT * FROM hotels WHERE name = $1",
2234 | 		},
2235 | 	}
2236 | 	for _, tc := range tcs {
2237 | 		t.Run(tc.name, func(t *testing.T) {
2238 | 			got, _ := tools.ResolveTemplateParams(tc.templateParams, tc.statement, tc.in)
2239 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
2240 | 				t.Fatalf("incorrect resolved template params: diff %v", diff)
2241 | 			}
2242 | 		})
2243 | 	}
2244 | }
2245 | 
2246 | func TestFailResolveTemplateParameters(t *testing.T) {
2247 | 	tcs := []struct {
2248 | 		name           string
2249 | 		templateParams tools.Parameters
2250 | 		statement      string
2251 | 		in             map[string]any
2252 | 		err            string
2253 | 	}{
2254 | 		{
2255 | 			name: "wrong param name",
2256 | 			templateParams: tools.Parameters{
2257 | 				tools.NewStringParameter("tableName", "this is a string template parameter"),
2258 | 			},
2259 | 			statement: "SELECT * FROM {{.missingParam}}",
2260 | 			in:        map[string]any{},
2261 | 			err:       "error getting template params missing parameter tableName",
2262 | 		},
2263 | 		{
2264 | 			name: "incomplete param template",
2265 | 			templateParams: tools.Parameters{
2266 | 				tools.NewStringParameter("tableName", "this is a string template parameter"),
2267 | 			},
2268 | 			statement: "SELECT * FROM {{.tableName",
2269 | 			in: map[string]any{
2270 | 				"tableName": "hotels",
2271 | 			},
2272 | 			err: "error creating go template template: statement:1: unclosed action",
2273 | 		},
2274 | 		{
2275 | 			name: "undefined function",
2276 | 			templateParams: tools.Parameters{
2277 | 				tools.NewStringParameter("tableName", "this is a string template parameter"),
2278 | 			},
2279 | 			statement: "SELECT * FROM {{json .tableName}}",
2280 | 			in: map[string]any{
2281 | 				"tableName": "hotels",
2282 | 			},
2283 | 			err: "error creating go template template: statement:1: function \"json\" not defined",
2284 | 		},
2285 | 		{
2286 | 			name: "undefined method",
2287 | 			templateParams: tools.Parameters{
2288 | 				tools.NewStringParameter("tableName", "this is a string template parameter"),
2289 | 			},
2290 | 			statement: "SELECT * FROM {{.tableName .wrong}}",
2291 | 			in: map[string]any{
2292 | 				"tableName": "hotels",
2293 | 			},
2294 | 			err: "error executing go template template: statement:1:16: executing \"statement\" at <.tableName>: tableName is not a method but has arguments",
2295 | 		},
2296 | 	}
2297 | 	for _, tc := range tcs {
2298 | 		t.Run(tc.name, func(t *testing.T) {
2299 | 			_, err := tools.ResolveTemplateParams(tc.templateParams, tc.statement, tc.in)
2300 | 			errStr := err.Error()
2301 | 			if errStr != tc.err {
2302 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
2303 | 			}
2304 | 		})
2305 | 	}
2306 | }
2307 | 
2308 | func TestCheckParamRequired(t *testing.T) {
2309 | 	tcs := []struct {
2310 | 		name     string
2311 | 		required bool
2312 | 		defaultV any
2313 | 		want     bool
2314 | 	}{
2315 | 		{
2316 | 			name:     "required and no default",
2317 | 			required: true,
2318 | 			defaultV: nil,
2319 | 			want:     true,
2320 | 		},
2321 | 		{
2322 | 			name:     "required and default",
2323 | 			required: true,
2324 | 			defaultV: "foo",
2325 | 			want:     false,
2326 | 		},
2327 | 		{
2328 | 			name:     "not required and no default",
2329 | 			required: false,
2330 | 			defaultV: nil,
2331 | 			want:     false,
2332 | 		},
2333 | 		{
2334 | 			name:     "not required and default",
2335 | 			required: false,
2336 | 			defaultV: "foo",
2337 | 			want:     false,
2338 | 		},
2339 | 	}
2340 | 	for _, tc := range tcs {
2341 | 		t.Run(tc.name, func(t *testing.T) {
2342 | 			got := tools.CheckParamRequired(tc.required, tc.defaultV)
2343 | 			if got != tc.want {
2344 | 				t.Fatalf("got %v, want %v", got, tc.want)
2345 | 			}
2346 | 		})
2347 | 	}
2348 | }
2349 | 
```
Page 55/59FirstPrevNextLast