mirror of https://github.com/grafana/grafana
Peakq: move templates into query service (#82193)
parent
a439ee46bf
commit
ac5a387086
@ -1,2 +0,0 @@ |
|||||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/peakq/v0alpha1,QueryTemplateSpec,Variables |
|
||||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/peakq/v0alpha1,TemplateVariable,DefaultValues |
|
@ -0,0 +1,6 @@ |
|||||||
|
// +k8s:deepcopy-gen=package
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
// +k8s:defaulter-gen=TypeMeta
|
||||||
|
// +groupName=query.grafana.app
|
||||||
|
|
||||||
|
package template // import "github.com/grafana/grafana/pkg/apis/query/v0alpha1/template"
|
@ -0,0 +1,81 @@ |
|||||||
|
package template |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
func TestFormat(t *testing.T) { |
||||||
|
// Invalid input
|
||||||
|
require.Equal(t, "", FormatVariables(FormatCSV, nil)) |
||||||
|
require.Equal(t, "", FormatVariables(FormatCSV, []string{})) |
||||||
|
|
||||||
|
type check struct { |
||||||
|
name string |
||||||
|
input []string |
||||||
|
output map[VariableFormat]string |
||||||
|
} |
||||||
|
|
||||||
|
tests := []check{ |
||||||
|
{ |
||||||
|
name: "three simple variables", |
||||||
|
input: []string{"a", "b", "c"}, |
||||||
|
output: map[VariableFormat]string{ |
||||||
|
FormatCSV: "a,b,c", |
||||||
|
FormatJSON: `["a","b","c"]`, |
||||||
|
FormatDoubleQuote: `"a","b","c"`, |
||||||
|
FormatSingleQuote: `'a','b','c'`, |
||||||
|
FormatPipe: `a|b|c`, |
||||||
|
FormatRaw: "a,b,c", |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "single value", |
||||||
|
input: []string{"a"}, |
||||||
|
output: map[VariableFormat]string{ |
||||||
|
FormatCSV: "a", |
||||||
|
FormatJSON: `["a"]`, |
||||||
|
FormatDoubleQuote: `"a"`, |
||||||
|
FormatSingleQuote: `'a'`, |
||||||
|
FormatPipe: "a", |
||||||
|
FormatRaw: "a", |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "value with quote", |
||||||
|
input: []string{`hello "world"`}, |
||||||
|
output: map[VariableFormat]string{ |
||||||
|
FormatCSV: `"hello ""world"""`, // note the double quotes
|
||||||
|
FormatJSON: `["hello \"world\""]`, |
||||||
|
FormatDoubleQuote: `"hello \"world\""`, |
||||||
|
FormatSingleQuote: `'hello "world"'`, |
||||||
|
FormatPipe: `hello "world"`, |
||||||
|
FormatRaw: `hello "world"`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
for _, test := range tests { |
||||||
|
// Make sure all keys are set in tests
|
||||||
|
all := map[VariableFormat]bool{ |
||||||
|
FormatRaw: true, |
||||||
|
FormatCSV: true, |
||||||
|
FormatJSON: true, |
||||||
|
FormatDoubleQuote: true, |
||||||
|
FormatSingleQuote: true, |
||||||
|
FormatPipe: true, |
||||||
|
} |
||||||
|
|
||||||
|
// Check the default (no format) matches CSV
|
||||||
|
require.Equal(t, test.output[FormatRaw], |
||||||
|
FormatVariables("", test.input), |
||||||
|
"test %s default values are not raw", test.name) |
||||||
|
|
||||||
|
// Check each input value
|
||||||
|
for format, v := range test.output { |
||||||
|
require.Equal(t, v, FormatVariables(format, test.input), "Test: %s (format:%s)", test.name, format) |
||||||
|
delete(all, format) |
||||||
|
} |
||||||
|
require.Empty(t, all, "test %s is missing cases for: %v", test.name, all) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
package template |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"sort" |
||||||
|
|
||||||
|
"github.com/spyzhov/ajson" |
||||||
|
|
||||||
|
query "github.com/grafana/grafana/pkg/apis/query/v0alpha1" |
||||||
|
) |
||||||
|
|
||||||
|
// RenderTemplate applies selected values into a query template
|
||||||
|
func RenderTemplate(qt QueryTemplate, selectedValues map[string][]string) ([]Target, error) { |
||||||
|
targets := qt.DeepCopy().Targets |
||||||
|
|
||||||
|
rawTargetObjects := make([]*ajson.Node, len(qt.Targets)) |
||||||
|
for i, t := range qt.Targets { |
||||||
|
b, err := t.Properties.MarshalJSON() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
rawTargetObjects[i], err = ajson.Unmarshal(b) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
rm := getReplacementMap(qt) |
||||||
|
for targetIdx, byTargetIdx := range rm { |
||||||
|
for path, reps := range byTargetIdx { |
||||||
|
o := rawTargetObjects[targetIdx] |
||||||
|
nodes, err := o.JSONPath(path) |
||||||
|
if err != nil { |
||||||
|
return nil, fmt.Errorf("failed to find path %v: %w", path, err) |
||||||
|
} |
||||||
|
if len(nodes) != 1 { |
||||||
|
return nil, fmt.Errorf("expected one lead node at path %v but got %v", path, len(nodes)) |
||||||
|
} |
||||||
|
n := nodes[0] |
||||||
|
if !n.IsString() { |
||||||
|
return nil, fmt.Errorf("only string type leaf notes supported currently, %v is not a string", path) |
||||||
|
} |
||||||
|
s := []rune(n.String()) |
||||||
|
s = s[1 : len(s)-1] |
||||||
|
var offSet int64 |
||||||
|
for _, r := range reps { |
||||||
|
value := []rune(FormatVariables(r.format, selectedValues[r.Key])) |
||||||
|
if r.Position == nil { |
||||||
|
return nil, fmt.Errorf("nil position not support yet, will be full replacement") |
||||||
|
} |
||||||
|
s = append(s[:r.Start+offSet], append(value, s[r.End+offSet:]...)...) |
||||||
|
offSet += int64(len(value)) - (r.End - r.Start) |
||||||
|
} |
||||||
|
if err = n.SetString(string(s)); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for i, aT := range rawTargetObjects { |
||||||
|
raw, err := ajson.Marshal(aT) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
u := query.GenericDataQuery{} |
||||||
|
err = u.UnmarshalJSON(raw) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
targets[i].Properties = u |
||||||
|
} |
||||||
|
|
||||||
|
return targets, nil |
||||||
|
} |
||||||
|
|
||||||
|
type replacement struct { |
||||||
|
*Position |
||||||
|
*TemplateVariable |
||||||
|
format VariableFormat |
||||||
|
} |
||||||
|
|
||||||
|
func getReplacementMap(qt QueryTemplate) map[int]map[string][]replacement { |
||||||
|
byTargetPath := make(map[int]map[string][]replacement) |
||||||
|
|
||||||
|
varMap := make(map[string]*TemplateVariable, len(qt.Variables)) |
||||||
|
for i, v := range qt.Variables { |
||||||
|
varMap[v.Key] = &qt.Variables[i] |
||||||
|
} |
||||||
|
|
||||||
|
for i, target := range qt.Targets { |
||||||
|
if byTargetPath[i] == nil { |
||||||
|
byTargetPath[i] = make(map[string][]replacement) |
||||||
|
} |
||||||
|
for k, vReps := range target.Variables { |
||||||
|
for rI, rep := range vReps { |
||||||
|
byTargetPath[i][rep.Path] = append(byTargetPath[i][rep.Path], |
||||||
|
replacement{ |
||||||
|
Position: vReps[rI].Position, |
||||||
|
TemplateVariable: varMap[k], |
||||||
|
format: rep.Format, |
||||||
|
}, |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for idx, byTargetIdx := range byTargetPath { |
||||||
|
for path := range byTargetIdx { |
||||||
|
sort.Slice(byTargetPath[idx][path], func(i, j int) bool { |
||||||
|
return byTargetPath[idx][path][i].Start < byTargetPath[idx][path][j].Start |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return byTargetPath |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
package template |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/data" |
||||||
|
|
||||||
|
common "github.com/grafana/grafana/pkg/apis/common/v0alpha1" |
||||||
|
query "github.com/grafana/grafana/pkg/apis/query/v0alpha1" |
||||||
|
) |
||||||
|
|
||||||
|
type QueryTemplate struct { |
||||||
|
// A display name
|
||||||
|
Title string `json:"title,omitempty"` |
||||||
|
|
||||||
|
// Longer description for why it is interesting
|
||||||
|
Description string `json:"description,omitempty"` |
||||||
|
|
||||||
|
// The variables that can be used to render
|
||||||
|
// +listType=map
|
||||||
|
// +listMapKey=key
|
||||||
|
Variables []TemplateVariable `json:"vars,omitempty"` |
||||||
|
|
||||||
|
// Output variables
|
||||||
|
// +listType=set
|
||||||
|
Targets []Target `json:"targets"` |
||||||
|
} |
||||||
|
|
||||||
|
type Target struct { |
||||||
|
// DataType is the returned Dataplane type from the query.
|
||||||
|
DataType data.FrameType `json:"dataType,omitempty"` |
||||||
|
|
||||||
|
// DataTypeVersion is the version for the Dataplane type.
|
||||||
|
// TODO 2[uint] seems to panic, maybe implement DeepCopy on data.FrameTypeVersion?
|
||||||
|
// DataTypeVersion data.FrameTypeVersion `json:"dataTypeVersion,omitempty"`
|
||||||
|
|
||||||
|
// Variables that will be replaced in the query
|
||||||
|
Variables map[string][]VariableReplacement `json:"variables"` |
||||||
|
|
||||||
|
// Query target
|
||||||
|
Properties query.GenericDataQuery `json:"properties"` |
||||||
|
} |
||||||
|
|
||||||
|
// TemplateVariable is the definition of a variable that will be interpolated
|
||||||
|
// in targets.
|
||||||
|
type TemplateVariable struct { |
||||||
|
// Key is the name of the variable.
|
||||||
|
Key string `json:"key"` |
||||||
|
|
||||||
|
// DefaultValue is the value to be used when there is no selected value
|
||||||
|
// during render.
|
||||||
|
// +listType=atomic
|
||||||
|
DefaultValues []string `json:"defaultValues,omitempty"` |
||||||
|
|
||||||
|
// ValueListDefinition is the object definition used by the FE
|
||||||
|
// to get a list of possible values to select for render.
|
||||||
|
ValueListDefinition common.Unstructured `json:"valueListDefinition,omitempty"` |
||||||
|
} |
||||||
|
|
||||||
|
// QueryVariable is the definition of a variable that will be interpolated
|
||||||
|
// in targets.
|
||||||
|
type VariableReplacement struct { |
||||||
|
// Path is the location of the property within a target.
|
||||||
|
// The format for this is not figured out yet (Maybe JSONPath?).
|
||||||
|
// Idea: ["string", int, "string"] where int indicates array offset
|
||||||
|
Path string `json:"path"` |
||||||
|
|
||||||
|
// Positions is a list of where to perform the interpolation
|
||||||
|
// within targets during render.
|
||||||
|
// The first string is the Idx of the target as a string, since openAPI
|
||||||
|
// does not support ints as map keys
|
||||||
|
Position *Position `json:"position,omitempty"` |
||||||
|
|
||||||
|
// How values should be interpolated
|
||||||
|
Format VariableFormat `json:"format,omitempty"` |
||||||
|
} |
||||||
|
|
||||||
|
// Define how to format values in the template.
|
||||||
|
// See: https://grafana.com/docs/grafana/latest/dashboards/variables/variable-syntax/#advanced-variable-format-options
|
||||||
|
// +enum
|
||||||
|
type VariableFormat string |
||||||
|
|
||||||
|
// Defines values for ItemType.
|
||||||
|
const ( |
||||||
|
// Formats variables with multiple values as a comma-separated string.
|
||||||
|
FormatCSV VariableFormat = "csv" |
||||||
|
|
||||||
|
// Formats variables with multiple values as a comma-separated string.
|
||||||
|
FormatJSON VariableFormat = "json" |
||||||
|
|
||||||
|
// Formats single- and multi-valued variables into a comma-separated string
|
||||||
|
FormatDoubleQuote VariableFormat = "doublequote" |
||||||
|
|
||||||
|
// Formats single- and multi-valued variables into a comma-separated string
|
||||||
|
FormatSingleQuote VariableFormat = "singlequote" |
||||||
|
|
||||||
|
// Formats variables with multiple values into a pipe-separated string.
|
||||||
|
FormatPipe VariableFormat = "pipe" |
||||||
|
|
||||||
|
// Formats variables with multiple values into comma-separated string.
|
||||||
|
// This is the default behavior when no format is specified
|
||||||
|
FormatRaw VariableFormat = "raw" |
||||||
|
) |
||||||
|
|
||||||
|
// Position is where to do replacement in the targets
|
||||||
|
// during render.
|
||||||
|
type Position struct { |
||||||
|
// Start is the byte offset within TargetKey's property of the variable.
|
||||||
|
// It is the start location for replacements).
|
||||||
|
Start int64 `json:"start"` // TODO: byte, rune?
|
||||||
|
|
||||||
|
// End is the byte offset of the end of the variable.
|
||||||
|
End int64 `json:"end"` |
||||||
|
} |
@ -0,0 +1,131 @@ |
|||||||
|
//go:build !ignore_autogenerated
|
||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package template |
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Position) DeepCopyInto(out *Position) { |
||||||
|
*out = *in |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Position.
|
||||||
|
func (in *Position) DeepCopy() *Position { |
||||||
|
if in == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
out := new(Position) |
||||||
|
in.DeepCopyInto(out) |
||||||
|
return out |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *QueryTemplate) DeepCopyInto(out *QueryTemplate) { |
||||||
|
*out = *in |
||||||
|
if in.Variables != nil { |
||||||
|
in, out := &in.Variables, &out.Variables |
||||||
|
*out = make([]TemplateVariable, len(*in)) |
||||||
|
for i := range *in { |
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i]) |
||||||
|
} |
||||||
|
} |
||||||
|
if in.Targets != nil { |
||||||
|
in, out := &in.Targets, &out.Targets |
||||||
|
*out = make([]Target, len(*in)) |
||||||
|
for i := range *in { |
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i]) |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QueryTemplate.
|
||||||
|
func (in *QueryTemplate) DeepCopy() *QueryTemplate { |
||||||
|
if in == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
out := new(QueryTemplate) |
||||||
|
in.DeepCopyInto(out) |
||||||
|
return out |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Target) DeepCopyInto(out *Target) { |
||||||
|
*out = *in |
||||||
|
if in.Variables != nil { |
||||||
|
in, out := &in.Variables, &out.Variables |
||||||
|
*out = make(map[string][]VariableReplacement, len(*in)) |
||||||
|
for key, val := range *in { |
||||||
|
var outVal []VariableReplacement |
||||||
|
if val == nil { |
||||||
|
(*out)[key] = nil |
||||||
|
} else { |
||||||
|
in, out := &val, &outVal |
||||||
|
*out = make([]VariableReplacement, len(*in)) |
||||||
|
for i := range *in { |
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i]) |
||||||
|
} |
||||||
|
} |
||||||
|
(*out)[key] = outVal |
||||||
|
} |
||||||
|
} |
||||||
|
in.Properties.DeepCopyInto(&out.Properties) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Target.
|
||||||
|
func (in *Target) DeepCopy() *Target { |
||||||
|
if in == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
out := new(Target) |
||||||
|
in.DeepCopyInto(out) |
||||||
|
return out |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TemplateVariable) DeepCopyInto(out *TemplateVariable) { |
||||||
|
*out = *in |
||||||
|
if in.DefaultValues != nil { |
||||||
|
in, out := &in.DefaultValues, &out.DefaultValues |
||||||
|
*out = make([]string, len(*in)) |
||||||
|
copy(*out, *in) |
||||||
|
} |
||||||
|
in.ValueListDefinition.DeepCopyInto(&out.ValueListDefinition) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplateVariable.
|
||||||
|
func (in *TemplateVariable) DeepCopy() *TemplateVariable { |
||||||
|
if in == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
out := new(TemplateVariable) |
||||||
|
in.DeepCopyInto(out) |
||||||
|
return out |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *VariableReplacement) DeepCopyInto(out *VariableReplacement) { |
||||||
|
*out = *in |
||||||
|
if in.Position != nil { |
||||||
|
in, out := &in.Position, &out.Position |
||||||
|
*out = new(Position) |
||||||
|
**out = **in |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VariableReplacement.
|
||||||
|
func (in *VariableReplacement) DeepCopy() *VariableReplacement { |
||||||
|
if in == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
out := new(VariableReplacement) |
||||||
|
in.DeepCopyInto(out) |
||||||
|
return out |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
//go:build !ignore_autogenerated
|
||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package template |
||||||
|
|
||||||
|
import ( |
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime" |
||||||
|
) |
||||||
|
|
||||||
|
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||||
|
// Public to allow building arbitrary schemes.
|
||||||
|
// All generated defaulters are covering - they call all nested defaulters.
|
||||||
|
func RegisterDefaults(scheme *runtime.Scheme) error { |
||||||
|
return nil |
||||||
|
} |
@ -1,83 +0,0 @@ |
|||||||
package peakq |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"github.com/stretchr/testify/require" |
|
||||||
|
|
||||||
peakq "github.com/grafana/grafana/pkg/apis/peakq/v0alpha1" |
|
||||||
) |
|
||||||
|
|
||||||
func TestFormat(t *testing.T) { |
|
||||||
// Invalid input
|
|
||||||
require.Equal(t, "", formatVariables(peakq.FormatCSV, nil)) |
|
||||||
require.Equal(t, "", formatVariables(peakq.FormatCSV, []string{})) |
|
||||||
|
|
||||||
type check struct { |
|
||||||
name string |
|
||||||
input []string |
|
||||||
output map[peakq.VariableFormat]string |
|
||||||
} |
|
||||||
|
|
||||||
tests := []check{ |
|
||||||
{ |
|
||||||
name: "three simple variables", |
|
||||||
input: []string{"a", "b", "c"}, |
|
||||||
output: map[peakq.VariableFormat]string{ |
|
||||||
peakq.FormatCSV: "a,b,c", |
|
||||||
peakq.FormatJSON: `["a","b","c"]`, |
|
||||||
peakq.FormatDoubleQuote: `"a","b","c"`, |
|
||||||
peakq.FormatSingleQuote: `'a','b','c'`, |
|
||||||
peakq.FormatPipe: `a|b|c`, |
|
||||||
peakq.FormatRaw: "a,b,c", |
|
||||||
}, |
|
||||||
}, |
|
||||||
{ |
|
||||||
name: "single value", |
|
||||||
input: []string{"a"}, |
|
||||||
output: map[peakq.VariableFormat]string{ |
|
||||||
peakq.FormatCSV: "a", |
|
||||||
peakq.FormatJSON: `["a"]`, |
|
||||||
peakq.FormatDoubleQuote: `"a"`, |
|
||||||
peakq.FormatSingleQuote: `'a'`, |
|
||||||
peakq.FormatPipe: "a", |
|
||||||
peakq.FormatRaw: "a", |
|
||||||
}, |
|
||||||
}, |
|
||||||
{ |
|
||||||
name: "value with quote", |
|
||||||
input: []string{`hello "world"`}, |
|
||||||
output: map[peakq.VariableFormat]string{ |
|
||||||
peakq.FormatCSV: `"hello ""world"""`, // note the double quotes
|
|
||||||
peakq.FormatJSON: `["hello \"world\""]`, |
|
||||||
peakq.FormatDoubleQuote: `"hello \"world\""`, |
|
||||||
peakq.FormatSingleQuote: `'hello "world"'`, |
|
||||||
peakq.FormatPipe: `hello "world"`, |
|
||||||
peakq.FormatRaw: `hello "world"`, |
|
||||||
}, |
|
||||||
}, |
|
||||||
} |
|
||||||
for _, test := range tests { |
|
||||||
// Make sure all keys are set in tests
|
|
||||||
all := map[peakq.VariableFormat]bool{ |
|
||||||
peakq.FormatRaw: true, |
|
||||||
peakq.FormatCSV: true, |
|
||||||
peakq.FormatJSON: true, |
|
||||||
peakq.FormatDoubleQuote: true, |
|
||||||
peakq.FormatSingleQuote: true, |
|
||||||
peakq.FormatPipe: true, |
|
||||||
} |
|
||||||
|
|
||||||
// Check the default (no format) matches CSV
|
|
||||||
require.Equal(t, test.output[peakq.FormatRaw], |
|
||||||
formatVariables("", test.input), |
|
||||||
"test %s default values are not raw", test.name) |
|
||||||
|
|
||||||
// Check each input value
|
|
||||||
for format, v := range test.output { |
|
||||||
require.Equal(t, v, formatVariables(format, test.input), "Test: %s (format:%s)", test.name, format) |
|
||||||
delete(all, format) |
|
||||||
} |
|
||||||
require.Empty(t, all, "test %s is missing cases for: %v", test.name, all) |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue