mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-01-07 22:54:06 -05:00
feat(tools/looker): add ability to set destination folder with make_look and make_dashboard. (#2245)
## Description When running with a service account, the user has no personal folder id. This allows a destination folder to be specified as part of the call to make_dashboard and make_look. If a folder is not specified the user's personal folder will be used. ## PR Checklist > Thank you for opening a Pull Request! Before submitting your PR, there are a > few things you can do to make sure it goes smoothly: - [x] Make sure you reviewed [CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md) - [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [x] Ensure the tests and linter pass - [x] Code coverage does not decrease (if any source code was changed) - [x] Appropriate docs were updated (if necessary) - [x] Make sure to add `!` if this involve a breaking change 🛠️ Fixes #2225 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -18,9 +18,11 @@ It's compatible with the following sources:
|
||||
|
||||
- [looker](../../sources/looker.md)
|
||||
|
||||
`looker-make-dashboard` takes one parameter:
|
||||
`looker-make-dashboard` takes three parameters:
|
||||
|
||||
1. the `title`
|
||||
2. the `description`
|
||||
3. an optional `folder` id. If not provided, the user's default folder will be used.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ It's compatible with the following sources:
|
||||
|
||||
- [looker](../../sources/looker.md)
|
||||
|
||||
`looker-make-look` takes eleven parameters:
|
||||
`looker-make-look` takes twelve parameters:
|
||||
|
||||
1. the `model`
|
||||
2. the `explore`
|
||||
@@ -31,6 +31,7 @@ It's compatible with the following sources:
|
||||
9. an optional `vis_config`
|
||||
10. the `title`
|
||||
11. an optional `description`
|
||||
12. an optional `folder` id. If not provided, the user's default folder will be used.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -76,6 +76,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
|
||||
params = append(params, titleParameter)
|
||||
descParameter := parameters.NewStringParameterWithDefault("description", "", "The description of the Dashboard")
|
||||
params = append(params, descParameter)
|
||||
folderParameter := parameters.NewStringParameterWithDefault("folder", "", "The folder id where the Dashboard will be created. Leave blank to use the user's personal folder")
|
||||
params = append(params, folderParameter)
|
||||
|
||||
annotations := cfg.Annotations
|
||||
if annotations == nil {
|
||||
@@ -130,21 +132,26 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting sdk: %w", err)
|
||||
}
|
||||
|
||||
paramsMap := params.AsMap()
|
||||
title := paramsMap["title"].(string)
|
||||
description := paramsMap["description"].(string)
|
||||
folder := paramsMap["folder"].(string)
|
||||
|
||||
mrespFields := "id,personal_folder_id"
|
||||
mresp, err := sdk.Me(mrespFields, source.LookerApiSettings())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making me request: %s", err)
|
||||
}
|
||||
|
||||
paramsMap := params.AsMap()
|
||||
title := paramsMap["title"].(string)
|
||||
description := paramsMap["description"].(string)
|
||||
|
||||
if mresp.PersonalFolderId == nil || *mresp.PersonalFolderId == "" {
|
||||
return nil, fmt.Errorf("user does not have a personal folder. cannot continue")
|
||||
if folder == "" {
|
||||
if mresp.PersonalFolderId == nil || *mresp.PersonalFolderId == "" {
|
||||
return nil, fmt.Errorf("user does not have a personal folder. A folder must be specified")
|
||||
}
|
||||
folder = *mresp.PersonalFolderId
|
||||
}
|
||||
|
||||
dashs, err := sdk.FolderDashboards(*mresp.PersonalFolderId, "title", source.LookerApiSettings())
|
||||
dashs, err := sdk.FolderDashboards(folder, "title", source.LookerApiSettings())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting existing dashboards in folder: %s", err)
|
||||
}
|
||||
@@ -155,13 +162,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
|
||||
}
|
||||
if slices.Contains(dashTitles, title) {
|
||||
lt, _ := json.Marshal(dashTitles)
|
||||
return nil, fmt.Errorf("title %s already used in user's folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt))
|
||||
return nil, fmt.Errorf("title %s already used in folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt))
|
||||
}
|
||||
|
||||
wd := v4.WriteDashboard{
|
||||
Title: &title,
|
||||
Description: &description,
|
||||
FolderId: mresp.PersonalFolderId,
|
||||
FolderId: &folder,
|
||||
}
|
||||
resp, err := sdk.CreateDashboard(wd, source.LookerApiSettings())
|
||||
if err != nil {
|
||||
|
||||
@@ -76,6 +76,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
|
||||
params = append(params, titleParameter)
|
||||
descParameter := parameters.NewStringParameterWithDefault("description", "", "The description of the Look")
|
||||
params = append(params, descParameter)
|
||||
folderParameter := parameters.NewStringParameterWithDefault("folder", "", "The folder id where the Look will be created. Leave blank to use the user's personal folder")
|
||||
params = append(params, folderParameter)
|
||||
vizParameter := parameters.NewMapParameterWithDefault("vis_config",
|
||||
map[string]any{},
|
||||
"The visualization config for the query",
|
||||
@@ -140,17 +142,26 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting sdk: %w", err)
|
||||
}
|
||||
paramsMap := params.AsMap()
|
||||
title := paramsMap["title"].(string)
|
||||
description := paramsMap["description"].(string)
|
||||
folder := paramsMap["folder"].(string)
|
||||
visConfig := paramsMap["vis_config"].(map[string]any)
|
||||
|
||||
mrespFields := "id,personal_folder_id"
|
||||
mresp, err := sdk.Me(mrespFields, source.LookerApiSettings())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making me request: %s", err)
|
||||
}
|
||||
|
||||
paramsMap := params.AsMap()
|
||||
title := paramsMap["title"].(string)
|
||||
description := paramsMap["description"].(string)
|
||||
if folder == "" {
|
||||
if mresp.PersonalFolderId == nil || *mresp.PersonalFolderId == "" {
|
||||
return nil, fmt.Errorf("user does not have a personal folder. A folder must be specified")
|
||||
}
|
||||
folder = *mresp.PersonalFolderId
|
||||
}
|
||||
|
||||
looks, err := sdk.FolderLooks(*mresp.PersonalFolderId, "title", source.LookerApiSettings())
|
||||
looks, err := sdk.FolderLooks(folder, "title", source.LookerApiSettings())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting existing looks in folder: %s", err)
|
||||
}
|
||||
@@ -161,10 +172,9 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
|
||||
}
|
||||
if slices.Contains(lookTitles, title) {
|
||||
lt, _ := json.Marshal(lookTitles)
|
||||
return nil, fmt.Errorf("title %s already used in user's folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt))
|
||||
return nil, fmt.Errorf("title %s already used in folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt))
|
||||
}
|
||||
|
||||
visConfig := paramsMap["vis_config"].(map[string]any)
|
||||
wq.VisConfig = &visConfig
|
||||
|
||||
qrespFields := "id"
|
||||
@@ -178,7 +188,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
|
||||
UserId: mresp.Id,
|
||||
Description: &description,
|
||||
QueryId: qresp.Id,
|
||||
FolderId: mresp.PersonalFolderId,
|
||||
FolderId: &folder,
|
||||
}
|
||||
resp, err := sdk.CreateLook(wlwq, "", source.LookerApiSettings())
|
||||
if err != nil {
|
||||
|
||||
@@ -799,6 +799,13 @@ func TestLooker(t *testing.T) {
|
||||
"required": false,
|
||||
"type": "string",
|
||||
},
|
||||
map[string]any{
|
||||
"authSources": []any{},
|
||||
"description": "The folder id where the Look will be created. Leave blank to use the user's personal folder",
|
||||
"name": "folder",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
},
|
||||
map[string]any{
|
||||
"additionalProperties": true,
|
||||
"authSources": []any{},
|
||||
@@ -869,6 +876,13 @@ func TestLooker(t *testing.T) {
|
||||
"required": false,
|
||||
"type": "string",
|
||||
},
|
||||
map[string]any{
|
||||
"authSources": []any{},
|
||||
"description": "The folder id where the Dashboard will be created. Leave blank to use the user's personal folder",
|
||||
"name": "folder",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user