Skip to content

Commit

Permalink
When parsing query args, ensure jsonb args are passed to query as str…
Browse files Browse the repository at this point in the history
…ing not map. Support argument definitions which make an array out of a runtime dependency. #2772, Closes #2802
  • Loading branch information
kaidaguerre authored Nov 25, 2022
1 parent d2f8541 commit 882b678
Show file tree
Hide file tree
Showing 17 changed files with 1,585 additions and 338 deletions.
45 changes: 30 additions & 15 deletions pkg/dashboard/dashboardexecute/leaf_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,22 +441,14 @@ func (r *LeafRun) buildRuntimeDependencyArgs() (*modconfig.QueryArgs, error) {

// build map of default params
for _, dep := range r.runtimeDependencies {
// TACTICAL
// format the arg value as a JSON string
jsonBytes, err := json.Marshal(dep.value)
valStr := string(jsonBytes)
if err != nil {
return nil, err
}
if dep.dependency.ArgName != nil {
res.ArgMap[*dep.dependency.ArgName] = valStr
res.SetNamedArgVal(dep.value, *dep.dependency.ArgName)

} else {
if dep.dependency.ArgIndex == nil {
return nil, fmt.Errorf("invalid runtime dependency - both ArgName and ArgIndex are nil ")
}

// now add at correct index
res.ArgList[*dep.dependency.ArgIndex] = &valStr
res.SetPositionalArgVal(dep.value, *dep.dependency.ArgIndex)
}
}
return res, nil
Expand Down Expand Up @@ -504,16 +496,21 @@ func (r *LeafRun) executeWithRuns(ctx context.Context) {
// so all with runs have completed - check for errors
err := error_helpers.CombineErrors(errors...)
if err == nil {
r.setWithData()
if err := r.setWithData(); err != nil {
r.SetError(ctx, err)
}
} else {
r.SetError(ctx, err)
}
}

func (r *LeafRun) setWithData() {
func (r *LeafRun) setWithData() error {
for _, w := range r.withRuns {
r.setWithValue(w.DashboardNode.GetUnqualifiedName(), w.Data)
if err := r.setWithValue(w.DashboardNode.GetUnqualifiedName(), w.Data); err != nil {
return err
}
}
return nil
}

// if this leaf run has children (nodes/edges), execute them
Expand Down Expand Up @@ -651,9 +648,27 @@ func columnValuesFromRows(column string, rows []map[string]interface{}) (any, er
}
return res, nil
}
func (r *LeafRun) setWithValue(name string, result *dashboardtypes.LeafData) {
func (r *LeafRun) setWithValue(name string, result *dashboardtypes.LeafData) error {
r.withValueMutex.Lock()
defer r.withValueMutex.Unlock()

// TACTICAL - is there are any JSON columns convert them back to a JSON string
var jsonColumns []string
for _, c := range result.Columns {
if c.DataType == "JSONB" || c.DataType == "JSON" {
jsonColumns = append(jsonColumns, c.Name)
}
}
// now convert any json values into a json string
for _, c := range jsonColumns {
for _, row := range result.Rows {
jsonBytes, err := json.Marshal(row[c])
if err != nil {
return err
}
row[c] = string(jsonBytes)
}
}
r.withValues[name] = result
return nil
}
5 changes: 5 additions & 0 deletions pkg/dashboard/dashboardexecute/resolved_runtime_dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dashboardexecute

import (
"fmt"
"github.com/turbot/steampipe/pkg/type_conversion"
"sync"

"github.com/turbot/go-kit/helpers"
Expand Down Expand Up @@ -43,6 +44,10 @@ func (d *ResolvedRuntimeDependency) Resolve() (bool, error) {
if err != nil {
return false, err
}
// TACTICAL - if IsArray flag is set, wrap the dependency value in an array
if d.dependency.IsArray {
val = type_conversion.AnySliceToTypedSlice([]any{val})
}
d.value = val

// did we succeed
Expand Down
12 changes: 6 additions & 6 deletions pkg/steampipeconfig/modconfig/mod_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,17 +320,17 @@ func (m *ResourceMaps) Equals(other *ResourceMaps) bool {
}
}

for name, tables := range m.DashboardTables {
for name, table := range m.DashboardTables {
if otherTable, ok := other.DashboardTables[name]; !ok {
return false
} else if !tables.Equals(otherTable) {
} else if !table.Equals(otherTable) {
return false
}
}
for name, Categorys := range m.DashboardCategories {
for name, category := range m.DashboardCategories {
if otherCategory, ok := other.DashboardCategories[name]; !ok {
return false
} else if !Categorys.Equals(otherCategory) {
} else if !category.Equals(otherCategory) {
return false
}
}
Expand All @@ -340,10 +340,10 @@ func (m *ResourceMaps) Equals(other *ResourceMaps) bool {
}
}

for name, texts := range m.DashboardTexts {
for name, text := range m.DashboardTexts {
if otherText, ok := other.DashboardTexts[name]; !ok {
return false
} else if !texts.Equals(otherText) {
} else if !text.Equals(otherText) {
return false
}
}
Expand Down
48 changes: 41 additions & 7 deletions pkg/steampipeconfig/modconfig/param_def.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package modconfig

import (
"encoding/json"
"fmt"

"github.com/hashicorp/hcl/v2"
typehelpers "github.com/turbot/go-kit/types"
)

type ParamDef struct {
Name string `cty:"name" json:"name"`
FullName string `cty:"full_name" json:"-"`
Description *string `cty:"description" json:"description"`
RawDefault interface{} `json:"-"`
Default *string `cty:"default" json:"default"`
Name string `cty:"name" json:"name"`
FullName string `cty:"full_name" json:"-"`
Description *string `cty:"description" json:"description"`
Default *string `cty:"default" json:"default"`
// tactical - is the raw value a string
IsString bool `cty:"is_string" json:"-"`

// list of all blocks referenced by the resource
References []*ResourceReference `json:"-"`
Expand All @@ -27,12 +29,44 @@ func NewParamDef(block *hcl.Block) *ParamDef {
}
}

func (p ParamDef) String() string {
func (p *ParamDef) String() string {
return fmt.Sprintf("Name: %s, Description: %s, Default: %s", p.FullName, typehelpers.SafeString(p.Description), typehelpers.SafeString(p.Default))
}

func (p ParamDef) Equals(other *ParamDef) bool {
func (p *ParamDef) Equals(other *ParamDef) bool {
return p.Name == other.Name &&
typehelpers.SafeString(p.Description) == typehelpers.SafeString(other.Description) &&
typehelpers.SafeString(p.Default) == typehelpers.SafeString(other.Default)
}

// SetDefault sets the default as a atring points, marshalling to json is the underlying value is NOT a string
func (p *ParamDef) SetDefault(value interface{}) error {
strVal, ok := value.(string)
if ok {
p.IsString = true
// no need to convert to string
p.Default = &strVal
return nil
}
// format the arg value as a JSON string
jsonBytes, err := json.Marshal(value)
if err != nil {
return err
}
def := string(jsonBytes)
p.Default = &def
return nil
}

// GetDefault returns the default as an interface{}, unmarshalling json is the underlying value was NOT a string
func (p *ParamDef) GetDefault() (any, error) {
if p.Default == nil {
return nil, nil
}
if p.IsString {
return *p.Default, nil
}
var val any
err := json.Unmarshal([]byte(*p.Default), &val)
return val, err
}
Loading

0 comments on commit 882b678

Please sign in to comment.