This is page 43 of 44. Use http://codebase.md/googleapis/genai-toolbox?lines=false&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
--------------------------------------------------------------------------------
/tests/cloudhealthcare/cloud_healthcare_integration_test.go:
--------------------------------------------------------------------------------
```go
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// To run these tests, set the following environment variables:
// HEALTHCARE_PROJECT: Google Cloud project ID for healthcare resources.
// HEALTHCARE_REGION: Google Cloud region for healthcare resources.
package cloudhealthcare
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"regexp"
"strings"
"testing"
"time"
"github.com/google/uuid"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/testutils"
"github.com/googleapis/genai-toolbox/tests"
"golang.org/x/oauth2/google"
"google.golang.org/api/healthcare/v1"
"google.golang.org/api/option"
)
var (
healthcareSourceKind = "cloud-healthcare"
getDatasetToolKind = "cloud-healthcare-get-dataset"
listFHIRStoresToolKind = "cloud-healthcare-list-fhir-stores"
listDICOMStoresToolKind = "cloud-healthcare-list-dicom-stores"
getFHIRStoreToolKind = "cloud-healthcare-get-fhir-store"
getFHIRStoreMetricsToolKind = "cloud-healthcare-get-fhir-store-metrics"
getFHIRResourceToolKind = "cloud-healthcare-get-fhir-resource"
fhirPatientSearchToolKind = "cloud-healthcare-fhir-patient-search"
fhirPatientEverythingToolKind = "cloud-healthcare-fhir-patient-everything"
fhirFetchPageToolKind = "cloud-healthcare-fhir-fetch-page"
getDICOMStoreToolKind = "cloud-healthcare-get-dicom-store"
getDICOMStoreMetricsToolKind = "cloud-healthcare-get-dicom-store-metrics"
searchDICOMStudiesToolKind = "cloud-healthcare-search-dicom-studies"
searchDICOMSeriesToolKind = "cloud-healthcare-search-dicom-series"
searchDICOMInstancesToolKind = "cloud-healthcare-search-dicom-instances"
retrieveRenderedDICOMInstanceToolKind = "cloud-healthcare-retrieve-rendered-dicom-instance"
healthcareProject = os.Getenv("HEALTHCARE_PROJECT")
healthcareRegion = os.Getenv("HEALTHCARE_REGION")
healthcareDataset = os.Getenv("HEALTHCARE_DATASET")
healthcarePrepopulatedDICOMStore = os.Getenv("HEALTHCARE_PREPOPULATED_DICOM_STORE")
)
type DICOMInstance struct {
study, series, instance string
}
var (
singleFrameDICOMInstance = DICOMInstance{
study: "1.2.840.113619.2.176.3596.3364818.7819.1259708454.105",
series: "1.2.840.113619.2.176.3596.3364818.7819.1259708454.108",
instance: "1.2.840.113619.2.176.3596.3364818.7271.1259708501.876",
}
multiFrameDICOMInstance = DICOMInstance{
study: "1.2.826.0.1.3680043.9.5704.649259287",
series: "1.2.826.0.1.3680043.9.5704.983743739",
instance: "1.2.826.0.1.3680043.9.5704.983743739.2",
}
)
func getHealthcareVars(t *testing.T) map[string]any {
switch "" {
case healthcareProject:
t.Fatal("'HEALTHCARE_PROJECT' not set")
case healthcareRegion:
t.Fatal("'HEALTHCARE_REGION' not set")
case healthcareDataset:
t.Fatal("'HEALTHCARE_DATASET' not set")
case healthcarePrepopulatedDICOMStore:
t.Fatal("'HEALTHCARE_PREPOPULATED_DICOM_STORE' not set")
}
return map[string]any{
"kind": healthcareSourceKind,
"project": healthcareProject,
"region": healthcareRegion,
"dataset": healthcareDataset,
}
}
func TestHealthcareToolEndpoints(t *testing.T) {
sourceConfig := getHealthcareVars(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
healthcareService, err := newHealthcareService(ctx)
if err != nil {
t.Fatalf("failed to create healthcare service: %v", err)
}
fhirStoreID := "fhir-store-" + uuid.New().String()
dicomStoreID := "dicom-store-" + uuid.New().String()
patient1ID, patient2ID, teardown := setupHealthcareResources(t, healthcareService, healthcareDataset, fhirStoreID, dicomStoreID)
defer teardown(t)
toolsFile := getToolsConfig(sourceConfig)
toolsFile = addClientAuthSourceConfig(t, toolsFile)
var args []string
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
t.Fatalf("command initialization returned an error: %s", err)
}
defer cleanup()
waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
if err != nil {
t.Logf("toolbox command logs: %s", out)
t.Fatalf("toolbox didn't start successfully: %s", err)
}
datasetWant := fmt.Sprintf(`"name":"projects/%s/locations/%s/datasets/%s"`, healthcareProject, healthcareRegion, healthcareDataset)
fhirStoreWant := fmt.Sprintf(`"name":"projects/%s/locations/%s/datasets/%s/fhirStores/%s"`, healthcareProject, healthcareRegion, healthcareDataset, fhirStoreID)
dicomStoreWant := fmt.Sprintf(`"name":"projects/%s/locations/%s/datasets/%s/dicomStores/%s"`, healthcareProject, healthcareRegion, healthcareDataset, dicomStoreID)
runGetDatasetToolInvokeTest(t, datasetWant)
runListFHIRStoresToolInvokeTest(t, fhirStoreWant)
runListDICOMStoresToolInvokeTest(t, dicomStoreWant)
runGetFHIRStoreToolInvokeTest(t, fhirStoreID, fhirStoreWant)
runGetFHIRStoreMetricsToolInvokeTest(t, fhirStoreID, `"metrics"`)
runGetFHIRResourceToolInvokeTest(t, fhirStoreID, "Patient", patient1ID, `"id":"`+patient1ID+`"`)
runFHIRPatientSearchToolInvokeTest(t, fhirStoreID, patient1ID, patient2ID)
runFHIRPatientEverythingToolInvokeTest(t, fhirStoreID, patient1ID, `"resourceType":"Bundle"`)
nextURL := getNextPageURLForPatientEverything(t, fhirStoreID, patient2ID)
runFHIRFetchPageToolInvokeTest(t, nextURL, `"total":1`)
runGetDICOMStoreToolInvokeTest(t, dicomStoreID, dicomStoreWant)
runGetDICOMStoreMetricsToolInvokeTest(t, healthcarePrepopulatedDICOMStore, `"structuredStorageSizeBytes"`)
runSearchDICOMStudiesToolInvokeTest(t, healthcarePrepopulatedDICOMStore)
runSearchDICOMSeriesToolInvokeTest(t, healthcarePrepopulatedDICOMStore)
runSearchDICOMInstancesToolInvokeTest(t, healthcarePrepopulatedDICOMStore)
runRetrieveRenderedDICOMInstanceToolInvokeTest(t, healthcarePrepopulatedDICOMStore)
}
func TestHealthcareToolWithStoreRestriction(t *testing.T) {
sourceConfig := getHealthcareVars(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
healthcareService, err := newHealthcareService(ctx)
if err != nil {
t.Fatalf("failed to create healthcare service: %v", err)
}
// Create stores
allowedFHIRStoreID := "fhir-store-allowed-" + uuid.New().String()
allowedDICOMStoreID := "dicom-store-allowed-" + uuid.New().String()
disallowedFHIRStoreID := "fhir-store-disallowed-" + uuid.New().String()
disallowedDICOMStoreID := "dicom-store-disallowed-" + uuid.New().String()
_, _, teardownAllowedStores := setupHealthcareResources(t, healthcareService, healthcareDataset, allowedFHIRStoreID, allowedDICOMStoreID)
defer teardownAllowedStores(t)
_, _, teardownDisallowedStores := setupHealthcareResources(t, healthcareService, healthcareDataset, disallowedFHIRStoreID, disallowedDICOMStoreID)
defer teardownDisallowedStores(t)
// Configure source with dataset restriction.
sourceConfig["allowedFhirStores"] = []string{allowedFHIRStoreID}
sourceConfig["allowedDicomStores"] = []string{allowedDICOMStoreID}
// Configure tool
toolsConfig := map[string]any{
"list-fhir-stores-restricted": map[string]any{
"kind": "cloud-healthcare-list-fhir-stores",
"source": "my-instance",
"description": "Tool to list fhir stores",
},
"list-dicom-stores-restricted": map[string]any{
"kind": "cloud-healthcare-list-dicom-stores",
"source": "my-instance",
"description": "Tool to list dicom stores",
},
}
// Create config file
config := map[string]any{
"sources": map[string]any{
"my-instance": sourceConfig,
},
"tools": toolsConfig,
}
// Start server
cmd, cleanup, err := tests.StartCmd(ctx, config)
if err != nil {
t.Fatalf("command initialization returned an error: %s", err)
}
defer cleanup()
waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
if err != nil {
t.Logf("toolbox command logs: \n%s", out)
t.Fatalf("toolbox didn't start successfully: %s", err)
}
// Run tests
runListFHIRStoresWithRestriction(t, allowedFHIRStoreID, disallowedFHIRStoreID)
runListDICOMStoresWithRestriction(t, allowedDICOMStoreID, disallowedDICOMStoreID)
}
func createFHIRResource(t *testing.T, service *healthcare.Service, fhirStoreName, resourceType string, resourceBody io.Reader) (string, string) {
resp, err := service.Projects.Locations.Datasets.FhirStores.Fhir.Create(fhirStoreName, resourceType, resourceBody).Do()
if err != nil {
t.Fatalf("failed to create FHIR resource: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
t.Fatalf("failed to create FHIR resource, status: %d, body: %s", resp.StatusCode, string(body))
}
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
t.Fatalf("failed to decode response: %v", err)
}
id := result["id"].(string)
return id, fmt.Sprintf("%s/%s", resourceType, id)
}
func newHealthcareService(ctx context.Context) (*healthcare.Service, error) {
creds, err := google.FindDefaultCredentials(ctx, healthcare.CloudHealthcareScope)
if err != nil {
return nil, fmt.Errorf("failed to find default credentials: %w", err)
}
healthcareService, err := healthcare.NewService(ctx, option.WithCredentials(creds))
if err != nil {
return nil, fmt.Errorf("failed to create healthcare service: %w", err)
}
return healthcareService, nil
}
func setupHealthcareResources(t *testing.T, service *healthcare.Service, datasetID, fhirStoreID, dicomStoreID string) (string, string, func(*testing.T)) {
datasetName := fmt.Sprintf("projects/%s/locations/%s/datasets/%s", healthcareProject, healthcareRegion, datasetID)
var err error
// Create FHIR store
fhirStore := &healthcare.FhirStore{Version: "R4"}
if fhirStore, err = service.Projects.Locations.Datasets.FhirStores.Create(datasetName, fhirStore).FhirStoreId(fhirStoreID).Do(); err != nil {
t.Fatalf("failed to create fhir store: %v", err)
}
// Create DICOM store
dicomStore := &healthcare.DicomStore{}
if dicomStore, err = service.Projects.Locations.Datasets.DicomStores.Create(datasetName, dicomStore).DicomStoreId(dicomStoreID).Do(); err != nil {
t.Fatalf("failed to create dicom store: %v", err)
}
// Create Patient 1
patient1Body := bytes.NewBuffer([]byte(`{
"resourceType":"Patient",
"name":[{"use":"official","family":"Smith","given":["John"]}],
"birthDate":"1980-01-01",
"gender":"male",
"address":[{"use":"home","line":["123 Main St"],"city":"san fransisco","state":"CA","postalCode":"12345","country":"USA"}],
"active":true,
"deceasedBoolean":false,
"telecom":[{"system":"phone","value":"555-1234","use":"home"},{"system":"email","value":"[email protected]","use":"work"}],
"gender":"male",
"identifier":[{"system":"http://hospital.org","value":"1234567"}],
"communication":[{"language":{"coding":[{"system":"urn:ietf:bcp:47","code":"en","display":"English"}]},"preferred":true}]
}`))
patient1ID, patient1Name := createFHIRResource(t, service, fhirStore.Name, "Patient", patient1Body)
// Create Observation for Patient 1
observation1Body := bytes.NewBuffer([]byte(fmt.Sprintf(`
{
"resourceType": "Observation",
"status": "final",
"code": { "coding": [{ "system": "http://loinc.org", "code": "29463-7", "display": "Body Weight" }] },
"subject": { "reference": "%s" },
"valueQuantity": { "value": 185, "unit": "lbs", "system": "http://unitsofmeasure.org", "code": "[lb_av]" }
}`, patient1Name)))
createFHIRResource(t, service, fhirStore.Name, "Observation", observation1Body)
// Create Patient 2
patient2Body := bytes.NewBuffer([]byte(`{"resourceType":"Patient","name":[{"use":"official","family":"Doe","given":["Jane"]}]}`))
patient2ID, patient2Name := createFHIRResource(t, service, fhirStore.Name, "Patient", patient2Body)
// Create 101 Observations for Patient 2
for i := 0; i < 101; i++ {
observation2Body := bytes.NewBuffer([]byte(fmt.Sprintf(`
{
"resourceType": "Observation",
"status": "final",
"code": { "coding": [{ "system": "http://loinc.org", "code": "8302-2", "display": "Body Height" }] },
"subject": { "reference": "%s" },
"valueQuantity": { "value": 68, "unit": "in", "system": "http://unitsofmeasure.org", "code": "[in_i]" }
}`, patient2Name)))
createFHIRResource(t, service, fhirStore.Name, "Observation", observation2Body)
}
teardown := func(t *testing.T) {
if _, err := service.Projects.Locations.Datasets.FhirStores.Delete(fhirStore.Name).Do(); err != nil {
t.Logf("failed to delete fhir store: %v", err)
}
if _, err := service.Projects.Locations.Datasets.DicomStores.Delete(dicomStore.Name).Do(); err != nil {
t.Logf("failed to delete dicom store: %v", err)
}
}
return patient1ID, patient2ID, teardown
}
func getToolsConfig(sourceConfig map[string]any) map[string]any {
config := map[string]any{
"sources": map[string]any{
"my-instance": sourceConfig,
},
"tools": map[string]any{
"my-get-dataset-tool": map[string]any{
"kind": getDatasetToolKind,
"source": "my-instance",
"description": "Tool to get a healthcare dataset",
},
"my-list-fhir-stores-tool": map[string]any{
"kind": listFHIRStoresToolKind,
"source": "my-instance",
"description": "Tool to list FHIR stores",
},
"my-list-dicom-stores-tool": map[string]any{
"kind": listDICOMStoresToolKind,
"source": "my-instance",
"description": "Tool to list DICOM stores",
},
"my-get-fhir-store-tool": map[string]any{
"kind": getFHIRStoreToolKind,
"source": "my-instance",
"description": "Tool to get a FHIR store",
},
"my-get-fhir-store-metrics-tool": map[string]any{
"kind": getFHIRStoreMetricsToolKind,
"source": "my-instance",
"description": "Tool to get FHIR store metrics",
},
"my-get-fhir-resource-tool": map[string]any{
"kind": getFHIRResourceToolKind,
"source": "my-instance",
"description": "Tool to get FHIR resource",
},
"my-fhir-patient-search-tool": map[string]any{
"kind": fhirPatientSearchToolKind,
"source": "my-instance",
"description": "Tool to search for patients",
},
"my-fhir-patient-everything-tool": map[string]any{
"kind": fhirPatientEverythingToolKind,
"source": "my-instance",
"description": "Tool for patient everything",
},
"my-fhir-fetch-page-tool": map[string]any{
"kind": fhirFetchPageToolKind,
"source": "my-instance",
"description": "Tool to fetch a page of FHIR resources",
},
"my-get-dicom-store-tool": map[string]any{
"kind": getDICOMStoreToolKind,
"source": "my-instance",
"description": "Tool to get a DICOM store",
},
"my-get-dicom-store-metrics-tool": map[string]any{
"kind": getDICOMStoreMetricsToolKind,
"source": "my-instance",
"description": "Tool to get DICOM store metrics",
},
"my-search-dicom-studies-tool": map[string]any{
"kind": searchDICOMStudiesToolKind,
"source": "my-instance",
"description": "Tool to search DICOM studies",
},
"my-search-dicom-series-tool": map[string]any{
"kind": searchDICOMSeriesToolKind,
"source": "my-instance",
"description": "Tool to search DICOM series",
},
"my-search-dicom-instances-tool": map[string]any{
"kind": searchDICOMInstancesToolKind,
"source": "my-instance",
"description": "Tool to search DICOM instances",
},
"my-retrieve-rendered-dicom-instance-tool": map[string]any{
"kind": retrieveRenderedDICOMInstanceToolKind,
"source": "my-instance",
"description": "Tool to retrieve rendered DICOM instance",
},
"my-client-auth-get-dataset-tool": map[string]any{
"kind": getDatasetToolKind,
"source": "my-client-auth-source",
"description": "Tool to get a healthcare dataset",
},
"my-client-auth-list-fhir-stores-tool": map[string]any{
"kind": listFHIRStoresToolKind,
"source": "my-client-auth-source",
"description": "Tool to list FHIR stores",
},
"my-client-auth-list-dicom-stores-tool": map[string]any{
"kind": listDICOMStoresToolKind,
"source": "my-client-auth-source",
"description": "Tool to list DICOM stores",
},
"my-client-auth-get-fhir-store-tool": map[string]any{
"kind": getFHIRStoreToolKind,
"source": "my-client-auth-source",
"description": "Tool to get a FHIR store",
},
"my-client-auth-get-fhir-store-metrics-tool": map[string]any{
"kind": getFHIRStoreMetricsToolKind,
"source": "my-client-auth-source",
"description": "Tool to get FHIR store metrics",
},
"my-client-auth-get-fhir-resource-tool": map[string]any{
"kind": getFHIRResourceToolKind,
"source": "my-client-auth-source",
"description": "Tool to get FHIR resource",
},
"my-client-auth-fhir-patient-search-tool": map[string]any{
"kind": fhirPatientSearchToolKind,
"source": "my-client-auth-source",
"description": "Tool to search for patients",
},
"my-client-auth-fhir-patient-everything-tool": map[string]any{
"kind": fhirPatientEverythingToolKind,
"source": "my-client-auth-source",
"description": "Tool for patient everything",
},
"my-client-auth-fhir-fetch-page-tool": map[string]any{
"kind": fhirFetchPageToolKind,
"source": "my-client-auth-source",
"description": "Tool to fetch a page of FHIR resources",
},
"my-client-auth-get-dicom-store-tool": map[string]any{
"kind": getDICOMStoreToolKind,
"source": "my-client-auth-source",
"description": "Tool to get a DICOM store",
},
"my-client-auth-get-dicom-store-metrics-tool": map[string]any{
"kind": getDICOMStoreMetricsToolKind,
"source": "my-client-auth-source",
"description": "Tool to get DICOM store metrics",
},
"my-client-auth-search-dicom-studies-tool": map[string]any{
"kind": searchDICOMStudiesToolKind,
"source": "my-client-auth-source",
"description": "Tool to search DICOM studies",
},
"my-client-auth-search-dicom-series-tool": map[string]any{
"kind": searchDICOMSeriesToolKind,
"source": "my-client-auth-source",
"description": "Tool to search DICOM series",
},
"my-client-auth-search-dicom-instances-tool": map[string]any{
"kind": searchDICOMInstancesToolKind,
"source": "my-client-auth-source",
"description": "Tool to search DICOM instances",
},
"my-client-auth-retrieve-rendered-dicom-instance-tool": map[string]any{
"kind": retrieveRenderedDICOMInstanceToolKind,
"source": "my-client-auth-source",
"description": "Tool to retrieve rendered DICOM instance",
},
"my-auth-get-dataset-tool": map[string]any{
"kind": getDatasetToolKind,
"source": "my-instance",
"description": "Tool to get a healthcare dataset with auth",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-list-fhir-stores-tool": map[string]any{
"kind": listFHIRStoresToolKind,
"source": "my-instance",
"description": "Tool to list FHIR stores with auth",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-list-dicom-stores-tool": map[string]any{
"kind": listDICOMStoresToolKind,
"source": "my-instance",
"description": "Tool to list DICOM stores with auth",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-get-fhir-store-tool": map[string]any{
"kind": getFHIRStoreToolKind,
"source": "my-instance",
"description": "Tool to get a FHIR store",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-get-fhir-store-metrics-tool": map[string]any{
"kind": getFHIRStoreMetricsToolKind,
"source": "my-instance",
"description": "Tool to get FHIR store metrics",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-get-fhir-resource-tool": map[string]any{
"kind": getFHIRResourceToolKind,
"source": "my-instance",
"description": "Tool to get FHIR resource",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-fhir-patient-search-tool": map[string]any{
"kind": fhirPatientSearchToolKind,
"source": "my-instance",
"description": "Tool to search for patients",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-fhir-patient-everything-tool": map[string]any{
"kind": fhirPatientEverythingToolKind,
"source": "my-instance",
"description": "Tool for patient everything",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-fhir-fetch-page-tool": map[string]any{
"kind": fhirFetchPageToolKind,
"source": "my-instance",
"description": "Tool to fetch a page of FHIR resources",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-get-dicom-store-tool": map[string]any{
"kind": getDICOMStoreToolKind,
"source": "my-instance",
"description": "Tool to get a DICOM store",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-get-dicom-store-metrics-tool": map[string]any{
"kind": getDICOMStoreMetricsToolKind,
"source": "my-instance",
"description": "Tool to get DICOM store metrics",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-search-dicom-studies-tool": map[string]any{
"kind": searchDICOMStudiesToolKind,
"source": "my-instance",
"description": "Tool to search DICOM studies",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-search-dicom-series-tool": map[string]any{
"kind": searchDICOMSeriesToolKind,
"source": "my-instance",
"description": "Tool to search DICOM series",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-search-dicom-instances-tool": map[string]any{
"kind": searchDICOMInstancesToolKind,
"source": "my-instance",
"description": "Tool to search DICOM instances",
"authRequired": []string{
"my-google-auth",
},
},
"my-auth-retrieve-rendered-dicom-instance-tool": map[string]any{
"kind": retrieveRenderedDICOMInstanceToolKind,
"source": "my-instance",
"description": "Tool to retrieve rendered DICOM instance",
"authRequired": []string{
"my-google-auth",
},
},
},
"authServices": map[string]any{
"my-google-auth": map[string]any{
"kind": "google",
"clientId": tests.ClientId,
},
},
}
return config
}
func addClientAuthSourceConfig(t *testing.T, config map[string]any) map[string]any {
sources, ok := config["sources"].(map[string]any)
if !ok {
t.Fatalf("unable to get sources from config")
}
sources["my-client-auth-source"] = map[string]any{
"kind": healthcareSourceKind,
"project": healthcareProject,
"region": healthcareRegion,
"dataset": healthcareDataset,
"useClientOAuth": true,
}
config["sources"] = sources
return config
}
func runGetDatasetToolInvokeTest(t *testing.T, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-get-dataset-tool",
api: "http://127.0.0.1:5000/api/tool/my-get-dataset-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dataset-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dataset-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dataset-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-get-dataset-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dataset-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dataset-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-auth-get-dataset-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dataset-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-dataset-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dataset-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-get-dataset-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dataset-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-dataset-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dataset-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runListFHIRStoresToolInvokeTest(t *testing.T, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-list-fhir-stores-tool",
api: "http://127.0.0.1:5000/api/tool/my-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-list-fhir-stores-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-list-fhir-stores-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-list-fhir-stores-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-auth-list-fhir-stores-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-client-auth-list-fhir-stores-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-list-fhir-stores-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-client-auth-list-fhir-stores-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-list-fhir-stores-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runListDICOMStoresToolInvokeTest(t *testing.T, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-list-dicom-stores-tool",
api: "http://127.0.0.1:5000/api/tool/my-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-list-dicom-stores-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-list-dicom-stores-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-list-dicom-stores-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-auth-list-dicom-stores-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-client-auth-list-dicom-stores-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-list-dicom-stores-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
{
name: "invoke my-client-auth-list-dicom-stores-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-list-dicom-stores-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runGetFHIRStoreToolInvokeTest(t *testing.T, fhirStoreID, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-get-fhir-store-tool",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-store-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-store-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-store-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-store-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-store-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-get-fhir-store-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-store-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-get-fhir-store-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-fhir-store-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-store-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-get-fhir-store-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-fhir-store-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-store-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runGetFHIRStoreMetricsToolInvokeTest(t *testing.T, fhirStoreID, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-get-fhir-store-metrics-tool",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-store-metrics-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-store-metrics-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-store-metrics-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-get-fhir-store-metrics-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-get-fhir-store-metrics-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-fhir-store-metrics-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-get-fhir-store-metrics-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-fhir-store-metrics-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-store-metrics-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runGetFHIRResourceToolInvokeTest(t *testing.T, storeID, resType, resID, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-get-fhir-resource-tool",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-resource-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-resource-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-fhir-resource-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-get-fhir-resource-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
isErr: true,
},
{
name: "invoke my-get-fhir-resource-tool with non-existent resource",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"foo"}`)),
isErr: true,
},
{
name: "invoke my-get-fhir-resource-tool with missing required id parameter",
api: "http://127.0.0.1:5000/api/tool/my-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-fhir-resource-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-get-fhir-resource-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-fhir-resource-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-fhir-resource-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + storeID + `", "resourceType":"` + resType + `", "resourceID":"` + resID + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runFHIRPatientSearchToolInvokeTest(t *testing.T, fhirStoreID string, patientIDs ...string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
want := `"total":` + fmt.Sprintf(`%d`, len(patientIDs))
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-fhir-patient-search-tool",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-patient-search-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-patient-search-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-patient-search-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-fhir-patient-search-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-fhir-patient-search-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-fhir-patient-search-tool wrong parameter type",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "name":true}`)),
isErr: true,
},
{
name: "invoke my-fhir-patient-search-tool filters",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "name":"john","gender":"male","state":"CA","active":"true","birthDateRange":"1970-01-01/2000-12-31"}`)),
want: patientIDs[0],
isErr: false,
},
{
name: "invoke my-fhir-patient-search-tool filters 2",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "givenName":"john","addressSubstring":"main st","email":"[email protected]","phone":"555-1234","language":"en","deceased":"false"}`)),
want: patientIDs[0],
isErr: false,
},
{
name: "invoke my-fhir-patient-search-tool filters 3",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "familyName":"smith","country":"USA","addressUse":"home","postalCode":"12345","identifier":"1234567"}`)),
want: patientIDs[0],
isErr: false,
},
{
name: "invoke my-fhir-patient-search-tool zero matches",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "gender":"unknown"}`)),
want: `"total":0`,
isErr: false,
},
{
name: "invoke my-fhir-patient-search-tool match second patient only",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `","familyName":"Doe"}`)),
want: patientIDs[1],
isErr: false,
},
{
name: "invoke my-client-auth-fhir-patient-search-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-fhir-patient-search-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-fhir-patient-search-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-patient-search-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runFHIRPatientEverythingToolInvokeTest(t *testing.T, fhirStoreID, patientID, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-fhir-patient-everything-tool",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-patient-everything-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-patient-everything-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-patient-everything-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-fhir-patient-everything-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
isErr: true,
},
{
name: "invoke my-fhir-patient-everything-tool with non-existent patient",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"foo"`)),
isErr: true,
},
{
name: "invoke my-fhir-patient-everything-tool with invalid since filter format",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `","sinceFilter":"October 10th, 2023"}`)),
isErr: true,
},
{
name: "invoke my-fhir-patient-everything-tool with type and since filters",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `","sinceFilter":"2023-01-01T00:00:00Z","resourceTypesFilter":["Observation"]}`)),
want: `"total":2`,
isErr: false,
},
{
name: "invoke my-fhir-patient-everything-tool with type and since keeps only patient",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `","sinceFilter":"` + time.Now().Format(time.RFC3339) + `","resourceTypesFilter":["Observation","Encounter"]}`)),
want: `"total":1`,
isErr: false,
},
{
name: "invoke my-fhir-patient-everything-tool with type keeps only patient",
api: "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `","resourceTypesFilter":["Encounter","Claim"]}`)),
want: `"total":1`,
isErr: false,
},
{
name: "invoke my-client-auth-fhir-patient-everything-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-fhir-patient-everything-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-fhir-patient-everything-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-patient-everything-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + fhirStoreID + `", "patientID":"` + patientID + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runFHIRFetchPageToolInvokeTest(t *testing.T, pageURL, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-fhir-fetch-page-tool",
api: "http://127.0.0.1:5000/api/tool/my-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-fetch-page-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-fetch-page-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-fhir-fetch-page-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-fhir-fetch-page-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
isErr: true,
},
{
name: "invoke my-fhir-fetch-page-tool with invalid url",
api: "http://127.0.0.1:5000/api/tool/my-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"google.com"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-fhir-fetch-page-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-fhir-fetch-page-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-fhir-fetch-page-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-fhir-fetch-page-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"pageURL":"` + pageURL + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func getNextPageURLForPatientEverything(t *testing.T, fhirStoreID, patientID string) string {
api := "http://127.0.0.1:5000/api/tool/my-fhir-patient-everything-tool/invoke"
reqBody := fmt.Sprintf(`{"storeID": "%s", "patientID": "%s"}`, fhirStoreID, patientID)
resp, bodyBytes := tests.RunRequest(t, http.MethodPost, api, bytes.NewBuffer([]byte(reqBody)), map[string]string{"Content-type": "application/json"})
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("response status code is not 200, got %d: %s", resp.StatusCode, string(bodyBytes))
}
var body map[string]interface{}
err := json.Unmarshal(bodyBytes, &body)
if err != nil {
t.Fatalf("error parsing response body")
}
resultStr, ok := body["result"].(string)
if !ok {
t.Fatalf("unable to find result in response body")
}
var resultJSON map[string]interface{}
if err := json.Unmarshal([]byte(resultStr), &resultJSON); err != nil {
t.Fatalf("failed to unmarshal result string: %v", err)
}
links, ok := resultJSON["link"].([]interface{})
if !ok {
t.Fatalf("no link field in result")
}
for _, l := range links {
link := l.(map[string]interface{})
if relation, ok := link["relation"].(string); ok && relation == "next" {
if url, ok := link["url"].(string); ok {
return url
}
}
}
t.Fatalf("next link not found in patient everything response")
return ""
}
func runTest(t *testing.T, api string, requestHeader map[string]string, requestBody io.Reader) (string, int) {
resp, bodyBytes := tests.RunRequest(t, http.MethodPost, api, requestBody, requestHeader)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", resp.StatusCode
}
var body map[string]interface{}
err := json.Unmarshal(bodyBytes, &body)
if err != nil {
t.Fatalf("error parsing response body")
}
got, ok := body["result"].(string)
if !ok {
t.Fatalf("unable to find result in response body")
}
return got, http.StatusOK
}
func runListFHIRStoresWithRestriction(t *testing.T, allowedFHIRStore, disallowedFHIRStore string) {
api := "http://127.0.0.1:5000/api/tool/list-fhir-stores-restricted/invoke"
got, status := runTest(t, api, map[string]string{"Content-type": "application/json"}, bytes.NewBuffer([]byte(`{}`)))
if status != http.StatusOK {
t.Fatalf("expected status OK but got %d", status)
}
if !strings.Contains(got, allowedFHIRStore) {
t.Fatalf("expected %q to contain %q, but it did not", got, allowedFHIRStore)
}
if strings.Contains(got, disallowedFHIRStore) {
t.Fatalf("expected %q to NOT contain %q, but it did", got, disallowedFHIRStore)
}
}
func runListDICOMStoresWithRestriction(t *testing.T, allowedDICOMStore, disallowedDICOMStore string) {
api := "http://127.0.0.1:5000/api/tool/list-dicom-stores-restricted/invoke"
got, status := runTest(t, api, map[string]string{"Content-type": "application/json"}, bytes.NewBuffer([]byte(`{}`)))
if status != http.StatusOK {
t.Fatalf("expected status OK but got %d", status)
}
if !strings.Contains(got, allowedDICOMStore) {
t.Fatalf("expected %q to contain %q, but it did not", got, allowedDICOMStore)
}
if strings.Contains(got, disallowedDICOMStore) {
t.Fatalf("expected %q to NOT contain %q, but it did", got, disallowedDICOMStore)
}
}
func runGetDICOMStoreToolInvokeTest(t *testing.T, dicomStoreID, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-get-dicom-store-tool",
api: "http://127.0.0.1:5000/api/tool/my-get-dicom-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dicom-store-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dicom-store-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dicom-store-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-get-dicom-store-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dicom-store-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dicom-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-get-dicom-store-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dicom-store-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-get-dicom-store-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-get-dicom-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-dicom-store-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dicom-store-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-get-dicom-store-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dicom-store-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-dicom-store-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dicom-store-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runGetDICOMStoreMetricsToolInvokeTest(t *testing.T, dicomStoreID, want string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-get-dicom-store-metrics-tool",
api: "http://127.0.0.1:5000/api/tool/my-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dicom-store-metrics-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dicom-store-metrics-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-auth-get-dicom-store-metrics-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-get-dicom-store-metrics-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-get-dicom-store-metrics-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-dicom-store-metrics-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: want,
isErr: false,
},
{
name: "invoke my-client-auth-get-dicom-store-metrics-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-get-dicom-store-metrics-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-get-dicom-store-metrics-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runSearchDICOMStudiesToolInvokeTest(t *testing.T, dicomStoreID string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-search-dicom-studies-tool",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: singleFrameDICOMInstance.study,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-studies-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: multiFrameDICOMInstance.study,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-studies-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: multiFrameDICOMInstance.study,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-studies-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-search-dicom-studies-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-search-dicom-studies-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-search-dicom-studies-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: singleFrameDICOMInstance.study,
isErr: false,
},
{
name: "invoke my-client-auth-search-dicom-studies-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-search-dicom-studies-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-search-dicom-studies-tool with patient name and fuzzy matching",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "PatientName":"Andrew", "fuzzymatching":true}`)),
want: multiFrameDICOMInstance.study,
isErr: false,
},
{
name: "invoke my-search-dicom-studies-tool with patient id filter",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-studies-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "PatientID":"Joelle-del"}`)),
want: singleFrameDICOMInstance.study,
isErr: false,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runSearchDICOMSeriesToolInvokeTest(t *testing.T, dicomStoreID string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-search-dicom-series-tool",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-series-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: singleFrameDICOMInstance.series,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-series-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-series-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: multiFrameDICOMInstance.series,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-series-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-series-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: multiFrameDICOMInstance.series,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-series-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-series-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-search-dicom-series-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-series-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-search-dicom-series-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-series-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-search-dicom-series-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-series-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: singleFrameDICOMInstance.series,
isErr: false,
},
{
name: "invoke my-client-auth-search-dicom-series-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-series-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-search-dicom-series-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-series-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-search-dicom-series-tool with study date and referring physician name filters",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-series-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyDate":"20170101-20171231", "ReferringPhysicianName":"Frederick^Bryant^^Ph.D."}`)),
want: multiFrameDICOMInstance.series,
isErr: false,
},
{
name: "invoke my-search-dicom-series-tool with series instance uid",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-series-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "SeriesInstanceUID":"1.2.840.113619.2.176.3596.3364818.7819.1259708454.108"}`)),
want: singleFrameDICOMInstance.series,
isErr: false,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runSearchDICOMInstancesToolInvokeTest(t *testing.T, dicomStoreID string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
want string
isErr bool
}{
{
name: "invoke my-search-dicom-instances-tool",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: singleFrameDICOMInstance.instance,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-instances-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: multiFrameDICOMInstance.instance,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-instances-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: multiFrameDICOMInstance.instance,
isErr: false,
},
{
name: "invoke my-auth-search-dicom-instances-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-search-dicom-instances-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-search-dicom-instances-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-search-dicom-instances-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
want: singleFrameDICOMInstance.instance,
isErr: false,
},
{
name: "invoke my-client-auth-search-dicom-instances-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-search-dicom-instances-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `"}`)),
isErr: true,
},
{
name: "invoke my-search-dicom-instances-tool with modality filter",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "modality":"SM"}`)),
want: multiFrameDICOMInstance.instance,
isErr: false,
},
{
name: "invoke my-search-dicom-instances-tool with include attribute",
api: "http://127.0.0.1:5000/api/tool/my-search-dicom-instances-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "includefield":["52009230"]}`)),
want: `"52009230"`,
isErr: false,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
got, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
} else if !strings.Contains(got, tc.want) {
t.Errorf("expected result to contain %q but got %q", tc.want, got)
}
})
}
}
func runRetrieveRenderedDICOMInstanceToolInvokeTest(t *testing.T, dicomStoreID string) {
idToken, err := tests.GetGoogleIdToken(tests.ClientId)
if err != nil {
t.Fatalf("error getting Google ID token: %s", err)
}
accessToken, err := sources.GetIAMAccessToken(t.Context())
if err != nil {
t.Fatalf("error getting access token from ADC: %s", err)
}
accessToken = "Bearer " + accessToken
invokeTcs := []struct {
name string
api string
requestHeader map[string]string
requestBody io.Reader
isErr bool
}{
{
name: "invoke my-retrieve-rendered-dicom-instance-tool",
api: "http://127.0.0.1:5000/api/tool/my-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: false,
},
{
name: "invoke my-auth-retrieve-rendered-dicom-instance-tool with auth",
api: "http://127.0.0.1:5000/api/tool/my-auth-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: false,
},
{
name: "invoke my-auth-retrieve-rendered-dicom-instance-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: false,
},
{
name: "invoke my-auth-retrieve-rendered-dicom-instance-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: true,
},
{
name: "invoke my-auth-retrieve-rendered-dicom-instance-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-auth-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{"Authorization": "invalid-token"},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: true,
},
{
name: "invoke my-retrieve-rendered-dicom-instance-tool with invalid storeID",
api: "http://127.0.0.1:5000/api/tool/my-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"invalid-store", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-retrieve-rendered-dicom-instance-tool with client auth",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{"Authorization": accessToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: false,
},
{
name: "invoke my-client-auth-retrieve-rendered-dicom-instance-tool without auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: true,
},
{
name: "invoke my-client-auth-retrieve-rendered-dicom-instance-tool with invalid auth token",
api: "http://127.0.0.1:5000/api/tool/my-client-auth-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{"my-google-auth_token": idToken},
requestBody: bytes.NewBuffer([]byte(`{"storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: true,
},
{
name: "invoke my-retrieve-rendered-dicom-instance-tool second frame on single-frame instance",
api: "http://127.0.0.1:5000/api/tool/my-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"FrameNumber": 2, "storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + singleFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + singleFrameDICOMInstance.series + `", "SOPInstanceUID":"` + singleFrameDICOMInstance.instance + `"}`)),
isErr: true,
},
{
name: "invoke my-retrieve-rendered-dicom-instance-tool second frame on multi-frame instance",
api: "http://127.0.0.1:5000/api/tool/my-retrieve-rendered-dicom-instance-tool/invoke",
requestHeader: map[string]string{},
requestBody: bytes.NewBuffer([]byte(`{"FrameNumber": 2, "storeID":"` + dicomStoreID + `", "StudyInstanceUID":"` + multiFrameDICOMInstance.study + `", "SeriesInstanceUID":"` + multiFrameDICOMInstance.series + `", "SOPInstanceUID":"` + multiFrameDICOMInstance.instance + `"}`)),
isErr: false,
},
}
for _, tc := range invokeTcs {
t.Run(tc.name, func(t *testing.T) {
_, status := runTest(t, tc.api, tc.requestHeader, tc.requestBody)
if tc.isErr {
if status == http.StatusOK {
t.Errorf("expected error but got success")
}
return
}
if status != http.StatusOK {
t.Errorf("expected status OK but got %d", status)
}
})
}
}
```