Dashboard Schema V2: Support panel actions (#110842)
* support panel actions * refactor * add test; move action transformer to utils * refactor so v2 headers and queryParams are just a simple record * update open api * update actions to be same shape accross all dashboard schemas and add validation on the backend * cleanup * update snapshot * add tests to validation
This commit is contained in:
@@ -302,6 +302,59 @@ lineage: schemas: [{
|
||||
// - "inControlsMenu" renders the link in bottom part of the dashboard controls dropdown menu
|
||||
#DashboardLinkPlacement: "inControlsMenu" @cuetsy(kind="type")
|
||||
|
||||
// Dashboard action type
|
||||
#ActionType: "fetch" | "infinity" @cuetsy(kind="type")
|
||||
|
||||
// Fetch options
|
||||
#FetchOptions: {
|
||||
method: #HttpRequestMethod
|
||||
url: string
|
||||
body?: string
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
queryParams?: [...[...string]]
|
||||
headers?: [...[...string]]
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// Infinity options
|
||||
#InfinityOptions: {
|
||||
method: #HttpRequestMethod
|
||||
url: string
|
||||
body?: string
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
queryParams?: [...[...string]]
|
||||
headers?: [...[...string]]
|
||||
datasourceUid: string
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" @cuetsy(kind="type")
|
||||
|
||||
// Action variable type
|
||||
#ActionVariableType: "string" @cuetsy(kind="type")
|
||||
|
||||
#ActionVariable: {
|
||||
key: string
|
||||
name: string
|
||||
type: #ActionVariableType
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// Dashboard action
|
||||
#Action: {
|
||||
type: #ActionType
|
||||
title: string
|
||||
fetch?: #FetchOptions
|
||||
infinity?: #InfinityOptions
|
||||
confirmation?: string
|
||||
oneClick?: bool
|
||||
variables?: [...#ActionVariable]
|
||||
style?: {
|
||||
backgroundColor?: string
|
||||
}
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// Dashboard variable type
|
||||
// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on.
|
||||
// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only).
|
||||
@@ -731,6 +784,9 @@ lineage: schemas: [{
|
||||
// The behavior when clicking on a result
|
||||
links?: [...] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Define interactive HTTP requests that can be triggered from data visualizations.
|
||||
actions?: [...#Action] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
|
||||
@@ -302,6 +302,59 @@ lineage: schemas: [{
|
||||
// - "inControlsMenu" renders the link in bottom part of the dashboard controls dropdown menu
|
||||
#DashboardLinkPlacement: "inControlsMenu" @cuetsy(kind="type")
|
||||
|
||||
// Dashboard action type
|
||||
#ActionType: "fetch" | "infinity" @cuetsy(kind="type")
|
||||
|
||||
// Fetch options
|
||||
#FetchOptions: {
|
||||
method: #HttpRequestMethod
|
||||
url: string
|
||||
body?: string
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
queryParams?: [...[...string]]
|
||||
headers?: [...[...string]]
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// Infinity options
|
||||
#InfinityOptions: {
|
||||
method: #HttpRequestMethod
|
||||
url: string
|
||||
body?: string
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
queryParams?: [...[...string]]
|
||||
headers?: [...[...string]]
|
||||
datasourceUid: string
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH" @cuetsy(kind="type")
|
||||
|
||||
// Action variable type
|
||||
#ActionVariableType: "string" @cuetsy(kind="type")
|
||||
|
||||
#ActionVariable: {
|
||||
key: string
|
||||
name: string
|
||||
type: #ActionVariableType
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// Dashboard action
|
||||
#Action: {
|
||||
type: #ActionType
|
||||
title: string
|
||||
fetch?: #FetchOptions
|
||||
infinity?: #InfinityOptions
|
||||
confirmation?: string
|
||||
oneClick?: bool
|
||||
variables?: [...#ActionVariable]
|
||||
style?: {
|
||||
backgroundColor?: string
|
||||
}
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// Dashboard variable type
|
||||
// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on.
|
||||
// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only).
|
||||
@@ -731,6 +784,9 @@ lineage: schemas: [{
|
||||
// The behavior when clicking on a result
|
||||
links?: [...] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Define interactive HTTP requests that can be triggered from data visualizations.
|
||||
actions?: [...#Action] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
|
||||
@@ -225,6 +225,9 @@ FieldConfig: {
|
||||
// The behavior when clicking on a result
|
||||
links?: [...]
|
||||
|
||||
// Define interactive HTTP requests that can be triggered from data visualizations.
|
||||
actions?: [...Action]
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string
|
||||
|
||||
@@ -368,6 +371,47 @@ FieldColor: {
|
||||
// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
|
||||
DashboardLinkType: "link" | "dashboards"
|
||||
|
||||
ActionType: "fetch" | "infinity"
|
||||
|
||||
FetchOptions: {
|
||||
method: HttpRequestMethod
|
||||
url: string
|
||||
body?: string
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
queryParams?: [...[...string]]
|
||||
headers?: [...[...string]]
|
||||
}
|
||||
|
||||
InfinityOptions: FetchOptions & {
|
||||
datasourceUid: string
|
||||
}
|
||||
|
||||
HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH"
|
||||
|
||||
// Action variable type
|
||||
ActionVariableType: "string"
|
||||
|
||||
ActionVariable: {
|
||||
key: string
|
||||
name: string
|
||||
type: ActionVariableType
|
||||
}
|
||||
|
||||
Action: {
|
||||
type: ActionType
|
||||
title: string
|
||||
fetch?: FetchOptions
|
||||
infinity?: InfinityOptions
|
||||
confirmation?: string
|
||||
oneClick?: bool
|
||||
variables?: [...ActionVariable]
|
||||
style?: {
|
||||
backgroundColor?: string
|
||||
}
|
||||
}
|
||||
|
||||
// --- Common types ---
|
||||
Kind: {
|
||||
kind: string
|
||||
|
||||
@@ -392,6 +392,8 @@ type DashboardFieldConfig struct {
|
||||
Color *DashboardFieldColor `json:"color,omitempty"`
|
||||
// The behavior when clicking on a result
|
||||
Links []interface{} `json:"links,omitempty"`
|
||||
// Define interactive HTTP requests that can be triggered from data visualizations.
|
||||
Actions []DashboardAction `json:"actions,omitempty"`
|
||||
// Alternative to empty string
|
||||
NoValue *string `json:"noValue,omitempty"`
|
||||
// custom is specified by the FieldConfig field
|
||||
@@ -623,6 +625,95 @@ const (
|
||||
DashboardFieldColorSeriesByModeLast DashboardFieldColorSeriesByMode = "last"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardAction struct {
|
||||
Type DashboardActionType `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Fetch *DashboardFetchOptions `json:"fetch,omitempty"`
|
||||
Infinity *DashboardInfinityOptions `json:"infinity,omitempty"`
|
||||
Confirmation *string `json:"confirmation,omitempty"`
|
||||
OneClick *bool `json:"oneClick,omitempty"`
|
||||
Variables []DashboardActionVariable `json:"variables,omitempty"`
|
||||
Style *DashboardV2alpha1ActionStyle `json:"style,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardAction creates a new DashboardAction object.
|
||||
func NewDashboardAction() *DashboardAction {
|
||||
return &DashboardAction{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardActionType string
|
||||
|
||||
const (
|
||||
DashboardActionTypeFetch DashboardActionType = "fetch"
|
||||
DashboardActionTypeInfinity DashboardActionType = "infinity"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardFetchOptions struct {
|
||||
Method DashboardHttpRequestMethod `json:"method"`
|
||||
Url string `json:"url"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
QueryParams [][]string `json:"queryParams,omitempty"`
|
||||
Headers [][]string `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardFetchOptions creates a new DashboardFetchOptions object.
|
||||
func NewDashboardFetchOptions() *DashboardFetchOptions {
|
||||
return &DashboardFetchOptions{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardHttpRequestMethod string
|
||||
|
||||
const (
|
||||
DashboardHttpRequestMethodGET DashboardHttpRequestMethod = "GET"
|
||||
DashboardHttpRequestMethodPUT DashboardHttpRequestMethod = "PUT"
|
||||
DashboardHttpRequestMethodPOST DashboardHttpRequestMethod = "POST"
|
||||
DashboardHttpRequestMethodDELETE DashboardHttpRequestMethod = "DELETE"
|
||||
DashboardHttpRequestMethodPATCH DashboardHttpRequestMethod = "PATCH"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardInfinityOptions struct {
|
||||
Method DashboardHttpRequestMethod `json:"method"`
|
||||
Url string `json:"url"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
QueryParams [][]string `json:"queryParams,omitempty"`
|
||||
DatasourceUid string `json:"datasourceUid"`
|
||||
Headers [][]string `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardInfinityOptions creates a new DashboardInfinityOptions object.
|
||||
func NewDashboardInfinityOptions() *DashboardInfinityOptions {
|
||||
return &DashboardInfinityOptions{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardActionVariable struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// NewDashboardActionVariable creates a new DashboardActionVariable object.
|
||||
func NewDashboardActionVariable() *DashboardActionVariable {
|
||||
return &DashboardActionVariable{
|
||||
Type: DashboardActionVariableType,
|
||||
}
|
||||
}
|
||||
|
||||
// Action variable type
|
||||
// +k8s:openapi-gen=true
|
||||
const DashboardActionVariableType = "string"
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardDynamicConfigValue struct {
|
||||
Id string `json:"id"`
|
||||
@@ -1831,6 +1922,16 @@ func NewDashboardV2alpha1SpecialValueMapOptions() *DashboardV2alpha1SpecialValue
|
||||
}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardV2alpha1ActionStyle struct {
|
||||
BackgroundColor *string `json:"backgroundColor,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardV2alpha1ActionStyle creates a new DashboardV2alpha1ActionStyle object.
|
||||
func NewDashboardV2alpha1ActionStyle() *DashboardV2alpha1ActionStyle {
|
||||
return &DashboardV2alpha1ActionStyle{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardRepeatOptionsDirection string
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList {
|
||||
}
|
||||
}
|
||||
|
||||
// Custom validation for action query params and headers
|
||||
validateAndTrimActionArrays(obj)
|
||||
|
||||
if err := cuejson.Validate(data, getCueSchema()); err != nil {
|
||||
errs := field.ErrorList{}
|
||||
|
||||
@@ -60,6 +63,61 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates and trims action query params and headers to exactly 2 elements each
|
||||
// This is because we couldn't generate with cue a go struct that would have exactly two strings in each sub-array
|
||||
func validateAndTrimActionArrays(obj *Dashboard) {
|
||||
for _, element := range obj.Spec.Elements {
|
||||
if element.PanelKind != nil {
|
||||
panelElement := element.PanelKind
|
||||
if panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil {
|
||||
processActions(panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to process action arrays
|
||||
func processActions(actions []DashboardAction) {
|
||||
for _, action := range actions {
|
||||
// Process FetchOptions if present
|
||||
if action.Fetch != nil {
|
||||
if action.Fetch.QueryParams != nil {
|
||||
action.Fetch.QueryParams = trimStringArrays(action.Fetch.QueryParams)
|
||||
}
|
||||
if action.Fetch.Headers != nil {
|
||||
action.Fetch.Headers = trimStringArrays(action.Fetch.Headers)
|
||||
}
|
||||
}
|
||||
|
||||
// Process InfinityOptions if present
|
||||
if action.Infinity != nil {
|
||||
if action.Infinity.QueryParams != nil {
|
||||
action.Infinity.QueryParams = trimStringArrays(action.Infinity.QueryParams)
|
||||
}
|
||||
if action.Infinity.Headers != nil {
|
||||
action.Infinity.Headers = trimStringArrays(action.Infinity.Headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to trim 2D string arrays to exactly 2 elements per sub-array
|
||||
func trimStringArrays(arrays [][]string) [][]string {
|
||||
if arrays == nil {
|
||||
return arrays
|
||||
}
|
||||
|
||||
result := make([][]string, len(arrays))
|
||||
for i, arr := range arrays {
|
||||
if len(arr) > 2 {
|
||||
result[i] = arr[:2]
|
||||
} else {
|
||||
result[i] = arr
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func formatErrorPath(path []string) string {
|
||||
return strings.Join(path, ".")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,495 @@
|
||||
package v2alpha1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestTrimStringArrays(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input [][]string
|
||||
expected [][]string
|
||||
}{
|
||||
{
|
||||
name: "nil input",
|
||||
input: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: [][]string{},
|
||||
expected: [][]string{},
|
||||
},
|
||||
{
|
||||
name: "arrays with exactly 2 elements",
|
||||
input: [][]string{{"key1", "value1"}, {"key2", "value2"}},
|
||||
expected: [][]string{{"key1", "value1"}, {"key2", "value2"}},
|
||||
},
|
||||
{
|
||||
name: "arrays with less than 2 elements",
|
||||
input: [][]string{{"key1"}, {}},
|
||||
expected: [][]string{{"key1"}, {}},
|
||||
},
|
||||
{
|
||||
name: "arrays with more than 2 elements",
|
||||
input: [][]string{{"key1", "value1", "extra1"}, {"key2", "value2", "extra2", "extra3"}},
|
||||
expected: [][]string{{"key1", "value1"}, {"key2", "value2"}},
|
||||
},
|
||||
{
|
||||
name: "mixed arrays",
|
||||
input: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3", "extra"}},
|
||||
expected: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := trimStringArrays(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessActions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
actions []DashboardAction
|
||||
expected []DashboardAction
|
||||
}{
|
||||
{
|
||||
name: "empty actions",
|
||||
actions: []DashboardAction{},
|
||||
expected: []DashboardAction{},
|
||||
},
|
||||
{
|
||||
name: "action with fetch options having oversized arrays",
|
||||
actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Fetch",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1", "extra1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1", "extra1", "extra2"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Fetch",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "action with infinity options having oversized arrays",
|
||||
actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeInfinity,
|
||||
Title: "Test Infinity",
|
||||
Infinity: &DashboardInfinityOptions{
|
||||
Method: DashboardHttpRequestMethodPOST,
|
||||
Url: "http://example.com",
|
||||
DatasourceUid: "test-uid",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1", "extra1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1", "extra1", "extra2"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeInfinity,
|
||||
Title: "Test Infinity",
|
||||
Infinity: &DashboardInfinityOptions{
|
||||
Method: DashboardHttpRequestMethodPOST,
|
||||
Url: "http://example.com",
|
||||
DatasourceUid: "test-uid",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "action without fetch or infinity options",
|
||||
actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
},
|
||||
},
|
||||
expected: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create a copy to avoid modifying the original
|
||||
actionsCopy := make([]DashboardAction, len(tt.actions))
|
||||
for i, action := range tt.actions {
|
||||
actionsCopy[i] = action
|
||||
// Deep copy the fetch options if they exist
|
||||
if action.Fetch != nil {
|
||||
fetchCopy := *action.Fetch
|
||||
if action.Fetch.QueryParams != nil {
|
||||
fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams))
|
||||
for j, param := range action.Fetch.QueryParams {
|
||||
fetchCopy.QueryParams[j] = make([]string, len(param))
|
||||
copy(fetchCopy.QueryParams[j], param)
|
||||
}
|
||||
}
|
||||
if action.Fetch.Headers != nil {
|
||||
fetchCopy.Headers = make([][]string, len(action.Fetch.Headers))
|
||||
for j, header := range action.Fetch.Headers {
|
||||
fetchCopy.Headers[j] = make([]string, len(header))
|
||||
copy(fetchCopy.Headers[j], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[i].Fetch = &fetchCopy
|
||||
}
|
||||
// Deep copy the infinity options if they exist
|
||||
if action.Infinity != nil {
|
||||
infinityCopy := *action.Infinity
|
||||
if action.Infinity.QueryParams != nil {
|
||||
infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams))
|
||||
for j, param := range action.Infinity.QueryParams {
|
||||
infinityCopy.QueryParams[j] = make([]string, len(param))
|
||||
copy(infinityCopy.QueryParams[j], param)
|
||||
}
|
||||
}
|
||||
if action.Infinity.Headers != nil {
|
||||
infinityCopy.Headers = make([][]string, len(action.Infinity.Headers))
|
||||
for j, header := range action.Infinity.Headers {
|
||||
infinityCopy.Headers[j] = make([]string, len(header))
|
||||
copy(infinityCopy.Headers[j], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[i].Infinity = &infinityCopy
|
||||
}
|
||||
}
|
||||
|
||||
processActions(actionsCopy)
|
||||
assert.Equal(t, tt.expected, actionsCopy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAndTrimActionArrays(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dashboard *Dashboard
|
||||
expected *Dashboard
|
||||
}{
|
||||
{
|
||||
name: "dashboard with no elements",
|
||||
dashboard: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{},
|
||||
},
|
||||
},
|
||||
expected: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with panel having actions with oversized arrays",
|
||||
dashboard: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1", "extra1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1", "extra1", "extra2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with panel having no actions",
|
||||
dashboard: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create a deep copy to avoid modifying the original
|
||||
dashboardCopy := deepCopyDashboard(tt.dashboard)
|
||||
|
||||
validateAndTrimActionArrays(dashboardCopy)
|
||||
assert.Equal(t, tt.expected, dashboardCopy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create a deep copy of a Dashboard for testing
|
||||
func deepCopyDashboard(original *Dashboard) *Dashboard {
|
||||
if original == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := &Dashboard{
|
||||
TypeMeta: original.TypeMeta,
|
||||
ObjectMeta: original.ObjectMeta,
|
||||
Spec: DashboardSpec{
|
||||
Elements: make(map[string]DashboardElement),
|
||||
},
|
||||
Status: original.Status,
|
||||
}
|
||||
|
||||
for key, element := range original.Spec.Elements {
|
||||
elementCopy := element
|
||||
|
||||
if element.PanelKind != nil {
|
||||
panelCopy := *element.PanelKind
|
||||
elementCopy.PanelKind = &panelCopy
|
||||
|
||||
if element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil {
|
||||
actions := element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions
|
||||
actionsCopy := make([]DashboardAction, len(actions))
|
||||
|
||||
for j, action := range actions {
|
||||
actionsCopy[j] = action
|
||||
|
||||
// Deep copy fetch options
|
||||
if action.Fetch != nil {
|
||||
fetchCopy := *action.Fetch
|
||||
if action.Fetch.QueryParams != nil {
|
||||
fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams))
|
||||
for k, param := range action.Fetch.QueryParams {
|
||||
fetchCopy.QueryParams[k] = make([]string, len(param))
|
||||
copy(fetchCopy.QueryParams[k], param)
|
||||
}
|
||||
}
|
||||
if action.Fetch.Headers != nil {
|
||||
fetchCopy.Headers = make([][]string, len(action.Fetch.Headers))
|
||||
for k, header := range action.Fetch.Headers {
|
||||
fetchCopy.Headers[k] = make([]string, len(header))
|
||||
copy(fetchCopy.Headers[k], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[j].Fetch = &fetchCopy
|
||||
}
|
||||
|
||||
// Deep copy infinity options
|
||||
if action.Infinity != nil {
|
||||
infinityCopy := *action.Infinity
|
||||
if action.Infinity.QueryParams != nil {
|
||||
infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams))
|
||||
for k, param := range action.Infinity.QueryParams {
|
||||
infinityCopy.QueryParams[k] = make([]string, len(param))
|
||||
copy(infinityCopy.QueryParams[k], param)
|
||||
}
|
||||
}
|
||||
if action.Infinity.Headers != nil {
|
||||
infinityCopy.Headers = make([][]string, len(action.Infinity.Headers))
|
||||
for k, header := range action.Infinity.Headers {
|
||||
infinityCopy.Headers[k] = make([]string, len(header))
|
||||
copy(infinityCopy.Headers[k], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[j].Infinity = &infinityCopy
|
||||
}
|
||||
}
|
||||
|
||||
elementCopy.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions = actionsCopy
|
||||
}
|
||||
}
|
||||
|
||||
if element.LibraryPanelKind != nil {
|
||||
libraryCopy := *element.LibraryPanelKind
|
||||
elementCopy.LibraryPanelKind = &libraryCopy
|
||||
}
|
||||
|
||||
result.Spec.Elements[key] = elementCopy
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -18,6 +18,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v2alpha1_AnnotationPermission(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.Dashboard": schema_pkg_apis_dashboard_v2alpha1_Dashboard(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAccess": schema_pkg_apis_dashboard_v2alpha1_DashboardAccess(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAction": schema_pkg_apis_dashboard_v2alpha1_DashboardAction(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardActionVariable": schema_pkg_apis_dashboard_v2alpha1_DashboardActionVariable(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAdHocFilterWithLabels": schema_pkg_apis_dashboard_v2alpha1_DashboardAdHocFilterWithLabels(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAdhocVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardAdhocVariableSpec(ref),
|
||||
@@ -52,6 +54,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDatasourceVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardDatasourceVariableSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDynamicConfigValue": schema_pkg_apis_dashboard_v2alpha1_DashboardDynamicConfigValue(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardElementReference": schema_pkg_apis_dashboard_v2alpha1_DashboardElementReference(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFetchOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardFetchOptions(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldColor": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldColor(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfigSource(ref),
|
||||
@@ -63,6 +66,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGridLayoutSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardInfinityOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardInfinityOptions(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableKind": schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardIntervalVariableSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardJSONCodec": schema_pkg_apis_dashboard_v2alpha1_DashboardJSONCodec(ref),
|
||||
@@ -109,6 +113,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeRangeOption(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeSettingsSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1ActionStyle": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1ActionStyle(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RangeMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RangeMapOptions(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RegexMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RegexMapOptions(ref),
|
||||
@@ -309,6 +314,109 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAccess(ref common.ReferenceCall
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardAction(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"fetch": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFetchOptions"),
|
||||
},
|
||||
},
|
||||
"infinity": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardInfinityOptions"),
|
||||
},
|
||||
},
|
||||
"confirmation": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"oneClick": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"variables": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardActionVariable"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"style": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1ActionStyle"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"type", "title"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardActionVariable", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFetchOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardInfinityOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1ActionStyle"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardActionVariable(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"key": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"key", "name", "type"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardAdHocFilterWithLabels(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -1805,6 +1913,82 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardElementReference(ref common.Ref
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardFetchOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"method": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"body": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"queryParams": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"method", "url"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldColor(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -1957,6 +2141,20 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref common.Referenc
|
||||
},
|
||||
},
|
||||
},
|
||||
"actions": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Define interactive HTTP requests that can be triggered from data visualizations.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAction"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"noValue": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Alternative to empty string",
|
||||
@@ -1983,7 +2181,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref common.Referenc
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"},
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAction", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2345,6 +2543,89 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardGroupByVariableSpec(ref common.
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardInfinityOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"method": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"body": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"queryParams": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"datasourceUid": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"headers": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"method", "url", "datasourceUid"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardIntervalVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -4337,6 +4618,24 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(ref common.R
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1ActionStyle(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"backgroundColor": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
+6
@@ -1,3 +1,4 @@
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAction,Variables
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdHocFilterWithLabels,ValueLabels
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdHocFilterWithLabels,Values
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,BaseFilters
|
||||
@@ -9,11 +10,16 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardCustomVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardDashboardLink,Tags
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardDatasourceVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFetchOptions,Headers
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFetchOptions,QueryParams
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Actions
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Links
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfig,Mappings
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardFieldConfigSource,Overrides
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGridLayoutSpec,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardGroupByVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardInfinityOptions,Headers
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardInfinityOptions,QueryParams
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardIntervalVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardMetadata,Finalizers
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardPanelSpec,Links
|
||||
|
||||
@@ -223,6 +223,9 @@ FieldConfig: {
|
||||
// The behavior when clicking on a result
|
||||
links?: [...]
|
||||
|
||||
// Define interactive HTTP requests that can be triggered from data visualizations.
|
||||
actions?: [...Action]
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string
|
||||
|
||||
@@ -366,6 +369,48 @@ FieldColor: {
|
||||
// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
|
||||
DashboardLinkType: "link" | "dashboards"
|
||||
|
||||
ActionType: "fetch" | "infinity"
|
||||
|
||||
FetchOptions: {
|
||||
method: HttpRequestMethod
|
||||
url: string
|
||||
body?: string
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
queryParams?: [...[...string]]
|
||||
headers?: [...[...string]]
|
||||
}
|
||||
|
||||
InfinityOptions: FetchOptions & {
|
||||
datasourceUid: string
|
||||
}
|
||||
|
||||
HttpRequestMethod: "GET" | "PUT" | "POST" | "DELETE" | "PATCH"
|
||||
|
||||
// Action variable type
|
||||
ActionVariableType: "string"
|
||||
|
||||
ActionVariable: {
|
||||
key: string
|
||||
name: string
|
||||
type: ActionVariableType
|
||||
}
|
||||
|
||||
Action: {
|
||||
type: ActionType
|
||||
title: string
|
||||
fetch?: FetchOptions
|
||||
infinity?: InfinityOptions
|
||||
confirmation?: string
|
||||
oneClick?: bool
|
||||
variables?: [...ActionVariable]
|
||||
style?: {
|
||||
backgroundColor?: string
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- Common types ---
|
||||
Kind: {
|
||||
kind: string
|
||||
|
||||
@@ -386,6 +386,8 @@ type DashboardFieldConfig struct {
|
||||
Color *DashboardFieldColor `json:"color,omitempty"`
|
||||
// The behavior when clicking on a result
|
||||
Links []interface{} `json:"links,omitempty"`
|
||||
// Define interactive HTTP requests that can be triggered from data visualizations.
|
||||
Actions []DashboardAction `json:"actions,omitempty"`
|
||||
// Alternative to empty string
|
||||
NoValue *string `json:"noValue,omitempty"`
|
||||
// custom is specified by the FieldConfig field
|
||||
@@ -617,6 +619,95 @@ const (
|
||||
DashboardFieldColorSeriesByModeLast DashboardFieldColorSeriesByMode = "last"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardAction struct {
|
||||
Type DashboardActionType `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Fetch *DashboardFetchOptions `json:"fetch,omitempty"`
|
||||
Infinity *DashboardInfinityOptions `json:"infinity,omitempty"`
|
||||
Confirmation *string `json:"confirmation,omitempty"`
|
||||
OneClick *bool `json:"oneClick,omitempty"`
|
||||
Variables []DashboardActionVariable `json:"variables,omitempty"`
|
||||
Style *DashboardV2beta1ActionStyle `json:"style,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardAction creates a new DashboardAction object.
|
||||
func NewDashboardAction() *DashboardAction {
|
||||
return &DashboardAction{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardActionType string
|
||||
|
||||
const (
|
||||
DashboardActionTypeFetch DashboardActionType = "fetch"
|
||||
DashboardActionTypeInfinity DashboardActionType = "infinity"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardFetchOptions struct {
|
||||
Method DashboardHttpRequestMethod `json:"method"`
|
||||
Url string `json:"url"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
QueryParams [][]string `json:"queryParams,omitempty"`
|
||||
Headers [][]string `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardFetchOptions creates a new DashboardFetchOptions object.
|
||||
func NewDashboardFetchOptions() *DashboardFetchOptions {
|
||||
return &DashboardFetchOptions{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardHttpRequestMethod string
|
||||
|
||||
const (
|
||||
DashboardHttpRequestMethodGET DashboardHttpRequestMethod = "GET"
|
||||
DashboardHttpRequestMethodPUT DashboardHttpRequestMethod = "PUT"
|
||||
DashboardHttpRequestMethodPOST DashboardHttpRequestMethod = "POST"
|
||||
DashboardHttpRequestMethodDELETE DashboardHttpRequestMethod = "DELETE"
|
||||
DashboardHttpRequestMethodPATCH DashboardHttpRequestMethod = "PATCH"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardInfinityOptions struct {
|
||||
Method DashboardHttpRequestMethod `json:"method"`
|
||||
Url string `json:"url"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
// These are 2D arrays of strings, each representing a key-value pair
|
||||
// We are defining them this way because we can't generate a go struct that
|
||||
// that would have exactly two strings in each sub-array
|
||||
QueryParams [][]string `json:"queryParams,omitempty"`
|
||||
DatasourceUid string `json:"datasourceUid"`
|
||||
Headers [][]string `json:"headers,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardInfinityOptions creates a new DashboardInfinityOptions object.
|
||||
func NewDashboardInfinityOptions() *DashboardInfinityOptions {
|
||||
return &DashboardInfinityOptions{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardActionVariable struct {
|
||||
Key string `json:"key"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// NewDashboardActionVariable creates a new DashboardActionVariable object.
|
||||
func NewDashboardActionVariable() *DashboardActionVariable {
|
||||
return &DashboardActionVariable{
|
||||
Type: DashboardActionVariableType,
|
||||
}
|
||||
}
|
||||
|
||||
// Action variable type
|
||||
// +k8s:openapi-gen=true
|
||||
const DashboardActionVariableType = "string"
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardDynamicConfigValue struct {
|
||||
Id string `json:"id"`
|
||||
@@ -1853,6 +1944,16 @@ func NewDashboardV2beta1SpecialValueMapOptions() *DashboardV2beta1SpecialValueMa
|
||||
}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardV2beta1ActionStyle struct {
|
||||
BackgroundColor *string `json:"backgroundColor,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardV2beta1ActionStyle creates a new DashboardV2beta1ActionStyle object.
|
||||
func NewDashboardV2beta1ActionStyle() *DashboardV2beta1ActionStyle {
|
||||
return &DashboardV2beta1ActionStyle{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardV2beta1GroupByVariableKindDatasource struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
|
||||
@@ -23,6 +23,9 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList {
|
||||
}
|
||||
}
|
||||
|
||||
// Custom validation for action query params and headers
|
||||
validateAndTrimActionArrays(obj)
|
||||
|
||||
if err := cuejson.Validate(data, getCueSchema()); err != nil {
|
||||
errs := field.ErrorList{}
|
||||
|
||||
@@ -60,6 +63,61 @@ func ValidateDashboardSpec(obj *Dashboard) field.ErrorList {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validates and trims action query params and headers to exactly 2 elements each
|
||||
// This is because we couldn't generate with cue a go struct that would have exactly two strings in each sub-array
|
||||
func validateAndTrimActionArrays(obj *Dashboard) {
|
||||
for _, element := range obj.Spec.Elements {
|
||||
if element.PanelKind != nil {
|
||||
panelElement := element.PanelKind
|
||||
if panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil {
|
||||
processActions(panelElement.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to process action arrays
|
||||
func processActions(actions []DashboardAction) {
|
||||
for _, action := range actions {
|
||||
// Process FetchOptions if present
|
||||
if action.Fetch != nil {
|
||||
if action.Fetch.QueryParams != nil {
|
||||
action.Fetch.QueryParams = trimStringArrays(action.Fetch.QueryParams)
|
||||
}
|
||||
if action.Fetch.Headers != nil {
|
||||
action.Fetch.Headers = trimStringArrays(action.Fetch.Headers)
|
||||
}
|
||||
}
|
||||
|
||||
// Process InfinityOptions if present
|
||||
if action.Infinity != nil {
|
||||
if action.Infinity.QueryParams != nil {
|
||||
action.Infinity.QueryParams = trimStringArrays(action.Infinity.QueryParams)
|
||||
}
|
||||
if action.Infinity.Headers != nil {
|
||||
action.Infinity.Headers = trimStringArrays(action.Infinity.Headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to trim 2D string arrays to exactly 2 elements per sub-array
|
||||
func trimStringArrays(arrays [][]string) [][]string {
|
||||
if arrays == nil {
|
||||
return arrays
|
||||
}
|
||||
|
||||
result := make([][]string, len(arrays))
|
||||
for i, arr := range arrays {
|
||||
if len(arr) > 2 {
|
||||
result[i] = arr[:2]
|
||||
} else {
|
||||
result[i] = arr
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func formatErrorPath(path []string) string {
|
||||
return strings.Join(path, ".")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,495 @@
|
||||
package v2beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestTrimStringArrays(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input [][]string
|
||||
expected [][]string
|
||||
}{
|
||||
{
|
||||
name: "nil input",
|
||||
input: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: [][]string{},
|
||||
expected: [][]string{},
|
||||
},
|
||||
{
|
||||
name: "arrays with exactly 2 elements",
|
||||
input: [][]string{{"key1", "value1"}, {"key2", "value2"}},
|
||||
expected: [][]string{{"key1", "value1"}, {"key2", "value2"}},
|
||||
},
|
||||
{
|
||||
name: "arrays with less than 2 elements",
|
||||
input: [][]string{{"key1"}, {}},
|
||||
expected: [][]string{{"key1"}, {}},
|
||||
},
|
||||
{
|
||||
name: "arrays with more than 2 elements",
|
||||
input: [][]string{{"key1", "value1", "extra1"}, {"key2", "value2", "extra2", "extra3"}},
|
||||
expected: [][]string{{"key1", "value1"}, {"key2", "value2"}},
|
||||
},
|
||||
{
|
||||
name: "mixed arrays",
|
||||
input: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3", "extra"}},
|
||||
expected: [][]string{{"key1"}, {"key2", "value2"}, {"key3", "value3"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := trimStringArrays(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessActions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
actions []DashboardAction
|
||||
expected []DashboardAction
|
||||
}{
|
||||
{
|
||||
name: "empty actions",
|
||||
actions: []DashboardAction{},
|
||||
expected: []DashboardAction{},
|
||||
},
|
||||
{
|
||||
name: "action with fetch options having oversized arrays",
|
||||
actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Fetch",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1", "extra1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1", "extra1", "extra2"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Fetch",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "action with infinity options having oversized arrays",
|
||||
actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeInfinity,
|
||||
Title: "Test Infinity",
|
||||
Infinity: &DashboardInfinityOptions{
|
||||
Method: DashboardHttpRequestMethodPOST,
|
||||
Url: "http://example.com",
|
||||
DatasourceUid: "test-uid",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1", "extra1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1", "extra1", "extra2"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeInfinity,
|
||||
Title: "Test Infinity",
|
||||
Infinity: &DashboardInfinityOptions{
|
||||
Method: DashboardHttpRequestMethodPOST,
|
||||
Url: "http://example.com",
|
||||
DatasourceUid: "test-uid",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1"},
|
||||
{"header2", "value2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "action without fetch or infinity options",
|
||||
actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
},
|
||||
},
|
||||
expected: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create a copy to avoid modifying the original
|
||||
actionsCopy := make([]DashboardAction, len(tt.actions))
|
||||
for i, action := range tt.actions {
|
||||
actionsCopy[i] = action
|
||||
// Deep copy the fetch options if they exist
|
||||
if action.Fetch != nil {
|
||||
fetchCopy := *action.Fetch
|
||||
if action.Fetch.QueryParams != nil {
|
||||
fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams))
|
||||
for j, param := range action.Fetch.QueryParams {
|
||||
fetchCopy.QueryParams[j] = make([]string, len(param))
|
||||
copy(fetchCopy.QueryParams[j], param)
|
||||
}
|
||||
}
|
||||
if action.Fetch.Headers != nil {
|
||||
fetchCopy.Headers = make([][]string, len(action.Fetch.Headers))
|
||||
for j, header := range action.Fetch.Headers {
|
||||
fetchCopy.Headers[j] = make([]string, len(header))
|
||||
copy(fetchCopy.Headers[j], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[i].Fetch = &fetchCopy
|
||||
}
|
||||
// Deep copy the infinity options if they exist
|
||||
if action.Infinity != nil {
|
||||
infinityCopy := *action.Infinity
|
||||
if action.Infinity.QueryParams != nil {
|
||||
infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams))
|
||||
for j, param := range action.Infinity.QueryParams {
|
||||
infinityCopy.QueryParams[j] = make([]string, len(param))
|
||||
copy(infinityCopy.QueryParams[j], param)
|
||||
}
|
||||
}
|
||||
if action.Infinity.Headers != nil {
|
||||
infinityCopy.Headers = make([][]string, len(action.Infinity.Headers))
|
||||
for j, header := range action.Infinity.Headers {
|
||||
infinityCopy.Headers[j] = make([]string, len(header))
|
||||
copy(infinityCopy.Headers[j], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[i].Infinity = &infinityCopy
|
||||
}
|
||||
}
|
||||
|
||||
processActions(actionsCopy)
|
||||
assert.Equal(t, tt.expected, actionsCopy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAndTrimActionArrays(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dashboard *Dashboard
|
||||
expected *Dashboard
|
||||
}{
|
||||
{
|
||||
name: "dashboard with no elements",
|
||||
dashboard: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{},
|
||||
},
|
||||
},
|
||||
expected: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with panel having actions with oversized arrays",
|
||||
dashboard: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1", "extra1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1", "extra1", "extra2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: []DashboardAction{
|
||||
{
|
||||
Type: DashboardActionTypeFetch,
|
||||
Title: "Test Action",
|
||||
Fetch: &DashboardFetchOptions{
|
||||
Method: DashboardHttpRequestMethodGET,
|
||||
Url: "http://example.com",
|
||||
QueryParams: [][]string{
|
||||
{"param1", "value1"},
|
||||
{"param2", "value2"},
|
||||
},
|
||||
Headers: [][]string{
|
||||
{"header1", "value1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with panel having no actions",
|
||||
dashboard: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Dashboard",
|
||||
APIVersion: "v2beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dashboard",
|
||||
},
|
||||
Spec: DashboardSpec{
|
||||
Elements: map[string]DashboardElement{
|
||||
"panel1": {
|
||||
PanelKind: &DashboardPanelKind{
|
||||
Spec: DashboardPanelSpec{
|
||||
VizConfig: DashboardVizConfigKind{
|
||||
Spec: DashboardVizConfigSpec{
|
||||
FieldConfig: DashboardFieldConfigSource{
|
||||
Defaults: DashboardFieldConfig{
|
||||
Actions: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create a deep copy to avoid modifying the original
|
||||
dashboardCopy := deepCopyDashboard(tt.dashboard)
|
||||
|
||||
validateAndTrimActionArrays(dashboardCopy)
|
||||
assert.Equal(t, tt.expected, dashboardCopy)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to create a deep copy of a Dashboard for testing
|
||||
func deepCopyDashboard(original *Dashboard) *Dashboard {
|
||||
if original == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := &Dashboard{
|
||||
TypeMeta: original.TypeMeta,
|
||||
ObjectMeta: original.ObjectMeta,
|
||||
Spec: DashboardSpec{
|
||||
Elements: make(map[string]DashboardElement),
|
||||
},
|
||||
Status: original.Status,
|
||||
}
|
||||
|
||||
for key, element := range original.Spec.Elements {
|
||||
elementCopy := element
|
||||
|
||||
if element.PanelKind != nil {
|
||||
panelCopy := *element.PanelKind
|
||||
elementCopy.PanelKind = &panelCopy
|
||||
|
||||
if element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions != nil {
|
||||
actions := element.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions
|
||||
actionsCopy := make([]DashboardAction, len(actions))
|
||||
|
||||
for j, action := range actions {
|
||||
actionsCopy[j] = action
|
||||
|
||||
// Deep copy fetch options
|
||||
if action.Fetch != nil {
|
||||
fetchCopy := *action.Fetch
|
||||
if action.Fetch.QueryParams != nil {
|
||||
fetchCopy.QueryParams = make([][]string, len(action.Fetch.QueryParams))
|
||||
for k, param := range action.Fetch.QueryParams {
|
||||
fetchCopy.QueryParams[k] = make([]string, len(param))
|
||||
copy(fetchCopy.QueryParams[k], param)
|
||||
}
|
||||
}
|
||||
if action.Fetch.Headers != nil {
|
||||
fetchCopy.Headers = make([][]string, len(action.Fetch.Headers))
|
||||
for k, header := range action.Fetch.Headers {
|
||||
fetchCopy.Headers[k] = make([]string, len(header))
|
||||
copy(fetchCopy.Headers[k], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[j].Fetch = &fetchCopy
|
||||
}
|
||||
|
||||
// Deep copy infinity options
|
||||
if action.Infinity != nil {
|
||||
infinityCopy := *action.Infinity
|
||||
if action.Infinity.QueryParams != nil {
|
||||
infinityCopy.QueryParams = make([][]string, len(action.Infinity.QueryParams))
|
||||
for k, param := range action.Infinity.QueryParams {
|
||||
infinityCopy.QueryParams[k] = make([]string, len(param))
|
||||
copy(infinityCopy.QueryParams[k], param)
|
||||
}
|
||||
}
|
||||
if action.Infinity.Headers != nil {
|
||||
infinityCopy.Headers = make([][]string, len(action.Infinity.Headers))
|
||||
for k, header := range action.Infinity.Headers {
|
||||
infinityCopy.Headers[k] = make([]string, len(header))
|
||||
copy(infinityCopy.Headers[k], header)
|
||||
}
|
||||
}
|
||||
actionsCopy[j].Infinity = &infinityCopy
|
||||
}
|
||||
}
|
||||
|
||||
elementCopy.PanelKind.Spec.VizConfig.Spec.FieldConfig.Defaults.Actions = actionsCopy
|
||||
}
|
||||
}
|
||||
|
||||
if element.LibraryPanelKind != nil {
|
||||
libraryCopy := *element.LibraryPanelKind
|
||||
elementCopy.LibraryPanelKind = &libraryCopy
|
||||
}
|
||||
|
||||
result.Spec.Elements[key] = elementCopy
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -18,6 +18,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.AnnotationPermission": schema_pkg_apis_dashboard_v2beta1_AnnotationPermission(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.Dashboard": schema_pkg_apis_dashboard_v2beta1_Dashboard(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAccess": schema_pkg_apis_dashboard_v2beta1_DashboardAccess(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction": schema_pkg_apis_dashboard_v2beta1_DashboardAction(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable": schema_pkg_apis_dashboard_v2beta1_DashboardActionVariable(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdHocFilterWithLabels": schema_pkg_apis_dashboard_v2beta1_DashboardAdHocFilterWithLabels(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableSpec(ref),
|
||||
@@ -51,6 +53,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardDatasourceVariableSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDynamicConfigValue": schema_pkg_apis_dashboard_v2beta1_DashboardDynamicConfigValue(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardElementReference": schema_pkg_apis_dashboard_v2beta1_DashboardElementReference(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions": schema_pkg_apis_dashboard_v2beta1_DashboardFetchOptions(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor": schema_pkg_apis_dashboard_v2beta1_DashboardFieldColor(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfigSource(ref),
|
||||
@@ -62,6 +65,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions": schema_pkg_apis_dashboard_v2beta1_DashboardInfinityOptions(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardJSONCodec": schema_pkg_apis_dashboard_v2beta1_DashboardJSONCodec(ref),
|
||||
@@ -108,6 +112,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2beta1_DashboardTimeRangeOption(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTimeSettingsSpec(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2beta1_DashboardTransformationKind(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1ActionStyle(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1AdhocVariableKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1AdhocVariableKindDatasource(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1DataQueryKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1DataQueryKindDatasource(ref),
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1FieldConfigSourceOverrides(ref),
|
||||
@@ -311,6 +316,109 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardAccess(ref common.ReferenceCallb
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardAction(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"fetch": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions"),
|
||||
},
|
||||
},
|
||||
"infinity": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions"),
|
||||
},
|
||||
},
|
||||
"confirmation": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"oneClick": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"variables": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"style": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"type", "title"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardActionVariable(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"key": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"key", "name", "type"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardAdHocFilterWithLabels(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -1836,6 +1944,82 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardElementReference(ref common.Refe
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardFetchOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"method": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"body": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"queryParams": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"headers": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"method", "url"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardFieldColor(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -1988,6 +2172,20 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref common.Reference
|
||||
},
|
||||
},
|
||||
},
|
||||
"actions": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Define interactive HTTP requests that can be triggered from data visualizations.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"noValue": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Alternative to empty string",
|
||||
@@ -2014,7 +2212,7 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref common.Reference
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"},
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThresholdsConfig", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2389,6 +2587,89 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableSpec(ref common.R
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardInfinityOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"method": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"body": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"queryParams": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"datasourceUid": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"headers": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"method", "url", "datasourceUid"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -4389,6 +4670,24 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardTransformationKind(ref common.Re
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1ActionStyle(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"backgroundColor": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1AdhocVariableKindDatasource(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
+6
@@ -1,3 +1,4 @@
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAction,Variables
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAdHocFilterWithLabels,ValueLabels
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAdHocFilterWithLabels,Values
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardAdhocVariableSpec,BaseFilters
|
||||
@@ -9,11 +10,16 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardCustomVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardDashboardLink,Tags
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardDatasourceVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFetchOptions,Headers
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFetchOptions,QueryParams
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfig,Actions
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfig,Links
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfig,Mappings
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardFieldConfigSource,Overrides
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardGridLayoutSpec,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardGroupByVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardInfinityOptions,Headers
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardInfinityOptions,QueryParams
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardIntervalVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardMetadata,Finalizers
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardPanelSpec,Links
|
||||
|
||||
Reference in New Issue
Block a user