fix(tools/looker): Enhance looker-run-dashboard to handle result maker structures and merge queries (#3698)

## Description

Not all dashboard tiles have a Query structure in the top layer. They
were not handled properly. This update handles query structures if they
are nested in result_makers or in merge result source queries.

## 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/mcp-toolbox/blob/main/CONTRIBUTING.md)
- [x] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/mcp-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 #3621
This commit is contained in:
Dr. Strangelove
2026-07-23 15:26:58 -04:00
committed by GitHub
parent 8e253e5b5b
commit 7666bc08de
@@ -181,12 +181,8 @@ func tileQueryWorker(ctx context.Context, sdk *v4.LookerSDK, options *rtl.ApiSet
}
// Check for SQL query
var sqlQueryId string
if element.ResultMaker != nil && element.ResultMaker.SqlQueryId != nil && *element.ResultMaker.SqlQueryId != "" {
sqlQueryId = *element.ResultMaker.SqlQueryId
}
if sqlQueryId != "" {
sqlQueryId := *element.ResultMaker.SqlQueryId
data["element_type"] = "sql_query"
queryResult, err := sdk.RunSqlQuery(sqlQueryId, "json", "", options)
if err != nil {
@@ -208,8 +204,53 @@ func tileQueryWorker(ctx context.Context, sdk *v4.LookerSDK, options *rtl.ApiSet
return
}
// Check for Merge query
if element.ResultMaker != nil && element.ResultMaker.MergeResultId != nil && *element.ResultMaker.MergeResultId != "" {
data["element_type"] = "merge_result"
mergeQuery, err := sdk.MergeQuery(*element.ResultMaker.MergeResultId, "", options)
if err != nil {
data["query_status"] = fmt.Sprintf("error getting merge query %s: %s", *element.ResultMaker.MergeResultId, err)
out <- data
return
}
data["parts"] = make([]any, 0)
for i, sourceQuery := range *mergeQuery.SourceQueries {
partData := make(map[string]any)
partData["index"] = i
if sourceQuery.Name != nil {
partData["name"] = *sourceQuery.Name
}
if sourceQuery.MergeFields != nil {
mar, err := json.Marshal(*sourceQuery.MergeFields)
if err == nil {
mergeFields := make([]any, 0)
if err := json.Unmarshal(mar, &mergeFields); err == nil {
partData["merge_fields"] = mergeFields
} else {
partData["merge_fields"] = fmt.Sprintf("error marshaling merge fields: %s", err)
}
} else {
partData["merge_fields"] = fmt.Sprintf("error unmarshaling merge fields: %s", err)
}
}
q, err := sdk.QueryForSlug(*sourceQuery.QuerySlug, "", options)
if err != nil {
partData["query_status"] = fmt.Sprintf("error getting query for slug %s: %s", *sourceQuery.QuerySlug, err)
} else {
runQuery(ctx, sdk, q, options, partData)
}
data["parts"] = append(data["parts"].([]any), partData)
}
out <- data
return
}
var q v4.Query
if element.Query != nil {
if element.ResultMaker != nil && element.ResultMaker.Query != nil {
data["element_type"] = "query"
q = *element.ResultMaker.Query
} else if element.Query != nil {
data["element_type"] = "query"
q = *element.Query
} else if element.Look != nil {
@@ -222,36 +263,38 @@ func tileQueryWorker(ctx context.Context, sdk *v4.LookerSDK, options *rtl.ApiSet
return
}
wq := v4.WriteQuery{
Model: q.Model,
View: q.View,
Fields: q.Fields,
Pivots: q.Pivots,
Filters: q.Filters,
Sorts: q.Sorts,
QueryTimezone: q.QueryTimezone,
Limit: q.Limit,
}
query_result, err := lookercommon.RunInlineQuery(ctx, sdk, &wq, "json", options)
if err != nil {
data["query_status"] = "error running query"
out <- data
return
}
var resp []any
e := json.Unmarshal([]byte(query_result), &resp)
if e != nil {
data["query_status"] = "error parsing query result"
out <- data
return
}
data["query_status"] = "success"
data["query_result"] = resp
runQuery(ctx, sdk, q, options, data)
out <- data
}()
return out
}
func runQuery(ctx context.Context, sdk *v4.LookerSDK, q v4.Query, options *rtl.ApiSettings, data map[string]any) {
wq := v4.WriteQuery{
Model: q.Model,
View: q.View,
Fields: q.Fields,
Pivots: q.Pivots,
Filters: q.Filters,
Sorts: q.Sorts,
QueryTimezone: q.QueryTimezone,
Limit: q.Limit,
}
query_result, err := lookercommon.RunInlineQuery(ctx, sdk, &wq, "json", options)
if err != nil {
data["query_status"] = fmt.Sprintf("error running query: %s", err)
return
}
var resp []any
e := json.Unmarshal([]byte(query_result), &resp)
if e != nil {
data["query_status"] = fmt.Sprintf("error parsing query result: %s", e)
return
}
data["query_status"] = "success"
data["query_result"] = resp
}
func merge(channels ...<-chan map[string]any) <-chan map[string]any {
var wg sync.WaitGroup
out := make(chan map[string]any)