Skip to content

Commit 931198d

Browse files
authored
Merge branch 'main' into remove-sse-access-control
2 parents f66d3da + 76617b2 commit 931198d

File tree

18 files changed

+698
-29
lines changed

18 files changed

+698
-29
lines changed

.ci/continuous.release.cloudbuild.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ steps:
4747
tar -xf zig.tar.xz -C /zig-tools --strip-components=1
4848
4949
- id: "install-macos-sdk"
50-
name: golang:1
50+
name: "gcr.io/cloud-builders/gcloud:latest"
5151
waitFor: ['-']
5252
volumes:
5353
- name: 'macos-sdk'
@@ -57,8 +57,8 @@ steps:
5757
set -e
5858
apt-get update && apt-get install -y xz-utils
5959
echo "Downloading macOS 14.5 SDK..."
60-
curl -fL -o sdk.tar.xz https://github.com/alexey-lysiuk/macos-sdk/releases/download/14.5/MacOSX14.5.tar.xz
61-
60+
gcloud storage cp gs://${_ASSETS_BUCKET}/MacOSX14.5.tar.xz sdk.tar.xz
61+
6262
mkdir -p /macos-sdk/MacOSX14.5.sdk
6363
echo "Unpacking macOS 14.5 SDK..."
6464
tar -xf sdk.tar.xz -C /macos-sdk/MacOSX14.5.sdk --strip-components=1
@@ -310,4 +310,5 @@ substitutions:
310310
_AR_REPO_NAME: toolbox-dev
311311
_OLD_BUCKET_NAME: genai-toolbox-dev
312312
_BUCKET_NAME: mcp-toolbox-for-databases-dev
313+
_ASSETS_BUCKET: toolbox-build-assets
313314
_DOCKER_URI: ${_AR_HOSTNAME}/${PROJECT_ID}/${_AR_REPO_NAME}/toolbox

.ci/versioned.release.cloudbuild.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ steps:
6363
tar -xf zig.tar.xz -C /zig-tools --strip-components=1
6464
6565
- id: "install-macos-sdk"
66-
name: golang:1
66+
name: "gcr.io/cloud-builders/gcloud:latest"
6767
waitFor: ['-']
6868
volumes:
6969
- name: 'macos-sdk'
@@ -73,8 +73,8 @@ steps:
7373
set -e
7474
apt-get update && apt-get install -y xz-utils
7575
echo "Downloading macOS 14.5 SDK..."
76-
curl -fL -o sdk.tar.xz https://github.com/alexey-lysiuk/macos-sdk/releases/download/14.5/MacOSX14.5.tar.xz
77-
76+
gcloud storage cp gs://${_ASSETS_BUCKET}/MacOSX14.5.tar.xz sdk.tar.xz
77+
7878
mkdir -p /macos-sdk/MacOSX14.5.sdk
7979
echo "Unpacking macOS 14.5 SDK..."
8080
tar -xf sdk.tar.xz -C /macos-sdk/MacOSX14.5.sdk --strip-components=1
@@ -375,5 +375,6 @@ substitutions:
375375
_AR_REPO_NAME: toolbox
376376
_OLD_BUCKET_NAME: genai-toolbox
377377
_BUCKET_NAME: mcp-toolbox-for-databases
378+
_ASSETS_BUCKET: toolbox-build-assets
378379
_DOCKER_URI: ${_AR_HOSTNAME}/${PROJECT_ID}/${_AR_REPO_NAME}/toolbox
379380
_PUSH_LATEST: "false" # Substituted in trigger

cmd/internal/config_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1803,7 +1803,7 @@ func TestPrebuiltTools(t *testing.T) {
18031803
wantToolset: server.ToolsetConfigs{
18041804
"discovery": tools.ToolsetConfig{
18051805
Name: "discovery",
1806-
ToolNames: []string{"search_entries", "lookup_entry", "search_aspect_types", "lookup_context"},
1806+
ToolNames: []string{"search_entries", "lookup_entry", "search_aspect_types", "lookup_context", "search_dq_scans"},
18071807
},
18081808
},
18091809
},

cmd/internal/imports.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import (
9595
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexlookupcontext"
9696
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexlookupentry"
9797
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexsearchaspecttypes"
98+
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexsearchdqscans"
9899
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataplex/dataplexsearchentries"
99100
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataproc/dataprocgetcluster"
100101
_ "github.com/googleapis/mcp-toolbox/internal/tools/dataproc/dataprocgetjob"

docs/en/integrations/bigquery/source.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ project: "my-project-id"
113113
# - "https://www.googleapis.com/auth/bigquery"
114114
# - "https://www.googleapis.com/auth/drive.readonly"
115115
# maxQueryResultRows: 50 # Optional: Limits the number of rows returned by queries. Defaults to 50.
116+
# maximumBytesBilled: 10737418240 # Optional: Per-query bytes scanned cap (in bytes).
116117
```
117118

118119
Initialize a BigQuery source that uses the client's access token:
@@ -133,6 +134,7 @@ useClientOAuth: true
133134
# - "https://www.googleapis.com/auth/bigquery"
134135
# - "https://www.googleapis.com/auth/drive.readonly"
135136
# maxQueryResultRows: 50 # Optional: Limits the number of rows returned by queries. Defaults to 50.
137+
# maximumBytesBilled: 10737418240 # Optional: Per-query bytes scanned cap (in bytes).
136138
```
137139

138140
## Reference
@@ -148,3 +150,4 @@ useClientOAuth: true
148150
| scopes | []string | false | A list of OAuth 2.0 scopes to use for the credentials. If not provided, default scopes are used. |
149151
| impersonateServiceAccount | string | false | Service account email to impersonate when making BigQuery and Dataplex API calls. The authenticated principal must have the `roles/iam.serviceAccountTokenCreator` role on the target service account. [Learn More](https://cloud.google.com/iam/docs/service-account-impersonation) |
150152
| maxQueryResultRows | int | false | The maximum number of rows to return from a query. Defaults to 50. |
153+
| maximumBytesBilled | int64 | false | The maximum bytes billed per query. When set, queries that exceed this limit fail before executing. |
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: "dataplex-search-dq-scans"
3+
type: docs
4+
weight: 1
5+
description: >
6+
A "dataplex-search-dq-scans" tool allows to search for data quality scans based on the provided parameters.
7+
aliases:
8+
- /resources/tools/dataplex-search-dq-scans
9+
---
10+
11+
## About
12+
13+
A `dataplex-search-dq-scans` tool returns data quality scans that match the given criteria.
14+
It's compatible with the following sources:
15+
16+
- [dataplex](../../sources/dataplex.md)
17+
18+
`dataplex-search-dq-scans` accepts the following optional parameters:
19+
20+
- `filter` - Filter string to search/filter data quality scans. E.g. "display_name = \"my-scan\"".
21+
- `data_scan_id` - The resource name of the data scan to filter by: projects/{project}/locations/{locationId}/dataScans/{dataScanId}.
22+
- `table_name` - The name of the table to filter by. Maps to data.entity in the filter string. E.g. "//bigquery.googleapis.com/projects/P/datasets/D/tables/T".
23+
- `pageSize` - Number of returned data quality scans in the page. Defaults to `10`.
24+
- `orderBy` - Specifies the ordering of results.
25+
26+
## Requirements
27+
28+
### IAM Permissions
29+
30+
Dataplex uses [Identity and Access Management (IAM)][iam-overview] to control
31+
user and group access to Dataplex resources. Toolbox will use your
32+
[Application Default Credentials (ADC)][adc] to authorize and authenticate when
33+
interacting with [Dataplex][dataplex-docs].
34+
35+
In addition to [setting the ADC for your server][set-adc], you need to ensure
36+
the IAM identity has been given the correct IAM permissions for the tasks you
37+
intend to perform. See [Dataplex Universal Catalog IAM permissions][iam-permissions]
38+
and [Dataplex Universal Catalog IAM roles][iam-roles] for more information on
39+
applying IAM permissions and roles to an identity.
40+
41+
[iam-overview]: https://cloud.google.com/dataplex/docs/iam-and-access-control
42+
[adc]: https://cloud.google.com/docs/authentication#adc
43+
[set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc
44+
[iam-permissions]: https://cloud.google.com/dataplex/docs/iam-permissions
45+
[iam-roles]: https://cloud.google.com/dataplex/docs/iam-roles
46+
[dataplex-docs]: https://cloud.google.com/dataplex
47+
48+
## Example
49+
50+
```yaml
51+
kind: tools
52+
name: dataplex-search-dq-scans
53+
type: dataplex-search-dq-scans
54+
source: my-dataplex-source
55+
description: Use this tool to search for data quality scans.
56+
```
57+
58+
## Reference
59+
60+
| **field** | **type** | **required** | **description** |
61+
|-------------|:--------:|:------------:|----------------------------------------------------|
62+
| type | string | true | Must be "dataplex-search-dq-scans". |
63+
| source | string | true | Name of the source the tool should execute on. |
64+
| description | string | true | Description of the tool that is passed to the LLM. |

internal/prebuiltconfigs/tools/dataplex.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,15 @@ tools:
3434
kind: dataplex-lookup-context
3535
source: dataplex-source
3636
description: Retrieves rich metadata regarding one or more data assets along with their relationships.
37+
search_dq_scans:
38+
kind: dataplex-search-dq-scans
39+
source: dataplex-source
40+
description: Use this tool to search for data quality scans in Dataplex.
3741

3842
toolsets:
3943
discovery:
4044
- search_entries
4145
- lookup_entry
4246
- search_aspect_types
4347
- lookup_context
48+
- search_dq_scans

internal/sources/bigquery/bigquery.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ type Config struct {
9090
ImpersonateServiceAccount string `yaml:"impersonateServiceAccount"`
9191
Scopes StringOrStringSlice `yaml:"scopes"`
9292
MaxQueryResultRows int `yaml:"maxQueryResultRows"`
93+
MaximumBytesBilled int64 `yaml:"maximumBytesBilled" validate:"gte=0"`
9394
}
9495

9596
// StringOrStringSlice is a custom type that can unmarshal both a single string
@@ -157,6 +158,7 @@ func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.So
157158
RestService: restService,
158159
TokenSource: tokenSource,
159160
MaxQueryResultRows: r.MaxQueryResultRows,
161+
MaximumBytesBilled: r.MaximumBytesBilled,
160162
ClientCreator: clientCreator,
161163
AuthTokenHeaderName: "Authorization",
162164
}
@@ -291,6 +293,7 @@ type Source struct {
291293
TokenSource oauth2.TokenSource
292294
AuthTokenHeaderName string
293295
MaxQueryResultRows int
296+
MaximumBytesBilled int64
294297
ClientCreator BigqueryClientCreator
295298
AllowedDatasets map[string]struct{}
296299
sessionMutex sync.Mutex
@@ -472,6 +475,10 @@ func (s *Source) GetMaxQueryResultRows() int {
472475
return s.MaxQueryResultRows
473476
}
474477

478+
func (s *Source) GetMaximumBytesBilled() int64 {
479+
return s.MaximumBytesBilled
480+
}
481+
475482
func (s *Source) BigQueryClientCreator() BigqueryClientCreator {
476483
return s.ClientCreator
477484
}
@@ -572,6 +579,9 @@ func (s *Source) RunSQL(ctx context.Context, bqClient *bigqueryapi.Client, state
572579
if connProps != nil {
573580
query.ConnectionProperties = connProps
574581
}
582+
if s.MaximumBytesBilled > 0 {
583+
query.MaxBytesBilled = s.MaximumBytesBilled
584+
}
575585

576586
// This block handles SELECT statements, which return a row set.
577587
// We iterate through the results, convert each row into a map of

internal/sources/bigquery/bigquery_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,26 @@ func TestParseFromYamlBigQuery(t *testing.T) {
237237
},
238238
},
239239
},
240+
{
241+
desc: "with maximum bytes billed example",
242+
in: `
243+
kind: source
244+
name: my-instance
245+
type: bigquery
246+
project: my-project
247+
location: us
248+
maximumBytesBilled: 10737418240
249+
`,
250+
want: map[string]sources.SourceConfig{
251+
"my-instance": bigquery.Config{
252+
Name: "my-instance",
253+
Type: bigquery.SourceType,
254+
Project: "my-project",
255+
Location: "us",
256+
MaximumBytesBilled: 10737418240,
257+
},
258+
},
259+
},
240260
}
241261
for _, tc := range tcs {
242262
t.Run(tc.desc, func(t *testing.T) {
@@ -279,6 +299,17 @@ func TestFailParseFromYaml(t *testing.T) {
279299
`,
280300
err: "error unmarshaling source: unable to parse source \"my-instance\" as \"bigquery\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
281301
},
302+
{
303+
desc: "negative maximum bytes billed",
304+
in: `
305+
kind: source
306+
name: my-instance
307+
type: bigquery
308+
project: my-project
309+
maximumBytesBilled: -1
310+
`,
311+
err: "error unmarshaling source: unable to parse source \"my-instance\" as \"bigquery\": [1:21] Key: 'Config.MaximumBytesBilled' Error:Field validation for 'MaximumBytesBilled' failed on the 'gte' tag\n> 1 | maximumBytesBilled: -1\n ^\n 2 | name: my-instance\n 3 | project: my-project\n 4 | type: bigquery",
312+
},
282313
}
283314
for _, tc := range tcs {
284315
t.Run(tc.desc, func(t *testing.T) {
@@ -347,6 +378,59 @@ func TestInitialize_MaxQueryResultRows(t *testing.T) {
347378
}
348379
}
349380

381+
func TestInitialize_MaximumBytesBilled(t *testing.T) {
382+
ctx, err := testutils.ContextWithNewLogger()
383+
if err != nil {
384+
t.Fatalf("unexpected error: %s", err)
385+
}
386+
ctx = util.WithUserAgent(ctx, "test-agent")
387+
tracer := noop.NewTracerProvider().Tracer("")
388+
389+
tcs := []struct {
390+
desc string
391+
cfg bigquery.Config
392+
want int64
393+
}{
394+
{
395+
desc: "default value",
396+
cfg: bigquery.Config{
397+
Name: "test-default",
398+
Type: bigquery.SourceType,
399+
Project: "test-project",
400+
UseClientOAuth: "true",
401+
},
402+
want: 0,
403+
},
404+
{
405+
desc: "configured value",
406+
cfg: bigquery.Config{
407+
Name: "test-configured",
408+
Type: bigquery.SourceType,
409+
Project: "test-project",
410+
UseClientOAuth: "true",
411+
MaximumBytesBilled: 10737418240,
412+
},
413+
want: 10737418240,
414+
},
415+
}
416+
417+
for _, tc := range tcs {
418+
t.Run(tc.desc, func(t *testing.T) {
419+
src, err := tc.cfg.Initialize(ctx, tracer)
420+
if err != nil {
421+
t.Fatalf("Initialize failed: %v", err)
422+
}
423+
bqSrc, ok := src.(*bigquery.Source)
424+
if !ok {
425+
t.Fatalf("Expected *bigquery.Source, got %T", src)
426+
}
427+
if bqSrc.MaximumBytesBilled != tc.want {
428+
t.Errorf("MaximumBytesBilled = %d, want %d", bqSrc.MaximumBytesBilled, tc.want)
429+
}
430+
})
431+
}
432+
}
433+
350434
func TestNormalizeValue(t *testing.T) {
351435
tests := []struct {
352436
name string

0 commit comments

Comments
 (0)