mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
schema: Migrate from scuemata to thema (#49805)
* Remove crufty scuemata bits Buhbye to: cue/ dir with old definitions, CI steps for checking unnecessary things, and the original dashboard scuemata file. * Remove grafana-cli cue subcommand * Remove old testdata * Don't swallow errors from codegen * Small nits and tweaks to cuectx package * WIP - refactor pluggen to use Thema Also consolidate the embed.FS in the repo root. * Finish halfway rename * Convert all panel models.cue to thema * Rewrite pluggen to use Thema * Remove pkg/schema, and trim command * Remove schemaloader service and usages Will be replaced by coremodel-centric hydrate/dehydrate system Soon™. * Remove schemaloader from wire * Remove hangover field on histogram models.cue * Fix lint errors, some vestiges of trim service * Remove unused cuetsify cli command
This commit is contained in:
parent
e7d6a58037
commit
8876d56495
116
.drone.yml
116
.drone.yml
@ -245,25 +245,6 @@ steps:
|
||||
environment: null
|
||||
image: grafana/build-container:1.5.5
|
||||
name: build-plugins
|
||||
- commands:
|
||||
- ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root .
|
||||
depends_on:
|
||||
- build-backend
|
||||
image: grafana/build-container:1.5.5
|
||||
name: validate-scuemata
|
||||
- commands:
|
||||
- '# It is required that the generated Typescript be in sync with the input CUE
|
||||
files.'
|
||||
- '# To enforce this, the following command will attempt to generate Typescript
|
||||
from all'
|
||||
- '# appropriate .cue files, then compare with the corresponding (*.gen.ts) file
|
||||
the generated'
|
||||
- '# code would have been written to. It exits 1 if any diffs are found.'
|
||||
- ./bin/linux-amd64/grafana-cli cue gen-ts --grafana-root . --diff
|
||||
depends_on:
|
||||
- validate-scuemata
|
||||
image: grafana/build-container:1.5.5
|
||||
name: ensure-cuetsified
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
with its inputs.'
|
||||
@ -940,25 +921,6 @@ steps:
|
||||
from_secret: grafana_api_key
|
||||
image: grafana/build-container:1.5.5
|
||||
name: build-plugins
|
||||
- commands:
|
||||
- ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root .
|
||||
depends_on:
|
||||
- build-backend
|
||||
image: grafana/build-container:1.5.5
|
||||
name: validate-scuemata
|
||||
- commands:
|
||||
- '# It is required that the generated Typescript be in sync with the input CUE
|
||||
files.'
|
||||
- '# To enforce this, the following command will attempt to generate Typescript
|
||||
from all'
|
||||
- '# appropriate .cue files, then compare with the corresponding (*.gen.ts) file
|
||||
the generated'
|
||||
- '# code would have been written to. It exits 1 if any diffs are found.'
|
||||
- ./bin/linux-amd64/grafana-cli cue gen-ts --grafana-root . --diff
|
||||
depends_on:
|
||||
- validate-scuemata
|
||||
image: grafana/build-container:1.5.5
|
||||
name: ensure-cuetsified
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
with its inputs.'
|
||||
@ -1588,25 +1550,6 @@ steps:
|
||||
from_secret: grafana_api_key
|
||||
image: grafana/build-container:1.5.5
|
||||
name: build-plugins
|
||||
- commands:
|
||||
- ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root .
|
||||
depends_on:
|
||||
- build-backend
|
||||
image: grafana/build-container:1.5.5
|
||||
name: validate-scuemata
|
||||
- commands:
|
||||
- '# It is required that the generated Typescript be in sync with the input CUE
|
||||
files.'
|
||||
- '# To enforce this, the following command will attempt to generate Typescript
|
||||
from all'
|
||||
- '# appropriate .cue files, then compare with the corresponding (*.gen.ts) file
|
||||
the generated'
|
||||
- '# code would have been written to. It exits 1 if any diffs are found.'
|
||||
- ./bin/linux-amd64/grafana-cli cue gen-ts --grafana-root . --diff
|
||||
depends_on:
|
||||
- validate-scuemata
|
||||
image: grafana/build-container:1.5.5
|
||||
name: ensure-cuetsified
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
with its inputs.'
|
||||
@ -2193,25 +2136,6 @@ steps:
|
||||
from_secret: grafana_api_key
|
||||
image: grafana/build-container:1.5.5
|
||||
name: build-plugins
|
||||
- commands:
|
||||
- ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root .
|
||||
depends_on:
|
||||
- build-backend
|
||||
image: grafana/build-container:1.5.5
|
||||
name: validate-scuemata
|
||||
- commands:
|
||||
- '# It is required that the generated Typescript be in sync with the input CUE
|
||||
files.'
|
||||
- '# To enforce this, the following command will attempt to generate Typescript
|
||||
from all'
|
||||
- '# appropriate .cue files, then compare with the corresponding (*.gen.ts) file
|
||||
the generated'
|
||||
- '# code would have been written to. It exits 1 if any diffs are found.'
|
||||
- ./bin/linux-amd64/grafana-cli cue gen-ts --grafana-root . --diff
|
||||
depends_on:
|
||||
- validate-scuemata
|
||||
image: grafana/build-container:1.5.5
|
||||
name: ensure-cuetsified
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
with its inputs.'
|
||||
@ -3414,25 +3338,6 @@ steps:
|
||||
from_secret: grafana_api_key
|
||||
image: grafana/build-container:1.5.5
|
||||
name: build-plugins
|
||||
- commands:
|
||||
- ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root .
|
||||
depends_on:
|
||||
- build-backend
|
||||
image: grafana/build-container:1.5.5
|
||||
name: validate-scuemata
|
||||
- commands:
|
||||
- '# It is required that the generated Typescript be in sync with the input CUE
|
||||
files.'
|
||||
- '# To enforce this, the following command will attempt to generate Typescript
|
||||
from all'
|
||||
- '# appropriate .cue files, then compare with the corresponding (*.gen.ts) file
|
||||
the generated'
|
||||
- '# code would have been written to. It exits 1 if any diffs are found.'
|
||||
- ./bin/linux-amd64/grafana-cli cue gen-ts --grafana-root . --diff
|
||||
depends_on:
|
||||
- validate-scuemata
|
||||
image: grafana/build-container:1.5.5
|
||||
name: ensure-cuetsified
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
with its inputs.'
|
||||
@ -3964,25 +3869,6 @@ steps:
|
||||
from_secret: grafana_api_key
|
||||
image: grafana/build-container:1.5.5
|
||||
name: build-plugins
|
||||
- commands:
|
||||
- ./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root .
|
||||
depends_on:
|
||||
- build-backend
|
||||
image: grafana/build-container:1.5.5
|
||||
name: validate-scuemata
|
||||
- commands:
|
||||
- '# It is required that the generated Typescript be in sync with the input CUE
|
||||
files.'
|
||||
- '# To enforce this, the following command will attempt to generate Typescript
|
||||
from all'
|
||||
- '# appropriate .cue files, then compare with the corresponding (*.gen.ts) file
|
||||
the generated'
|
||||
- '# code would have been written to. It exits 1 if any diffs are found.'
|
||||
- ./bin/linux-amd64/grafana-cli cue gen-ts --grafana-root . --diff
|
||||
depends_on:
|
||||
- validate-scuemata
|
||||
image: grafana/build-container:1.5.5
|
||||
name: ensure-cuetsified
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
with its inputs.'
|
||||
@ -4725,6 +4611,6 @@ kind: secret
|
||||
name: gcp_upload_artifacts_key
|
||||
---
|
||||
kind: signature
|
||||
hmac: ee50d41dc69f9e424ac31a7a3e282c913fbe182903d10aebe2df2acf8da6c9d8
|
||||
hmac: d2e97d6683c33ccb2dba773e3c103105227a74448fa732124e4c38d67eb464d8
|
||||
|
||||
...
|
||||
|
@ -1,27 +0,0 @@
|
||||
package scuemata
|
||||
|
||||
// Definition of the shape of a panel plugin's schema declarations in its
|
||||
// schema.cue file.
|
||||
//
|
||||
// Note that these keys do not appear directly in any real JSON artifact;
|
||||
// rather, they are composed into panel structures as they are defined within
|
||||
// the larger Dashboard schema.
|
||||
#PanelSchema: {
|
||||
// Defines plugin specific options for a panel
|
||||
PanelOptions: {...} @cuetsy(kind="interface")
|
||||
|
||||
// Define the custom properties that exist within standard field config
|
||||
PanelFieldConfig?: {...} @cuetsy(kind="interface")
|
||||
|
||||
// Panels may define their own types
|
||||
...
|
||||
}
|
||||
|
||||
// A lineage of panel schema
|
||||
#PanelLineage: [#PanelSchema, ...#PanelSchema]
|
||||
|
||||
// Panel plugin-specific Family
|
||||
#PanelFamily: {
|
||||
lineages: [#PanelLineage, ...#PanelLineage]
|
||||
migrations: [...#Migration]
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package scuemata
|
||||
|
||||
// A family is a collection of schemas that specify a single kind of object,
|
||||
// allowing evolution of the canonical schema for that kind of object over time.
|
||||
//
|
||||
// The schemas are organized into a list of Lineages, which are themselves ordered
|
||||
// lists of schemas where each schema with its predecessor in the lineage.
|
||||
//
|
||||
// If it is desired to define a schema with a breaking schema relative to its
|
||||
// predecessors, a new Lineage must be created, as well as a Migration that defines
|
||||
// a mapping to the new schema from the latest schema in prior Lineage.
|
||||
//
|
||||
// The version number of a schema is not controlled by the schema itself, but by
|
||||
// its position in the list of lineages - e.g., 0.0 corresponds to the first
|
||||
// schema in the first lineage.
|
||||
#Family: {
|
||||
compose?: {...}
|
||||
lineages: [#Lineage, ...#Lineage]
|
||||
migrations: [...#Migration]
|
||||
let lseq = lineages[len(lineages)-1]
|
||||
latest: #LastSchema & {_p: lseq}
|
||||
}
|
||||
|
||||
// A Lineage is a non-empty list containing an ordered series of schemas that
|
||||
// all describe a single kind of object, where each schema is backwards
|
||||
// compatible with its predecessor.
|
||||
#Lineage: [{...}, ...{...}]
|
||||
|
||||
#LastSchema: {
|
||||
_p: #Lineage
|
||||
_p[len(_p)-1]
|
||||
}
|
||||
|
||||
// A Migration defines a relation between two schemas, "_from" and "_to". The
|
||||
// relation expresses any complex mappings that must be performed to
|
||||
// transform an input artifact valid with respect to the _from schema, into
|
||||
// an artifact valid with respect to the _to schema. This is accomplished
|
||||
// in two stages:
|
||||
// 1. A Migration is initially defined by passing in schemas for _from and _to,
|
||||
// and mappings that translate _from to _to are defined in _rel.
|
||||
// 2. A concrete object may then be unified with _to, resulting in its values
|
||||
// being mapped onto "result" by way of _rel.
|
||||
//
|
||||
// This is the absolute simplest possible definition of a Migration. It's
|
||||
// incumbent on the implementor to manually ensure the correctness and
|
||||
// completeness of the mapping. The primary value in defining such a generic
|
||||
// structure is to allow comparably generic logic for migrating concrete
|
||||
// artifacts through schema changes.
|
||||
//
|
||||
// If _to isn't backwards compatible (accretion-only) with _from, then _rel must
|
||||
// explicitly enumerate every field in _from and map it to a field in _to, even
|
||||
// if they're identical. This is laborious for anything outside trivially tiny
|
||||
// schema. We'll want to eventually add helpers for whitelisting or blacklisting
|
||||
// of paths in _from, so that migrations of larger schema can focus narrowly on
|
||||
// the points of actual change.
|
||||
#Migration: {
|
||||
from: {...}
|
||||
to: {...}
|
||||
rel: {...}
|
||||
result: to & rel
|
||||
}
|
12
embed.go
12
embed.go
@ -4,13 +4,7 @@ import (
|
||||
"embed"
|
||||
)
|
||||
|
||||
// CoreSchema embeds all core CUE files, which live in packages/grafana-schema/src
|
||||
// CueSchemaFS embeds all schema-related CUE files in the Grafana project.
|
||||
//
|
||||
//go:embed cue.mod cue packages/grafana-schema/src/schema/*.cue packages/grafana-schema/src/scuemata/*/*.cue packages/grafana-schema/src/scuemata/*/*/*.cue
|
||||
var CoreSchema embed.FS
|
||||
|
||||
// PluginSchema embeds all expected plugin CUE files and plugin metadata from
|
||||
// within the public/app/plugins subdirectory.
|
||||
//
|
||||
//go:embed public/app/plugins/*/*/*.cue public/app/plugins/*/*/plugin.json
|
||||
var PluginSchema embed.FS
|
||||
//go:embed cue.mod packages/grafana-schema/src/schema/*.cue public/app/plugins/*/*/*.cue public/app/plugins/*/*/plugin.json
|
||||
var CueSchemaFS embed.FS
|
||||
|
@ -1,441 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"list"
|
||||
|
||||
"github.com/grafana/grafana/cue/scuemata"
|
||||
)
|
||||
|
||||
Family: scuemata.#Family & {
|
||||
lineages: [
|
||||
[
|
||||
{// 0.0
|
||||
// Unique numeric identifier for the dashboard.
|
||||
// TODO must isolate or remove identifiers local to a Grafana instance...?
|
||||
id?: number
|
||||
// Unique dashboard identifier that can be generated by anyone. string (8-40)
|
||||
uid?: string
|
||||
// Title of dashboard.
|
||||
title?: string
|
||||
// Description of dashboard.
|
||||
description?: string
|
||||
|
||||
gnetId?: string
|
||||
// Tags associated with dashboard.
|
||||
tags?: [...string]
|
||||
// Theme of dashboard.
|
||||
style: *"light" | "dark"
|
||||
// Timezone of dashboard,
|
||||
timezone?: *"browser" | "utc" | ""
|
||||
// Whether a dashboard is editable or not.
|
||||
editable: bool | *true
|
||||
// 0 for no shared crosshair or tooltip (default).
|
||||
// 1 for shared crosshair.
|
||||
// 2 for shared crosshair AND shared tooltip.
|
||||
graphTooltip: >=0 & <=2 | *0
|
||||
// Time range for dashboard, e.g. last 6 hours, last 7 days, etc
|
||||
time?: {
|
||||
from: string | *"now-6h"
|
||||
to: string | *"now"
|
||||
}
|
||||
// Timepicker metadata.
|
||||
timepicker?: {
|
||||
// Whether timepicker is collapsed or not.
|
||||
collapse: bool | *false
|
||||
// Whether timepicker is enabled or not.
|
||||
enable: bool | *true
|
||||
// Whether timepicker is visible or not.
|
||||
hidden: bool | *false
|
||||
// Selectable intervals for auto-refresh.
|
||||
refresh_intervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
||||
}
|
||||
// Templating.
|
||||
templating?: list: [...{...}]
|
||||
// Annotations.
|
||||
annotations?: list: [...{
|
||||
builtIn: number | *0
|
||||
// Datasource to use for annotation.
|
||||
datasource: {
|
||||
type?: string
|
||||
uid?: string
|
||||
}
|
||||
// Whether annotation is enabled.
|
||||
enable: bool | *true
|
||||
// Whether to hide annotation.
|
||||
hide?: bool | *false
|
||||
// Annotation icon color.
|
||||
iconColor?: string
|
||||
// Name of annotation.
|
||||
name?: string
|
||||
type: string | *"dashboard"
|
||||
// Query for annotation data.
|
||||
rawQuery?: string
|
||||
showIn: number | *0
|
||||
}]
|
||||
// Auto-refresh interval.
|
||||
refresh?: string | false
|
||||
// Version of the JSON schema, incremented each time a Grafana update brings
|
||||
// changes to said schema.
|
||||
// FIXME this is the old schema numbering system, and will be replaced by scuemata
|
||||
schemaVersion: number | *30
|
||||
// Version of the dashboard, incremented each time the dashboard is updated.
|
||||
version?: number
|
||||
panels?: [...(#Panel | #GraphPanel | #RowPanel)]
|
||||
|
||||
// TODO docs
|
||||
#FieldColorModeId: "thresholds" | "palette-classic" | "palette-saturated" | "continuous-GrYlRd" | "fixed" @cuetsy(kind="enum")
|
||||
|
||||
// TODO docs
|
||||
#FieldColorSeriesByMode: "min" | "max" | "last" @cuetsy(kind="type")
|
||||
|
||||
// TODO docs
|
||||
#FieldColor: {
|
||||
// The main color scheme mode
|
||||
mode: #FieldColorModeId | string
|
||||
// Stores the fixed color value if mode is fixed
|
||||
fixedColor?: string
|
||||
// Some visualizations need to know how to assign a series color from by value color schemes
|
||||
seriesBy?: #FieldColorSeriesByMode
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// TODO docs
|
||||
#Threshold: {
|
||||
// TODO docs
|
||||
// FIXME the corresponding typescript field is required/non-optional, but nulls currently appear here when serializing -Infinity to JSON
|
||||
value?: number
|
||||
// TODO docs
|
||||
color: string
|
||||
// TODO docs
|
||||
// TODO are the values here enumerable into a disjunction?
|
||||
// Some seem to be listed in typescript comment
|
||||
state?: string
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#ThresholdsMode: "absolute" | "percentage" @cuetsy(kind="enum")
|
||||
|
||||
#ThresholdsConfig: {
|
||||
mode: #ThresholdsMode
|
||||
|
||||
// Must be sorted by 'value', first value is always -Infinity
|
||||
steps: [...#Threshold]
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// TODO docs
|
||||
// FIXME this is extremely underspecfied; wasn't obvious which typescript types corresponded to it
|
||||
#Transformation: {
|
||||
id: string
|
||||
options: {...}
|
||||
}
|
||||
|
||||
// Schema for panel targets is specified by datasource
|
||||
// plugins. We use a placeholder definition, which the Go
|
||||
// schema loader either left open/as-is with the Base
|
||||
// variant of the Dashboard and Panel families, or filled
|
||||
// with types derived from plugins in the Instance variant.
|
||||
// When working directly from CUE, importers can extend this
|
||||
// type directly to achieve the same effect.
|
||||
#Target: {...}
|
||||
|
||||
// Dashboard panels. Panels are canonically defined inline
|
||||
// because they share a version timeline with the dashboard
|
||||
// schema; they do not evolve independently.
|
||||
#Panel: {
|
||||
// The panel plugin type id.
|
||||
type: !=""
|
||||
|
||||
// TODO docs
|
||||
id?: number
|
||||
|
||||
// FIXME this almost certainly has to be changed in favor of scuemata versions
|
||||
pluginVersion?: string
|
||||
|
||||
// TODO docs
|
||||
tags?: [...string]
|
||||
|
||||
// Internal - the exact major and minor versions of the panel plugin
|
||||
// schema. Hidden and therefore not a part of the data model, but
|
||||
// expected to be filled with panel plugin schema versions so that it's
|
||||
// possible to figure out which schema version matched on a successful
|
||||
// unification.
|
||||
// _pv: { maj: int, min: int }
|
||||
// The major and minor versions of the panel plugin for this schema.
|
||||
// TODO 2-tuple list instead of struct?
|
||||
// panelSchema?: { maj: number, min: number }
|
||||
panelSchema?: [number, number]
|
||||
|
||||
// TODO docs
|
||||
targets?: [...#Target]
|
||||
|
||||
// Panel title.
|
||||
title?: string
|
||||
// Description.
|
||||
description?: string
|
||||
// Whether to display the panel without a background.
|
||||
transparent: bool | *false
|
||||
// The datasource used in all targets.
|
||||
datasource?: {
|
||||
type?: string
|
||||
uid?: string
|
||||
}
|
||||
// Grid position.
|
||||
gridPos?: {
|
||||
// Panel
|
||||
h: number & >0 | *9
|
||||
// Panel
|
||||
w: number & >0 & <=24 | *12
|
||||
// Panel x
|
||||
x: number & >=0 & <24 | *0
|
||||
// Panel y
|
||||
y: number & >=0 | *0
|
||||
// true if fixed
|
||||
static?: bool
|
||||
}
|
||||
// Panel links.
|
||||
// FIXME this is temporarily specified as a closed list so
|
||||
// that validation will pass when no links are present, but
|
||||
// to force a failure as soon as it's checked against there
|
||||
// being anything in the list so it can be fixed in
|
||||
// accordance with that object
|
||||
links?: []
|
||||
|
||||
// Name of template variable to repeat for.
|
||||
repeat?: string
|
||||
// Direction to repeat in if 'repeat' is set.
|
||||
// "h" for horizontal, "v" for vertical.
|
||||
repeatDirection: *"h" | "v"
|
||||
|
||||
// TODO docs
|
||||
maxDataPoints?: number
|
||||
|
||||
// TODO docs
|
||||
thresholds?: [...]
|
||||
|
||||
// TODO docs
|
||||
timeRegions?: [...]
|
||||
|
||||
transformations: [...#Transformation]
|
||||
|
||||
// TODO docs
|
||||
// TODO tighter constraint
|
||||
interval?: string
|
||||
|
||||
// TODO docs
|
||||
// TODO tighter constraint
|
||||
timeFrom?: string
|
||||
|
||||
// TODO docs
|
||||
// TODO tighter constraint
|
||||
timeShift?: string
|
||||
|
||||
// options is specified by the PanelOptions field in panel
|
||||
// plugin schemas.
|
||||
options: {}
|
||||
|
||||
fieldConfig: {
|
||||
defaults: {
|
||||
// The display value for this field. This supports template variables blank is auto
|
||||
displayName?: string
|
||||
|
||||
// This can be used by data sources that return and explicit naming structure for values and labels
|
||||
// When this property is configured, this value is used rather than the default naming strategy.
|
||||
displayNameFromDS?: string
|
||||
|
||||
// Human readable field metadata
|
||||
description?: string
|
||||
|
||||
// An explict path to the field in the datasource. When the frame meta includes a path,
|
||||
// This will default to `${frame.meta.path}/${field.name}
|
||||
//
|
||||
// When defined, this value can be used as an identifier within the datasource scope, and
|
||||
// may be used to update the results
|
||||
path?: string
|
||||
|
||||
// True if data source can write a value to the path. Auth/authz are supported separately
|
||||
writeable?: bool
|
||||
|
||||
// True if data source field supports ad-hoc filters
|
||||
filterable?: bool
|
||||
|
||||
// Numeric Options
|
||||
unit?: string
|
||||
|
||||
// Significant digits (for display)
|
||||
decimals?: number
|
||||
|
||||
min?: number
|
||||
max?: number
|
||||
|
||||
// Convert input values into a display string
|
||||
//
|
||||
// TODO this one corresponds to a complex type with
|
||||
// generics on the typescript side. Ouch. Will
|
||||
// either need special care, or we'll just need to
|
||||
// accept a very loosely specified schema. It's very
|
||||
// unlikely we'll be able to translate cue to
|
||||
// typescript generics in the general case, though
|
||||
// this particular one *may* be able to work.
|
||||
mappings?: [...{...}]
|
||||
|
||||
// Map numeric values to states
|
||||
thresholds?: #ThresholdsConfig
|
||||
|
||||
// // Map values to a display color
|
||||
color?: #FieldColor
|
||||
|
||||
// // Used when reducing field values
|
||||
// nullValueMode?: NullValueMode
|
||||
|
||||
// // The behavior when clicking on a result
|
||||
links?: [...]
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string
|
||||
|
||||
// custom is specified by the PanelFieldConfig field
|
||||
// in panel plugin schemas.
|
||||
custom?: {}
|
||||
}
|
||||
overrides: [...{
|
||||
matcher: {
|
||||
id: string | *""
|
||||
options?: _
|
||||
}
|
||||
properties: [...{
|
||||
id: string | *""
|
||||
value?: _
|
||||
}]
|
||||
}]
|
||||
}
|
||||
// Embed the disjunction of all injected panel schema, if any were injected.
|
||||
if len(compose._panelSchemas) > 0 {
|
||||
or(compose._panelSchemas)// TODO try to stick graph in here
|
||||
}
|
||||
|
||||
// Make the plugin-composed subtrees open if the panel is
|
||||
// of unknown types. This is important in every possible case:
|
||||
// - Base (this file only): no real dashboard json
|
||||
// containing any panels would ever validate
|
||||
// - Dist (this file + core plugin schema): dashboard json containing
|
||||
// panels with any third-party panel plugins would fail to validate,
|
||||
// as well as any core plugins lacking a models.cue. The latter case
|
||||
// is not normally expected, but this is not the appropriate place
|
||||
// to enforce the invariant, anyway.
|
||||
// - Instance (this file + core + third-party plugin schema): dashboard
|
||||
// json containing panels with a third-party plugin that exists but
|
||||
// is not currently installed would fail to validate.
|
||||
if !list.Contains(compose._panelTypes, type) {
|
||||
options: {...}
|
||||
fieldConfig: defaults: custom: {...}
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
// Row panel
|
||||
#RowPanel: {
|
||||
type: "row"
|
||||
collapsed: bool | *false
|
||||
title?: string
|
||||
|
||||
// Name of default datasource.
|
||||
datasource: {
|
||||
type?: string
|
||||
uid?: string
|
||||
}
|
||||
|
||||
gridPos?: {
|
||||
// Panel
|
||||
h: number & >0 | *9
|
||||
// Panel
|
||||
w: number & >0 & <=24 | *12
|
||||
// Panel x
|
||||
x: number & >=0 & <24 | *0
|
||||
// Panel y
|
||||
y: number & >=0 | *0
|
||||
// true if fixed
|
||||
static?: bool
|
||||
}
|
||||
id: number
|
||||
panels: [...(#Panel | #GraphPanel)]
|
||||
// Name of template variable to repeat for.
|
||||
repeat?: string
|
||||
}
|
||||
|
||||
// Support for legacy graph panels.
|
||||
#GraphPanel: {
|
||||
...
|
||||
type: "graph"
|
||||
thresholds: [...{...}]
|
||||
timeRegions?: [...{...}]
|
||||
seriesOverrides: [...{...}]
|
||||
aliasColors?: [string]: string
|
||||
bars: bool | *false
|
||||
dashes: bool | *false
|
||||
dashLength: number | *10
|
||||
fill?: number
|
||||
fillGradient?: number
|
||||
hiddenSeries: bool | *false
|
||||
legend: {...}
|
||||
lines: bool | *false
|
||||
linewidth?: number
|
||||
nullPointMode: *"null" | "connected" | "null as zero"
|
||||
percentage: bool | *false
|
||||
points: bool | *false
|
||||
pointradius?: number
|
||||
renderer: string
|
||||
spaceLength: number | *10
|
||||
stack: bool | *false
|
||||
steppedLine: bool | *false
|
||||
tooltip?: {
|
||||
shared?: bool
|
||||
sort: number | *0
|
||||
value_type: *"individual" | "cumulative"
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
]
|
||||
compose: {
|
||||
// Scuemata families for all panel types that should be composed into the
|
||||
// dashboard schema.
|
||||
Panel: [string]: scuemata.#PanelFamily
|
||||
|
||||
// _panelTypes: [for typ, _ in Panel {typ}]
|
||||
_panelTypes: [ for typ, _ in Panel {typ}, "graph", "row"]
|
||||
_panelSchemas: [ for typ, scue in Panel {
|
||||
for lv, lin in scue.lineages {
|
||||
for sv, sch in lin {
|
||||
(_mapPanel & {arg: {
|
||||
type: typ
|
||||
v: [lv, sv] // TODO add optionality for exact, at least, at most, any
|
||||
model: sch // TODO Does this need to be close()d?
|
||||
}}).out
|
||||
}
|
||||
}
|
||||
}, {type: string}]
|
||||
_mapPanel: {
|
||||
arg: {
|
||||
type: string & !=""
|
||||
v: [number, number]
|
||||
model: {...}
|
||||
}
|
||||
// Until CUE introduces the must() constraint, we have to enforce
|
||||
// that the input model is as expected by checking for unification
|
||||
// in this hidden property (see https://github.com/cue-lang/cue/issues/575).
|
||||
// If we unified arg.model with the scuemata.#PanelSchema
|
||||
// meta-schema directly, the struct openness (PanelOptions: {...})
|
||||
// would be applied to the actual schema instance in the arg. Here,
|
||||
// where we're actually putting those in the dashboard schema, want
|
||||
// those to be closed, or at least preserve closed-ness.
|
||||
_checkSchema: scuemata.#PanelSchema & arg.model
|
||||
out: {
|
||||
type: arg.type
|
||||
panelSchema: arg.v // TODO add optionality for exact, at least, at most, any
|
||||
options: arg.model.PanelOptions
|
||||
fieldConfig: defaults: custom: {}
|
||||
if arg.model.PanelFieldConfig != _|_ {
|
||||
fieldConfig: defaults: custom: arg.model.PanelFieldConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package dist
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/packages/grafana-schema/src/scuemata/dashboard"
|
||||
pannolist "github.com/grafana/grafana/public/app/plugins/panel/annolist:grafanaschema"
|
||||
pbarchart "github.com/grafana/grafana/public/app/plugins/panel/barchart:grafanaschema"
|
||||
pbargauge "github.com/grafana/grafana/public/app/plugins/panel/bargauge:grafanaschema"
|
||||
pcanvas "github.com/grafana/grafana/public/app/plugins/panel/canvas:grafanaschema"
|
||||
pdashlist "github.com/grafana/grafana/public/app/plugins/panel/dashlist:grafanaschema"
|
||||
pgauge "github.com/grafana/grafana/public/app/plugins/panel/gauge:grafanaschema"
|
||||
phistogram "github.com/grafana/grafana/public/app/plugins/panel/histogram:grafanaschema"
|
||||
pcandlestick "github.com/grafana/grafana/public/app/plugins/panel/candlestick:grafanaschema"
|
||||
pnews "github.com/grafana/grafana/public/app/plugins/panel/news:grafanaschema"
|
||||
pstat "github.com/grafana/grafana/public/app/plugins/panel/stat:grafanaschema"
|
||||
st "github.com/grafana/grafana/public/app/plugins/panel/state-timeline:grafanaschema"
|
||||
sh "github.com/grafana/grafana/public/app/plugins/panel/status-history:grafanaschema"
|
||||
ptable "github.com/grafana/grafana/public/app/plugins/panel/table:grafanaschema"
|
||||
ptext "github.com/grafana/grafana/public/app/plugins/panel/text:grafanaschema"
|
||||
ptimeseries "github.com/grafana/grafana/public/app/plugins/panel/timeseries:grafanaschema"
|
||||
pheatmap_new "github.com/grafana/grafana/public/app/plugins/panel/heatmap-new:grafanaschema"
|
||||
)
|
||||
|
||||
// Family composes the base dashboard scuemata family with all Grafana core plugins -
|
||||
// the plugins that are dist[ributed] with Grafana. The resulting composed scuemata is
|
||||
// exactly equivalent to what's produced by the DistDashboardFamily() Go function.
|
||||
//
|
||||
// CUE programs should default to importing this dist variant over the base variant.
|
||||
Family: dashboard.Family & {
|
||||
compose: Panel: {
|
||||
// TODO do this with a loop once we include the panel type/plugin id in the model
|
||||
annolist: pannolist.Panel
|
||||
barchart: pbarchart.Panel
|
||||
bargauge: pbargauge.Panel
|
||||
canvas: pcanvas.Panel
|
||||
dashlist: pdashlist.Panel
|
||||
gauge: pgauge.Panel
|
||||
histogram: phistogram.Panel
|
||||
candlestick: pcandlestick.Panel
|
||||
news: pnews.Panel
|
||||
stat: pstat.Panel
|
||||
"state-timeline": st.Panel
|
||||
"status-history": sh.Panel
|
||||
text: ptext.Panel
|
||||
table: ptable.Panel
|
||||
timeseries: ptimeseries.Panel
|
||||
"heatmap-new": pheatmap_new.Panel
|
||||
}
|
||||
}
|
@ -58,20 +58,12 @@ func (hs *HTTPServer) TrimDashboard(c *models.ReqContext) response.Response {
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
var err error
|
||||
dash := cmd.Dashboard
|
||||
meta := cmd.Meta
|
||||
|
||||
trimedResult := *dash
|
||||
if !hs.LoadSchemaService.IsDisabled() {
|
||||
trimedResult, err = hs.LoadSchemaService.DashboardTrimDefaults(*dash)
|
||||
if err != nil {
|
||||
return response.Error(500, "Error while exporting with default values removed", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO temporarily just return the input as a no-op while we convert to thema calls
|
||||
dto := dtos.TrimDashboardFullWithMeta{
|
||||
Dashboard: &trimedResult,
|
||||
Dashboard: dash,
|
||||
Meta: meta,
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/queryhistory"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/services/schemaloader"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
"github.com/grafana/grafana/pkg/services/searchusers"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
@ -122,7 +121,6 @@ type HTTPServer struct {
|
||||
ContextHandler *contexthandler.ContextHandler
|
||||
SQLStore sqlstore.Store
|
||||
AlertEngine *alerting.AlertEngine
|
||||
LoadSchemaService *schemaloader.SchemaLoaderService
|
||||
AlertNG *ngalert.AlertNG
|
||||
LibraryPanelService librarypanels.Service
|
||||
LibraryElementService libraryelements.Service
|
||||
@ -180,8 +178,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
dataSourceProxy *datasourceproxy.DataSourceProxyService, searchService *search.SearchService,
|
||||
live *live.GrafanaLive, livePushGateway *pushhttp.Gateway, plugCtxProvider *plugincontext.Provider,
|
||||
contextHandler *contexthandler.ContextHandler, features *featuremgmt.FeatureManager,
|
||||
schemaService *schemaloader.SchemaLoaderService, alertNG *ngalert.AlertNG,
|
||||
libraryPanelService librarypanels.Service, libraryElementService libraryelements.Service,
|
||||
alertNG *ngalert.AlertNG, libraryPanelService librarypanels.Service, libraryElementService libraryelements.Service,
|
||||
quotaService *quota.QuotaService, socialService social.Service, tracer tracing.Tracer, exportService export.ExportService,
|
||||
encryptionService encryption.Internal, grafanaUpdateChecker *updatechecker.GrafanaService,
|
||||
pluginsUpdateChecker *updatechecker.PluginsService, searchUsersService searchusers.Service,
|
||||
@ -237,7 +234,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
LivePushGateway: livePushGateway,
|
||||
PluginContextProvider: plugCtxProvider,
|
||||
ContextHandler: contextHandler,
|
||||
LoadSchemaService: schemaService,
|
||||
AlertNG: alertNG,
|
||||
LibraryPanelService: libraryPanelService,
|
||||
LibraryElementService: libraryElementService,
|
||||
|
@ -100,12 +100,6 @@ func runPluginCommand(command func(commandLine utils.CommandLine) error) func(co
|
||||
}
|
||||
}
|
||||
|
||||
func runCueCommand(command func(commandLine utils.CommandLine) error) func(context *cli.Context) error {
|
||||
return func(context *cli.Context) error {
|
||||
return command(&utils.ContextCommandLine{Context: context})
|
||||
}
|
||||
}
|
||||
|
||||
// Command contains command state.
|
||||
type Command struct {
|
||||
Client utils.ApiClient
|
||||
@ -197,74 +191,6 @@ var adminCommands = []*cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var cueCommands = []*cli.Command{
|
||||
{
|
||||
Name: "validate-schema",
|
||||
Usage: "validate known *.cue files in the Grafana project",
|
||||
Action: runCueCommand(cmd.validateScuemata),
|
||||
Description: `validate-schema checks that all CUE schema files are valid with respect
|
||||
to basic standards - valid CUE, valid scuemata, etc. Note that this
|
||||
command checks only paths that existed when grafana-cli was compiled,
|
||||
so must be recompiled to validate newly-added CUE files.`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-root",
|
||||
Usage: "path to the root of a Grafana repository to validate",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "validate-resource",
|
||||
Usage: "validate resource files (e.g. dashboard JSON) against schema",
|
||||
Action: runCueCommand(cmd.validateResources),
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "dashboard",
|
||||
Usage: "dashboard JSON file to validate",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "base-only",
|
||||
Usage: "validate using only base schema, not dist (includes plugin schema)",
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "trim-resource",
|
||||
Usage: "trim schema-specified defaults from a resource",
|
||||
Action: runCueCommand(cmd.trimResource),
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "dashboard",
|
||||
Usage: "path to file containing (valid) dashboard JSON",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "apply",
|
||||
Usage: "invert the operation: apply defaults instead of trimming them",
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "gen-ts",
|
||||
Usage: "generate TypeScript from all known CUE file types",
|
||||
Description: `gen-ts generates TypeScript from all CUE files at
|
||||
expected positions in the filesystem tree of a Grafana repository.`,
|
||||
Action: runCueCommand(cmd.generateTypescript),
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "grafana-root",
|
||||
Usage: "path to the root of a Grafana repository in which to generate TypeScript from CUE files",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "diff",
|
||||
Usage: "diff results of codegen against files already on disk. Exits 1 if diff is non-empty",
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "plugins",
|
||||
@ -276,9 +202,4 @@ var Commands = []*cli.Command{
|
||||
Usage: "Grafana admin commands",
|
||||
Subcommands: adminCommands,
|
||||
},
|
||||
{
|
||||
Name: "cue",
|
||||
Usage: "Cue validation commands",
|
||||
Subcommands: cueCommands,
|
||||
},
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
gerrors "errors"
|
||||
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
"github.com/grafana/grafana/pkg/codegen"
|
||||
)
|
||||
|
||||
var ctx = cuecontext.New()
|
||||
|
||||
// TODO remove this whole thing
|
||||
func (cmd Command) generateTypescript(c utils.CommandLine) error {
|
||||
root := c.String("grafana-root")
|
||||
if root == "" {
|
||||
return gerrors.New("must provide path to the root of a Grafana repository checkout")
|
||||
}
|
||||
|
||||
wd, err := codegen.CuetsifyPlugins(ctx, root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Bool("diff") {
|
||||
return wd.Verify()
|
||||
}
|
||||
|
||||
return wd.Write()
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
gerrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing/fstest"
|
||||
|
||||
"cuelang.org/go/cue/errors"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
"github.com/grafana/grafana/pkg/schema/load"
|
||||
)
|
||||
|
||||
var paths = load.GetDefaultLoadPaths()
|
||||
|
||||
func (cmd Command) validateScuemata(c utils.CommandLine) error {
|
||||
root := c.String("grafana-root")
|
||||
if root == "" {
|
||||
return gerrors.New("must provide path to the root of a Grafana repository checkout")
|
||||
}
|
||||
|
||||
// Construct MapFS with the same set of files as those embedded in
|
||||
// /embed.go, but sourced straight through from disk instead of relying on
|
||||
// what's compiled. Not the greatest, because we're duplicating
|
||||
// filesystem-loading logic with what's in /embed.go.
|
||||
var fspaths load.BaseLoadPaths
|
||||
var err error
|
||||
|
||||
fspaths.BaseCueFS, err = populateMapFSFromRoot(paths.BaseCueFS, root, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fspaths.DistPluginCueFS, err = populateMapFSFromRoot(paths.DistPluginCueFS, root, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateScuemata(fspaths, load.DistDashboardFamily); err != nil {
|
||||
return schema.WrapCUEError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function that populates an fs.FS by walking over a virtual filesystem,
|
||||
// and reading files from disk corresponding to each file encountered.
|
||||
func populateMapFSFromRoot(in fs.FS, root, join string) (fs.FS, error) {
|
||||
out := make(fstest.MapFS)
|
||||
err := fs.WalkDir(in, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
// Ignore gosec warning G304. The input set here is necessarily
|
||||
// constrained to files specified in embed.go
|
||||
// nolint:gosec
|
||||
b, err := os.Open(filepath.Join(root, join, path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
byt, err := io.ReadAll(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out[path] = &fstest.MapFile{Data: byt}
|
||||
return nil
|
||||
})
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (cmd Command) validateResources(c utils.CommandLine) error {
|
||||
filename := c.String("dashboard")
|
||||
baseonly := c.Bool("base-only")
|
||||
if filename == "" {
|
||||
return gerrors.New("must specify dashboard to validate with --dashboard")
|
||||
}
|
||||
b, err := os.Open(filepath.Clean(filename))
|
||||
res := schema.Resource{Value: b, Name: filename}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sch schema.VersionedCueSchema
|
||||
if baseonly {
|
||||
sch, err = load.BaseDashboardFamily(paths)
|
||||
} else {
|
||||
sch, err = load.DistDashboardFamily(paths)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while loading dashboard scuemata, err: %w", err)
|
||||
}
|
||||
|
||||
err = sch.Validate(res)
|
||||
if err != nil {
|
||||
return gerrors.New(errors.Details(err, nil))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateScuemata(p load.BaseLoadPaths, loader func(p load.BaseLoadPaths) (schema.VersionedCueSchema, error)) error {
|
||||
dash, err := loader(p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while loading dashboard scuemata, err: %w", err)
|
||||
}
|
||||
|
||||
// Check that a CUE value exists.
|
||||
cueValue := dash.CUE()
|
||||
if !cueValue.Exists() {
|
||||
return fmt.Errorf("cue value for schema does not exist")
|
||||
}
|
||||
|
||||
// Check CUE validity.
|
||||
if err := cueValue.Validate(); err != nil {
|
||||
return fmt.Errorf("all schema should be valid with respect to basic CUE rules, %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/schema/load"
|
||||
"github.com/laher/mergefs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var defaultBaseLoadPaths = load.GetDefaultLoadPaths()
|
||||
|
||||
func TestValidateScuemataBasics(t *testing.T) {
|
||||
t.Run("Testing scuemata validity with valid cue schemas", func(t *testing.T) {
|
||||
var baseLoadPaths = load.BaseLoadPaths{
|
||||
BaseCueFS: defaultBaseLoadPaths.BaseCueFS,
|
||||
DistPluginCueFS: defaultBaseLoadPaths.DistPluginCueFS,
|
||||
}
|
||||
|
||||
err := validateScuemata(baseLoadPaths, load.BaseDashboardFamily)
|
||||
require.NoError(t, err, "error while loading base dashboard scuemata")
|
||||
|
||||
err = validateScuemata(baseLoadPaths, load.DistDashboardFamily)
|
||||
require.NoError(t, err, "error while loading dist dashboard scuemata")
|
||||
})
|
||||
|
||||
t.Run("Testing scuemata validity with invalid cue schemas - family missing", func(t *testing.T) {
|
||||
t.Skip() // TODO debug, re-enable and move
|
||||
genCue, err := os.ReadFile("testdata/missing_family.cue")
|
||||
require.NoError(t, err)
|
||||
|
||||
filesystem := fstest.MapFS{
|
||||
"packages/grafana-schema/src/scuemata/dashboard/dashboard.cue": &fstest.MapFile{Data: genCue},
|
||||
}
|
||||
mergedFS := mergefs.Merge(filesystem, defaultBaseLoadPaths.BaseCueFS)
|
||||
|
||||
var baseLoadPaths = load.BaseLoadPaths{
|
||||
BaseCueFS: mergedFS,
|
||||
DistPluginCueFS: defaultBaseLoadPaths.DistPluginCueFS,
|
||||
}
|
||||
|
||||
err = validateScuemata(baseLoadPaths, load.BaseDashboardFamily)
|
||||
assert.EqualError(t, err, "error while loading dashboard scuemata, err: dashboard schema family did not exist at expected path in expected file")
|
||||
})
|
||||
|
||||
t.Run("Testing scuemata validity with invalid cue schemas - panel missing ", func(t *testing.T) {
|
||||
t.Skip() // TODO debug, re-enable and move
|
||||
genCue, err := os.ReadFile("testdata/missing_panel.cue")
|
||||
require.NoError(t, err)
|
||||
|
||||
filesystem := fstest.MapFS{
|
||||
"packages/grafana-schema/src/scuemata/dashboard/dashboard.cue": &fstest.MapFile{Data: genCue},
|
||||
}
|
||||
mergedFS := mergefs.Merge(filesystem, defaultBaseLoadPaths.BaseCueFS)
|
||||
|
||||
var baseLoadPaths = load.BaseLoadPaths{
|
||||
BaseCueFS: mergedFS,
|
||||
DistPluginCueFS: defaultBaseLoadPaths.DistPluginCueFS,
|
||||
}
|
||||
|
||||
err = validateScuemata(baseLoadPaths, load.BaseDashboardFamily)
|
||||
require.NoError(t, err, "error while loading base dashboard scuemata")
|
||||
|
||||
err = validateScuemata(baseLoadPaths, load.DistDashboardFamily)
|
||||
assert.EqualError(t, err, "all schema should be valid with respect to basic CUE rules, Family.lineages.0.0: field #Panel not allowed")
|
||||
})
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import "github.com/grafana/grafana/cue/scuemata"
|
||||
|
||||
Dummy: scuemata.#Family & {
|
||||
lineages: [
|
||||
[
|
||||
{ // 0.0
|
||||
// Unique numeric identifier for the dashboard.
|
||||
// TODO must isolate or remove identifiers local to a Grafana instance...?
|
||||
id?: number
|
||||
// Unique dashboard identifier that can be generated by anyone. string (8-40)
|
||||
uid: string
|
||||
// Title of dashboard.
|
||||
title?: string
|
||||
// Description of dashboard.
|
||||
description?: string
|
||||
|
||||
gnetId?: string
|
||||
// Tags associated with dashboard.
|
||||
tags?: [...string]
|
||||
// Theme of dashboard.
|
||||
style: *"light" | "dark"
|
||||
// Timezone of dashboard,
|
||||
timezone?: *"browser" | "utc"
|
||||
// Whether a dashboard is editable or not.
|
||||
editable: bool | *true
|
||||
// 0 for no shared crosshair or tooltip (default).
|
||||
// 1 for shared crosshair.
|
||||
// 2 for shared crosshair AND shared tooltip.
|
||||
graphTooltip: >=0 & <=2 | *0
|
||||
// Time range for dashboard, e.g. last 6 hours, last 7 days, etc
|
||||
time?: {
|
||||
from: string | *"now-6h"
|
||||
to: string | *"now"
|
||||
}
|
||||
// Timepicker metadata.
|
||||
timepicker?: {
|
||||
// Whether timepicker is collapsed or not.
|
||||
collapse: bool | *false
|
||||
// Whether timepicker is enabled or not.
|
||||
enable: bool | *true
|
||||
// Whether timepicker is visible or not.
|
||||
hidden: bool | *false
|
||||
// Selectable intervals for auto-refresh.
|
||||
refresh_intervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
||||
}
|
||||
// Templating.
|
||||
templating?: list: [...{...}]
|
||||
// Annotations.
|
||||
annotations?: list: [...{
|
||||
builtIn: number | *0
|
||||
// Datasource to use for annotation.
|
||||
datasource: string
|
||||
// Whether annotation is enabled.
|
||||
enable?: bool | *true
|
||||
// Whether to hide annotation.
|
||||
hide?: bool | *false
|
||||
// Annotation icon color.
|
||||
iconColor?: string
|
||||
// Name of annotation.
|
||||
name?: string
|
||||
type: string | *"dashboard"
|
||||
// Query for annotation data.
|
||||
rawQuery?: string
|
||||
showIn: number | *0
|
||||
}]
|
||||
// Auto-refresh interval.
|
||||
refresh?: string
|
||||
// Version of the JSON schema, incremented each time a Grafana update brings
|
||||
// changes to said schema.
|
||||
schemaVersion: number | *25
|
||||
// Version of the dashboard, incremented each time the dashboard is updated.
|
||||
version?: number
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import "github.com/grafana/grafana/cue/scuemata"
|
||||
|
||||
Family: scuemata.#Family & {
|
||||
lineages: [
|
||||
[
|
||||
{ // 0.0
|
||||
// Unique numeric identifier for the dashboard.
|
||||
// TODO must isolate or remove identifiers local to a Grafana instance...?
|
||||
id?: number
|
||||
// Unique dashboard identifier that can be generated by anyone. string (8-40)
|
||||
uid: string
|
||||
// Title of dashboard.
|
||||
title?: string
|
||||
// Description of dashboard.
|
||||
description?: string
|
||||
|
||||
gnetId?: string
|
||||
// Tags associated with dashboard.
|
||||
tags?: [...string]
|
||||
// Theme of dashboard.
|
||||
style: *"light" | "dark"
|
||||
// Timezone of dashboard,
|
||||
timezone?: *"browser" | "utc"
|
||||
// Whether a dashboard is editable or not.
|
||||
editable: bool | *true
|
||||
// 0 for no shared crosshair or tooltip (default).
|
||||
// 1 for shared crosshair.
|
||||
// 2 for shared crosshair AND shared tooltip.
|
||||
graphTooltip: >=0 & <=2 | *0
|
||||
// Time range for dashboard, e.g. last 6 hours, last 7 days, etc
|
||||
time?: {
|
||||
from: string | *"now-6h"
|
||||
to: string | *"now"
|
||||
}
|
||||
// Timepicker metadata.
|
||||
timepicker?: {
|
||||
// Whether timepicker is collapsed or not.
|
||||
collapse: bool | *false
|
||||
// Whether timepicker is enabled or not.
|
||||
enable: bool | *true
|
||||
// Whether timepicker is visible or not.
|
||||
hidden: bool | *false
|
||||
// Selectable intervals for auto-refresh.
|
||||
refresh_intervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
||||
}
|
||||
// Templating.
|
||||
templating?: list: [...{...}]
|
||||
// Annotations.
|
||||
annotations?: list: [...{
|
||||
builtIn: number | *0
|
||||
// Datasource to use for annotation.
|
||||
datasource: string
|
||||
// Whether annotation is enabled.
|
||||
enable?: bool | *true
|
||||
// Whether to hide annotation.
|
||||
hide?: bool | *false
|
||||
// Annotation icon color.
|
||||
iconColor?: string
|
||||
// Name of annotation.
|
||||
name?: string
|
||||
type: string | *"dashboard"
|
||||
// Query for annotation data.
|
||||
rawQuery?: string
|
||||
showIn: number | *0
|
||||
}]
|
||||
// Auto-refresh interval.
|
||||
refresh?: string
|
||||
// Version of the JSON schema, incremented each time a Grafana update brings
|
||||
// changes to said schema.
|
||||
schemaVersion: number | *25
|
||||
// Version of the dashboard, incremented each time the dashboard is updated.
|
||||
version?: number
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"panels": [
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"filterable": false
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": [],
|
||||
"unit": "watt"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_GDEV-TESTDATA",
|
||||
"label": "gdev-testdata",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "testdata",
|
||||
"pluginName": "TestData DB"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "7.5.0-pre"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "table",
|
||||
"name": "Table",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "testdata",
|
||||
"name": "TestData DB",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"rawQuery": "wtf",
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"id": 42,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"filterable": false
|
||||
},
|
||||
"decimals": 3,
|
||||
"unit": "watt"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Max"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "lcd-gauge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "A"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"points": [],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk_table",
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "table",
|
||||
"panelSchema": {
|
||||
"maj": 0,
|
||||
"min": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"schemaVersion": 27,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timezone": "browser",
|
||||
"title": "with table",
|
||||
"uid": "emal8gQMz",
|
||||
"version": 2
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
gerrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue/errors"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
"github.com/grafana/grafana/pkg/schema/load"
|
||||
)
|
||||
|
||||
func (cmd Command) trimResource(c utils.CommandLine) error {
|
||||
filename := c.String("dashboard")
|
||||
if filename == "" {
|
||||
return gerrors.New("must specify dashboard file path with --dashboard")
|
||||
}
|
||||
apply := c.Bool("apply")
|
||||
|
||||
f, err := os.Open(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res := schema.Resource{Value: string(b), Name: filename}
|
||||
sch, err := load.DistDashboardFamily(paths)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while loading dashboard scuemata, err: %w", err)
|
||||
}
|
||||
|
||||
var out schema.Resource
|
||||
if apply {
|
||||
out, err = schema.ApplyDefaults(res, sch.CUE())
|
||||
} else {
|
||||
out, err = schema.TrimDefaults(res, sch.CUE())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return gerrors.New(errors.Details(err, nil))
|
||||
}
|
||||
|
||||
b = []byte(out.Value.(string))
|
||||
var buf bytes.Buffer
|
||||
err = json.Indent(&buf, b, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(buf.String())
|
||||
return nil
|
||||
}
|
@ -6,26 +6,32 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing/fstest"
|
||||
"text/template"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/ast"
|
||||
"cuelang.org/go/cue/build"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/errors"
|
||||
cload "cuelang.org/go/cue/load"
|
||||
"cuelang.org/go/cue/load"
|
||||
"cuelang.org/go/cue/parser"
|
||||
"github.com/grafana/cuetsy"
|
||||
"github.com/grafana/grafana/pkg/schema/load"
|
||||
"github.com/grafana/grafana"
|
||||
"github.com/grafana/thema"
|
||||
tload "github.com/grafana/thema/load"
|
||||
)
|
||||
|
||||
// The only import statement we currently allow in any models.cue file
|
||||
const allowedImport = "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
const schemasPath = "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
|
||||
// CUE import paths, mapped to corresponding TS import paths. An empty value
|
||||
// indicates the import path should be dropped in the conversion to TS. Imports
|
||||
// not present in the list are not not allowed, and code generation will fail.
|
||||
var importMap = map[string]string{
|
||||
allowedImport: "@grafana/schema",
|
||||
"github.com/grafana/thema": "",
|
||||
schemasPath: "@grafana/schema",
|
||||
}
|
||||
|
||||
// Hard-coded list of paths to skip. Remove a particular file as we're ready
|
||||
@ -44,55 +50,33 @@ var skipPaths = []string{
|
||||
|
||||
const prefix = "/"
|
||||
|
||||
var paths = load.GetDefaultLoadPaths()
|
||||
|
||||
// CuetsifyPlugins runs cuetsy against plugins' models.cue files.
|
||||
func CuetsifyPlugins(ctx *cue.Context, root string) (WriteDiffer, error) {
|
||||
lib := thema.NewLibrary(ctx)
|
||||
// TODO this whole func has a lot of old, crufty behavior from the scuemata era; needs TLC
|
||||
var fspaths load.BaseLoadPaths
|
||||
var err error
|
||||
|
||||
fspaths.BaseCueFS, err = populateMapFSFromRoot(paths.BaseCueFS, root, "")
|
||||
overlay := make(map[string]load.Source)
|
||||
err := toOverlay(prefix, grafana.CueSchemaFS, overlay)
|
||||
// err := tload.ToOverlay(prefix, grafana.CueSchemaFS, overlay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fspaths.DistPluginCueFS, err = populateMapFSFromRoot(paths.DistPluginCueFS, root, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
overlay, err := defaultOverlay(fspaths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prep the cue load config
|
||||
clcfg := &cload.Config{
|
||||
Overlay: overlay,
|
||||
// FIXME these module paths won't work for things not under our cue.mod - AKA third-party plugins
|
||||
ModuleRoot: prefix,
|
||||
Module: "github.com/grafana/grafana",
|
||||
}
|
||||
|
||||
// FIXME hardcoding paths to exclude is not the way to handle this
|
||||
excl := map[string]bool{
|
||||
"cue.mod": true,
|
||||
"cue/scuemata": true,
|
||||
"packages/grafana-schema/src/scuemata/dashboard": true,
|
||||
"packages/grafana-schema/src/scuemata/dashboard/dist": true,
|
||||
}
|
||||
|
||||
exclude := func(path string) bool {
|
||||
dir := filepath.Dir(path)
|
||||
if excl[dir] {
|
||||
return true
|
||||
}
|
||||
for _, p := range skipPaths {
|
||||
if path == p {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return filepath.Dir(path) == "cue.mod"
|
||||
}
|
||||
|
||||
// Prep the cue load config
|
||||
clcfg := &load.Config{
|
||||
Overlay: overlay,
|
||||
// FIXME these module paths won't work for things not under our cue.mod - AKA third-party plugins
|
||||
ModuleRoot: prefix,
|
||||
Module: "github.com/grafana/grafana",
|
||||
}
|
||||
|
||||
outfiles := NewWriteDiffer()
|
||||
@ -110,88 +94,74 @@ func CuetsifyPlugins(ctx *cue.Context, root string) (WriteDiffer, error) {
|
||||
}
|
||||
seen[dir] = true
|
||||
clcfg.Dir = filepath.Join(root, dir)
|
||||
// FIXME Horrible hack to figure out the identifier used for
|
||||
// imported packages - intercept the parser called by the loader to
|
||||
// look at the ast.Files on their way in to building.
|
||||
// Much better if we could work backwards from the cue.Value,
|
||||
// maybe even directly in cuetsy itself, and figure out when a
|
||||
// referenced object is "out of bounds".
|
||||
// var imports sync.Map
|
||||
var imports []*ast.ImportSpec
|
||||
clcfg.ParseFile = func(name string, src interface{}) (*ast.File, error) {
|
||||
f, err := parser.ParseFile(name, src, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imports = append(imports, f.Imports...)
|
||||
return f, nil
|
||||
}
|
||||
if strings.Contains(path, "public/app/plugins") {
|
||||
clcfg.Package = "grafanaschema"
|
||||
} else {
|
||||
clcfg.Package = ""
|
||||
}
|
||||
|
||||
// FIXME loading in this way causes all files in a dir to be loaded
|
||||
// as a single cue.Instance or cue.Value, which makes it quite
|
||||
// difficult to map them _back_ onto the original file and generate
|
||||
// discrete .gen.ts files for each .cue input. However, going one
|
||||
// .cue file at a time and passing it as the first arg to
|
||||
// load.Instances() means that the other files are ignored
|
||||
// completely, causing references between these files to be
|
||||
// unresolved, and thus encounter a different kind of error.
|
||||
insts := cload.Instances(nil, clcfg)
|
||||
if len(insts) > 1 {
|
||||
panic("extra instances")
|
||||
}
|
||||
bi := insts[0]
|
||||
|
||||
v := ctx.BuildInstance(bi)
|
||||
if v.Err() != nil {
|
||||
return v.Err()
|
||||
}
|
||||
|
||||
var b []byte
|
||||
f := &tsFile{}
|
||||
seen := make(map[string]bool)
|
||||
// FIXME explicitly mapping path patterns to conversion patterns
|
||||
// is exactly what we want to avoid
|
||||
|
||||
switch {
|
||||
// panel plugin models.cue files
|
||||
case strings.Contains(path, "public/app/plugins"):
|
||||
for _, im := range imports {
|
||||
default:
|
||||
insts := load.Instances(nil, clcfg)
|
||||
if len(insts) > 1 {
|
||||
return fmt.Errorf("%s: resulted in more than one instance", path)
|
||||
}
|
||||
v := ctx.BuildInstance(insts[0])
|
||||
|
||||
b, err = cuetsy.Generate(v, cuetsy.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case strings.Contains(path, "public/app/plugins"): // panel plugin models.cue files
|
||||
// The simple - and preferable - thing would be to have plugins use the same
|
||||
// package name for their models.cue as their containing dir. That's not
|
||||
// possible, though, because we allow dashes in plugin names, but CUE does not
|
||||
// allow them in package names. Yuck.
|
||||
inst, err := loadInstancesWithThema(in, dir, "grafanaschema")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not load CUE instance for %s: %w", dir, err)
|
||||
}
|
||||
|
||||
// Also parse file directly to extract imports.
|
||||
// NOTE this will need refactoring to support working with more than one file at a time
|
||||
of, _ := in.Open(path)
|
||||
pf, _ := parser.ParseFile(filepath.Base(path), of, parser.ParseComments)
|
||||
|
||||
iseen := make(map[string]bool)
|
||||
for _, im := range pf.Imports {
|
||||
ip := strings.Trim(im.Path.Value, "\"")
|
||||
if ip != allowedImport {
|
||||
mappath, has := importMap[ip]
|
||||
if !has {
|
||||
// TODO make a specific error type for this
|
||||
return errors.Newf(im.Pos(), "import %q not allowed, panel plugins may only import from %q", ip, allowedImport)
|
||||
var all []string
|
||||
for im := range importMap {
|
||||
all = append(all, fmt.Sprintf("\t%s", im))
|
||||
}
|
||||
return errors.Newf(im.Pos(), "%s: import %q not allowed, panel plugins may only import from:\n%s\n", path, ip, strings.Join(all, "\n"))
|
||||
}
|
||||
// TODO this approach will silently swallow the unfixable
|
||||
// error case where multiple files in the same dir import
|
||||
// the same package to a different ident
|
||||
if !seen[ip] {
|
||||
seen[ip] = true
|
||||
if mappath != "" && !iseen[ip] {
|
||||
iseen[ip] = true
|
||||
f.Imports = append(f.Imports, convertImport(im))
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the latest schema and its version number. (All of this goes away with Thema, whew)
|
||||
f.V = &tsModver{}
|
||||
lins := v.LookupPath(cue.ParsePath("Panel.lineages"))
|
||||
f.V.Lin, _ = lins.Len().Int64()
|
||||
f.V.Lin = f.V.Lin - 1
|
||||
schs := lins.LookupPath(cue.MakePath(cue.Index(int(f.V.Lin))))
|
||||
f.V.Sch, _ = schs.Len().Int64()
|
||||
f.V.Sch = f.V.Sch - 1
|
||||
latest := schs.LookupPath(cue.MakePath(cue.Index(int(f.V.Sch))))
|
||||
v := ctx.BuildInstance(inst)
|
||||
|
||||
b, err = cuetsy.Generate(latest, cuetsy.Config{})
|
||||
default:
|
||||
b, err = cuetsy.Generate(v, cuetsy.Config{})
|
||||
lin, err := thema.BindLineage(v.LookupPath(cue.ParsePath("Panel")), lib)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: failed to bind lineage: %w", path, err)
|
||||
}
|
||||
f.V = thema.LatestVersion(lin)
|
||||
f.WriteModelVersion = true
|
||||
|
||||
b, err = cuetsy.Generate(thema.SchemaP(lin, f.V).UnwrapCUE(), cuetsy.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Body = string(b)
|
||||
|
||||
var buf bytes.Buffer
|
||||
@ -201,11 +171,7 @@ func CuetsifyPlugins(ctx *cue.Context, root string) (WriteDiffer, error) {
|
||||
})
|
||||
}
|
||||
|
||||
err = cuetsify(fspaths.BaseCueFS)
|
||||
if err != nil {
|
||||
return nil, gerrors.New(errors.Details(err, nil))
|
||||
}
|
||||
err = cuetsify(fspaths.DistPluginCueFS)
|
||||
err = cuetsify(grafana.CueSchemaFS)
|
||||
if err != nil {
|
||||
return nil, gerrors.New(errors.Details(err, nil))
|
||||
}
|
||||
@ -215,7 +181,7 @@ func CuetsifyPlugins(ctx *cue.Context, root string) (WriteDiffer, error) {
|
||||
|
||||
func convertImport(im *ast.ImportSpec) *tsImport {
|
||||
tsim := &tsImport{
|
||||
Pkg: importMap[allowedImport],
|
||||
Pkg: importMap[schemasPath],
|
||||
}
|
||||
if im.Name != nil && im.Name.String() != "" {
|
||||
tsim.Ident = im.Name.String()
|
||||
@ -231,21 +197,101 @@ func convertImport(im *ast.ImportSpec) *tsImport {
|
||||
return tsim
|
||||
}
|
||||
|
||||
func defaultOverlay(p load.BaseLoadPaths) (map[string]cload.Source, error) {
|
||||
overlay := make(map[string]cload.Source)
|
||||
var themamodpath string = filepath.Join("cue.mod", "pkg", "github.com", "grafana", "thema")
|
||||
|
||||
if err := toOverlay(prefix, p.BaseCueFS, overlay); err != nil {
|
||||
// all copied and hacked up from Thema's LoadInstancesWithThema, simply to allow setting the
|
||||
// package name
|
||||
func loadInstancesWithThema(modFS fs.FS, dir string, pkgname string) (*build.Instance, error) {
|
||||
var modname string
|
||||
err := fs.WalkDir(modFS, "cue.mod", func(path string, d fs.DirEntry, err error) error {
|
||||
// fs.FS implementations tend to not use path separators as expected. Use a
|
||||
// normalized one for comparisons, but retain the original for calls back into modFS.
|
||||
normpath := filepath.FromSlash(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
switch normpath {
|
||||
case filepath.Join("cue.mod", "gen"), filepath.Join("cue.mod", "usr"):
|
||||
return fs.SkipDir
|
||||
case themamodpath:
|
||||
return fmt.Errorf("path %q already exists in modFS passed to InstancesWithThema, must be absent for dynamic dependency injection", themamodpath)
|
||||
}
|
||||
return nil
|
||||
} else if normpath == filepath.Join("cue.mod", "module.cue") {
|
||||
modf, err := modFS.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer modf.Close() // nolint: errcheck
|
||||
|
||||
b, err := io.ReadAll(modf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modname, err = cuecontext.New().CompileBytes(b).LookupPath(cue.MakePath(cue.Str("module"))).String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if modname == "" {
|
||||
return fmt.Errorf("InstancesWithThema requires non-empty module name in modFS' cue.mod/module.cue")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := toOverlay(prefix, p.DistPluginCueFS, overlay); err != nil {
|
||||
if modname == "" {
|
||||
return nil, errors.New("cue.mod/module.cue did not exist")
|
||||
}
|
||||
|
||||
modroot := filepath.FromSlash(filepath.Join("/", modname))
|
||||
overlay := make(map[string]load.Source)
|
||||
if err := tload.ToOverlay(modroot, modFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return overlay, nil
|
||||
// Special case for when we're calling this loader with paths inside the thema module
|
||||
if modname == "github.com/grafana/thema" {
|
||||
if err := tload.ToOverlay(modroot, thema.CueJointFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := tload.ToOverlay(filepath.Join(modroot, themamodpath), thema.CueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if dir == "" {
|
||||
dir = "."
|
||||
}
|
||||
|
||||
cfg := &load.Config{
|
||||
Overlay: overlay,
|
||||
ModuleRoot: modroot,
|
||||
Module: modname,
|
||||
Dir: filepath.Join(modroot, dir),
|
||||
Package: pkgname,
|
||||
}
|
||||
if dir == "." {
|
||||
cfg.Package = filepath.Base(modroot)
|
||||
cfg.Dir = modroot
|
||||
}
|
||||
|
||||
inst := load.Instances(nil, cfg)[0]
|
||||
if inst.Err != nil {
|
||||
return nil, inst.Err
|
||||
}
|
||||
|
||||
return inst, nil
|
||||
}
|
||||
|
||||
func toOverlay(prefix string, vfs fs.FS, overlay map[string]cload.Source) error {
|
||||
func toOverlay(prefix string, vfs fs.FS, overlay map[string]load.Source) error {
|
||||
if !filepath.IsAbs(prefix) {
|
||||
return fmt.Errorf("must provide absolute path prefix when generating cue overlay, got %q", prefix)
|
||||
}
|
||||
@ -274,7 +320,7 @@ func toOverlay(prefix string, vfs fs.FS, overlay map[string]cload.Source) error
|
||||
return err
|
||||
}
|
||||
|
||||
overlay[filepath.Join(prefix, path)] = cload.FromBytes(b)
|
||||
overlay[filepath.Join(prefix, path)] = load.FromBytes(b)
|
||||
return nil
|
||||
})
|
||||
|
||||
@ -285,44 +331,11 @@ func toOverlay(prefix string, vfs fs.FS, overlay map[string]cload.Source) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function that populates an fs.FS by walking over a virtual filesystem,
|
||||
// and reading files from disk corresponding to each file encountered.
|
||||
func populateMapFSFromRoot(in fs.FS, root, join string) (fs.FS, error) {
|
||||
out := make(fstest.MapFS)
|
||||
err := fs.WalkDir(in, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
// Ignore gosec warning G304. The input set here is necessarily
|
||||
// constrained to files specified in embed.go
|
||||
// nolint:gosec
|
||||
b, err := os.Open(filepath.Join(root, join, path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
byt, err := io.ReadAll(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out[path] = &fstest.MapFile{Data: byt}
|
||||
return nil
|
||||
})
|
||||
return out, err
|
||||
}
|
||||
|
||||
type tsFile struct {
|
||||
V *tsModver
|
||||
Imports []*tsImport
|
||||
Body string
|
||||
}
|
||||
|
||||
type tsModver struct {
|
||||
Lin, Sch int64
|
||||
V thema.SyntacticVersion
|
||||
WriteModelVersion bool
|
||||
Imports []*tsImport
|
||||
Body string
|
||||
}
|
||||
|
||||
type tsImport struct {
|
||||
@ -337,7 +350,7 @@ var tsTemplate = template.Must(template.New("cuetsygen").Parse(`//~~~~~~~~~~~~~~
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{{range .Imports}}
|
||||
import * as {{.Ident}} from '{{.Pkg}}';{{end}}
|
||||
{{if .V}}
|
||||
export const modelVersion = Object.freeze([{{ .V.Lin }}, {{ .V.Sch }}]);
|
||||
{{if .WriteModelVersion }}
|
||||
export const modelVersion = Object.freeze([{{index .V 0}}, {{index .V 1}}]);
|
||||
{{end}}
|
||||
{{.Body}}`))
|
||||
|
@ -17,8 +17,8 @@ import (
|
||||
"github.com/grafana/thema/load"
|
||||
)
|
||||
|
||||
var ctx *cue.Context = cuecontext.New()
|
||||
var lib thema.Library = thema.NewLibrary(ctx)
|
||||
var ctx = cuecontext.New()
|
||||
var lib = thema.NewLibrary(ctx)
|
||||
|
||||
// ProvideCUEContext is a wire service provider of a central cue.Context.
|
||||
func ProvideCUEContext() *cue.Context {
|
||||
@ -82,6 +82,12 @@ func LoadGrafanaInstancesWithThema(
|
||||
return lin, nil
|
||||
}
|
||||
|
||||
// prefixWithGrafanaCUE constructs an fs.FS that merges the provided fs.FS with one
|
||||
// containing grafana's cue.mod at the root. The provided prefix should be the
|
||||
//
|
||||
// The returned fs.FS is suitable for passing to a CUE loader, such as
|
||||
// cuelang.org/cue/load.Instances or
|
||||
// github.com/grafana/thema/load.InstancesWithThema.
|
||||
func prefixWithGrafanaCUE(prefix string, inputfs fs.FS) (fs.FS, error) {
|
||||
m := fstest.MapFS{
|
||||
// fstest can recognize only forward slashes.
|
||||
@ -89,7 +95,7 @@ func prefixWithGrafanaCUE(prefix string, inputfs fs.FS) (fs.FS, error) {
|
||||
}
|
||||
|
||||
prefix = filepath.FromSlash(prefix)
|
||||
err := fs.WalkDir(inputfs, ".", (func(path string, d fs.DirEntry, err error) error {
|
||||
err := fs.WalkDir(inputfs, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -111,7 +117,7 @@ func prefixWithGrafanaCUE(prefix string, inputfs fs.FS) (fs.FS, error) {
|
||||
// fstest can recognize only forward slashes.
|
||||
m[filepath.ToSlash(filepath.Join(prefix, path))] = &fstest.MapFile{Data: b}
|
||||
return nil
|
||||
}))
|
||||
})
|
||||
|
||||
return m, err
|
||||
}
|
||||
|
@ -1,94 +0,0 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/grafana/grafana"
|
||||
)
|
||||
|
||||
var ctx = cuecontext.New()
|
||||
|
||||
// Families can have variants, where more typing information narrows the
|
||||
// possible values for certain keys in schemas. These are a meta-property
|
||||
// of the schema, effectively encoded in these loaders.
|
||||
//
|
||||
// We can generally define three variants:
|
||||
// - "Base": strictly core schema files, no plugins. (go:embed-able)
|
||||
// - "Dist": "Base" + plugins that ship with vanilla Grafana (go:embed-able)
|
||||
// - "Instance": "Dist" + the non-core plugins available in an actual, running Grafana
|
||||
|
||||
// BaseLoadPaths contains the configuration for loading a DistDashboard
|
||||
type BaseLoadPaths struct {
|
||||
// BaseCueFS should be rooted at a directory containing the filesystem layout
|
||||
// expected to exist at github.com/grafana/grafana/cue.
|
||||
BaseCueFS fs.FS
|
||||
|
||||
// DistPluginCueFS should point to some fs path (TBD) under which all core
|
||||
// plugins live.
|
||||
DistPluginCueFS fs.FS
|
||||
|
||||
// InstanceCueFS should point to a root dir in which non-core plugins live.
|
||||
// Normal case will be that this only happens when an actual Grafana
|
||||
// instance is making the call, and has a plugin dir to offer - though
|
||||
// external tools could always create their own dirs shaped like a Grafana
|
||||
// plugin dir, and point to those.
|
||||
InstanceCueFS fs.FS
|
||||
}
|
||||
|
||||
func GetDefaultLoadPaths() BaseLoadPaths {
|
||||
return BaseLoadPaths{
|
||||
BaseCueFS: grafana.CoreSchema,
|
||||
DistPluginCueFS: grafana.PluginSchema,
|
||||
}
|
||||
}
|
||||
|
||||
// toOverlay converts all .cue files in the fs.FS into Source entries in an
|
||||
// overlay map, as expected by load.Config.
|
||||
//
|
||||
// Each entry is placed in the map with the provided prefix - which must be an
|
||||
// absolute path - ahead of the actual path of the added file within the fs.FS.
|
||||
//
|
||||
// The function writes into the provided overlay map, to facilitate the
|
||||
// construction of a single overlay map from multiple fs.FS.
|
||||
//
|
||||
// All files reachable by walking the provided fs.FS are added to the overlay
|
||||
// map, on the premise that control over the FS is sufficient to allow any
|
||||
// desired filtering.
|
||||
func toOverlay(prefix string, vfs fs.FS, overlay map[string]load.Source) error {
|
||||
if !filepath.IsAbs(prefix) {
|
||||
return fmt.Errorf("must provide absolute path prefix when generating cue overlay, got %q", prefix)
|
||||
}
|
||||
err := fs.WalkDir(vfs, ".", (func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := vfs.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overlay[filepath.Join(prefix, path)] = load.FromBytes(b)
|
||||
return nil
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package load
|
||||
|
||||
const prefix = "/"
|
@ -1,6 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package load
|
||||
|
||||
const prefix = "C:\\"
|
@ -1,214 +0,0 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
)
|
||||
|
||||
var panelSubpath = cue.MakePath(cue.Def("#Panel"))
|
||||
|
||||
var dashboardDir = filepath.Join("packages", "grafana-schema", "src", "scuemata", "dashboard")
|
||||
|
||||
func defaultOverlay(p BaseLoadPaths) (map[string]load.Source, error) {
|
||||
overlay := make(map[string]load.Source)
|
||||
|
||||
if err := toOverlay(prefix, p.BaseCueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := toOverlay(prefix, p.DistPluginCueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return overlay, nil
|
||||
}
|
||||
|
||||
// BaseDashboardFamily loads the family of schema representing the "Base" variant of
|
||||
// a Grafana dashboard: the core-defined dashboard schema that applies universally to
|
||||
// all dashboards, independent of any plugins.
|
||||
//
|
||||
// The returned VersionedCueSchema will always be the oldest schema in the
|
||||
// family: the 0.0 schema. schema.Find() provides easy traversal to newer schema
|
||||
// versions.
|
||||
func BaseDashboardFamily(p BaseLoadPaths) (schema.VersionedCueSchema, error) {
|
||||
v, err := baseDashboardFamily(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buildGenericScuemata(v)
|
||||
}
|
||||
|
||||
// Helper that gets the entire scuemata family, for reuse by Dist/Instance callers.
|
||||
func baseDashboardFamily(p BaseLoadPaths) (cue.Value, error) {
|
||||
overlay, err := defaultOverlay(p)
|
||||
if err != nil {
|
||||
return cue.Value{}, err
|
||||
}
|
||||
|
||||
cfg := &load.Config{
|
||||
Overlay: overlay,
|
||||
ModuleRoot: prefix,
|
||||
Module: "github.com/grafana/grafana",
|
||||
Dir: filepath.Join(prefix, dashboardDir),
|
||||
}
|
||||
inst := ctx.BuildInstance(load.Instances(nil, cfg)[0])
|
||||
if inst.Err() != nil {
|
||||
cueError := schema.WrapCUEError(inst.Err())
|
||||
if inst.Err() != nil {
|
||||
return cue.Value{}, cueError
|
||||
}
|
||||
}
|
||||
|
||||
famval := inst.LookupPath(cue.MakePath(cue.Str("Family")))
|
||||
if !famval.Exists() {
|
||||
return cue.Value{}, errors.New("dashboard schema family did not exist at expected path in expected file")
|
||||
}
|
||||
|
||||
return famval, nil
|
||||
}
|
||||
|
||||
// DistDashboardFamily loads the family of schema representing the "Dist"
|
||||
// variant of a Grafana dashboard: the "Base" variant (see
|
||||
// BaseDashboardFamily()), but constrained such that all substructures (e.g.
|
||||
// panels) must be valid with respect to the schemas provided by the core
|
||||
// plugins that ship with Grafana.
|
||||
//
|
||||
// The returned VersionedCueSchema will always be the oldest schema in the
|
||||
// family: the 0.0 schema. schema.Find() provides easy traversal to newer schema
|
||||
// versions.
|
||||
func DistDashboardFamily(p BaseLoadPaths) (schema.VersionedCueSchema, error) {
|
||||
famval, err := baseDashboardFamily(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scuemap, err := loadPanelScuemata(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO see if unifying into the expected form in a loop, then unifying that
|
||||
// consolidated form improves performance
|
||||
for typ, fam := range scuemap {
|
||||
famval = famval.FillPath(cue.MakePath(cue.Str("compose"), cue.Str("Panel"), cue.Str(typ)), fam)
|
||||
}
|
||||
head, err := buildGenericScuemata(famval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO sloppy duplicate logic of what's in readPanelModels(), for now
|
||||
all := make(map[string]schema.VersionedCueSchema)
|
||||
for id, val := range scuemap {
|
||||
fam, err := buildGenericScuemata(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all[id] = fam
|
||||
}
|
||||
|
||||
var first, prev *compositeDashboardSchema
|
||||
for head != nil {
|
||||
cds := &compositeDashboardSchema{
|
||||
base: head,
|
||||
actual: head.CUE(),
|
||||
panelFams: all,
|
||||
// TODO migrations
|
||||
migration: terminalMigrationFunc,
|
||||
}
|
||||
|
||||
if prev == nil {
|
||||
first = cds
|
||||
} else {
|
||||
prev.next = cds
|
||||
}
|
||||
|
||||
prev = cds
|
||||
head = head.Successor()
|
||||
}
|
||||
return first, nil
|
||||
}
|
||||
|
||||
type compositeDashboardSchema struct {
|
||||
// The base/root dashboard schema
|
||||
base schema.VersionedCueSchema
|
||||
actual cue.Value
|
||||
next *compositeDashboardSchema
|
||||
migration migrationFunc
|
||||
panelFams map[string]schema.VersionedCueSchema
|
||||
}
|
||||
|
||||
// Validate checks that the resource is correct with respect to the schema.
|
||||
func (cds *compositeDashboardSchema) Validate(r schema.Resource) error {
|
||||
name := r.Name
|
||||
if name == "" {
|
||||
name = "resource"
|
||||
}
|
||||
rv := ctx.CompileString(r.Value.(string), cue.Filename(name))
|
||||
if rv.Err() != nil {
|
||||
return rv.Err()
|
||||
}
|
||||
return cds.actual.Unify(rv).Validate(cue.Concrete(true))
|
||||
}
|
||||
|
||||
// CUE returns the cue.Value representing the actual schema.
|
||||
func (cds *compositeDashboardSchema) CUE() cue.Value {
|
||||
return cds.actual
|
||||
}
|
||||
|
||||
// Version reports the major and minor versions of the schema.
|
||||
func (cds *compositeDashboardSchema) Version() (major int, minor int) {
|
||||
return cds.base.Version()
|
||||
}
|
||||
|
||||
// Returns the next VersionedCueSchema
|
||||
func (cds *compositeDashboardSchema) Successor() schema.VersionedCueSchema {
|
||||
if cds.next == nil {
|
||||
// Untyped nil, allows `<sch> == nil` checks to work as people expect
|
||||
return nil
|
||||
}
|
||||
return cds.next
|
||||
}
|
||||
|
||||
func (cds *compositeDashboardSchema) Migrate(x schema.Resource) (schema.Resource, schema.VersionedCueSchema, error) { // TODO restrict input/return type to concrete
|
||||
r, sch, err := cds.migration(x.Value)
|
||||
if err != nil || sch == nil {
|
||||
// TODO fix sloppy types
|
||||
r = x.Value.(cue.Value)
|
||||
}
|
||||
|
||||
return schema.Resource{Value: r}, sch, nil
|
||||
}
|
||||
|
||||
func (cds *compositeDashboardSchema) LatestPanelSchemaFor(id string) (schema.VersionedCueSchema, error) {
|
||||
// So much slop rn, but it's OK because i FINALLY know where this is going!
|
||||
psch, has := cds.panelFams[id]
|
||||
if !has {
|
||||
// TODO typed errors
|
||||
return nil, fmt.Errorf("unknown panel plugin type %q", id)
|
||||
}
|
||||
|
||||
latest := schema.Find(psch, schema.Latest())
|
||||
// FIXME this relies on old sloppiness
|
||||
sch := &genericVersionedSchema{
|
||||
actual: cds.base.CUE().LookupPath(panelSubpath).Unify(mapPanelModel(id, latest)),
|
||||
}
|
||||
sch.major, sch.minor = latest.Version()
|
||||
|
||||
return sch, nil
|
||||
}
|
||||
|
||||
// One-off special interface for dashboard composite schema, until the composite
|
||||
// dashboard schema pattern is fully generalized.
|
||||
//
|
||||
// NOTE: THIS IS A TEMPORARY TYPE. IT WILL BE REPLACED WITH A GENERIC INTERFACE
|
||||
// TO REPRESENT COMPOSITIONAL SCHEMA FAMILY PRIOR TO GRAFANA 8. UPDATING WILL
|
||||
// SHOULD BE TRIVIAL, BUT IT WILL CAUSE BREAKAGES.
|
||||
type CompositeDashboardSchema interface {
|
||||
schema.VersionedCueSchema
|
||||
LatestPanelSchemaFor(id string) (schema.VersionedCueSchema, error)
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
)
|
||||
|
||||
// getBaseScuemata attempts to load the base scuemata family and schema
|
||||
// definitions on which all Grafana scuemata rely.
|
||||
//
|
||||
// TODO probably cache this or something
|
||||
func getBaseScuemata(p BaseLoadPaths) (cue.Value, error) {
|
||||
overlay := make(map[string]load.Source)
|
||||
|
||||
if err := toOverlay(filepath.Join(prefix, "grafana"), p.BaseCueFS, overlay); err != nil {
|
||||
return cue.Value{}, err
|
||||
}
|
||||
|
||||
cfg := &load.Config{
|
||||
Overlay: overlay,
|
||||
Package: "scuemata",
|
||||
// TODO Semantics of loading instances is quite confusing. This 'Dir'
|
||||
// field is a case in point. It must be set to "/" in order for the
|
||||
// overlay to be searched and have all files loaded in the cue/scuemata
|
||||
// directory. (This isn't necessary when loading individual .cue files.)
|
||||
// But anchoring a search at root seems like we're begging for
|
||||
// vulnerabilities where Grafana can read and print out anything on the
|
||||
// filesystem, which can be a disclosure problem, unless we're
|
||||
// absolutely sure the search is within a virtual filesystem. Which i'm
|
||||
// not.
|
||||
//
|
||||
// And no, changing the toOverlay() to have a subpath and the
|
||||
// load.Instances to mirror that subpath does not allow us to get rid of
|
||||
// this "/".
|
||||
Dir: prefix,
|
||||
}
|
||||
|
||||
res := ctx.BuildInstance(load.Instances([]string{
|
||||
filepath.Join(prefix, "grafana", "cue", "scuemata", "scuemata.cue"),
|
||||
filepath.Join(prefix, "grafana", "cue", "scuemata", "panel-plugin.cue"),
|
||||
}, cfg)[0])
|
||||
return res, res.Err()
|
||||
}
|
||||
|
||||
func buildGenericScuemata(famval cue.Value) (schema.VersionedCueSchema, error) {
|
||||
// TODO verify subsumption by #Family; renders many
|
||||
// error checks below unnecessary
|
||||
majiter, err := famval.LookupPath(cue.MakePath(cue.Str("lineages"))).List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var major int
|
||||
var first, lastgvs *genericVersionedSchema
|
||||
for majiter.Next() {
|
||||
var minor int
|
||||
miniter, _ := majiter.Value().List()
|
||||
for miniter.Next() {
|
||||
gvs := &genericVersionedSchema{
|
||||
actual: miniter.Value(),
|
||||
major: major,
|
||||
minor: minor,
|
||||
// This gets overwritten on all but the very final schema
|
||||
migration: terminalMigrationFunc,
|
||||
}
|
||||
|
||||
if minor != 0 {
|
||||
// TODO Verify that this schema is backwards compat with prior.
|
||||
// Create an implicit migration operation on the prior schema.
|
||||
lastgvs.migration = implicitMigration(gvs.actual, gvs)
|
||||
lastgvs.next = gvs
|
||||
} else if major != 0 {
|
||||
lastgvs.next = gvs
|
||||
// x.0. There should exist an explicit migration definition;
|
||||
// load it up and ready it for use, and place it on the final
|
||||
// schema in the prior sequence.
|
||||
//
|
||||
// Also...should at least try to make sure it's pointing at the
|
||||
// expected schema, to maintain our invariants?
|
||||
|
||||
// TODO impl
|
||||
} else {
|
||||
first = gvs
|
||||
}
|
||||
lastgvs = gvs
|
||||
minor++
|
||||
}
|
||||
major++
|
||||
}
|
||||
|
||||
return first, nil
|
||||
}
|
||||
|
||||
type genericVersionedSchema struct {
|
||||
actual cue.Value
|
||||
major int
|
||||
minor int
|
||||
next *genericVersionedSchema
|
||||
migration migrationFunc
|
||||
}
|
||||
|
||||
// Validate checks that the resource is correct with respect to the schema.
|
||||
func (gvs *genericVersionedSchema) Validate(r schema.Resource) error {
|
||||
name := r.Name
|
||||
if name == "" {
|
||||
name = "resource"
|
||||
}
|
||||
rv := ctx.CompileString(r.Value.(string), cue.Filename(name))
|
||||
if rv.Err() != nil {
|
||||
return rv.Err()
|
||||
}
|
||||
return gvs.actual.Unify(rv).Validate(cue.Concrete(true))
|
||||
}
|
||||
|
||||
// CUE returns the cue.Value representing the actual schema.
|
||||
func (gvs *genericVersionedSchema) CUE() cue.Value {
|
||||
return gvs.actual
|
||||
}
|
||||
|
||||
// Version reports the major and minor versions of the schema.
|
||||
func (gvs *genericVersionedSchema) Version() (major int, minor int) {
|
||||
return gvs.major, gvs.minor
|
||||
}
|
||||
|
||||
// Returns the next VersionedCueSchema
|
||||
func (gvs *genericVersionedSchema) Successor() schema.VersionedCueSchema {
|
||||
if gvs.next == nil {
|
||||
// Untyped nil, allows `<sch> == nil` checks to work as people expect
|
||||
return nil
|
||||
}
|
||||
return gvs.next
|
||||
}
|
||||
|
||||
// Migrate transforms a resource into a new Resource that is correct with
|
||||
// respect to its Successor schema.
|
||||
func (gvs *genericVersionedSchema) Migrate(x schema.Resource) (schema.Resource, schema.VersionedCueSchema, error) { // TODO restrict input/return type to concrete
|
||||
r, sch, err := gvs.migration(x.Value)
|
||||
if err != nil || sch == nil {
|
||||
r = x.Value.(cue.Value)
|
||||
}
|
||||
|
||||
return schema.Resource{Value: r}, sch, nil
|
||||
}
|
||||
|
||||
type migrationFunc func(x interface{}) (cue.Value, schema.VersionedCueSchema, error)
|
||||
|
||||
var terminalMigrationFunc = func(x interface{}) (cue.Value, schema.VersionedCueSchema, error) {
|
||||
// TODO send back the input
|
||||
return cue.Value{}, nil, nil
|
||||
}
|
||||
|
||||
// panic if called
|
||||
// var panicMigrationFunc = func(x interface{}) (cue.Value, schema.VersionedCueSchema, error) {
|
||||
// panic("migrations are not yet implemented")
|
||||
// }
|
||||
|
||||
// Creates a func to perform a "migration" that simply unifies the input
|
||||
// artifact (which is expected to have already have been validated against an
|
||||
// earlier schema) with a later schema.
|
||||
func implicitMigration(v cue.Value, next schema.VersionedCueSchema) migrationFunc {
|
||||
return func(x interface{}) (cue.Value, schema.VersionedCueSchema, error) {
|
||||
w := v.FillPath(cue.Path{}, x)
|
||||
// TODO is it possible that migration would be successful, but there
|
||||
// still exists some error here? Need to better understand internal CUE
|
||||
// erroring rules? seems like incomplete cue.Value may always an Err()?
|
||||
//
|
||||
// TODO should check concreteness here? Or can we guarantee a priori it
|
||||
// can be made concrete simply by looking at the schema, before
|
||||
// implicitMigration() is called to create this function?
|
||||
if w.Err() != nil {
|
||||
return w, nil, w.Err()
|
||||
}
|
||||
return w, next, w.Err()
|
||||
}
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/errors"
|
||||
"cuelang.org/go/cue/load"
|
||||
cuejson "cuelang.org/go/pkg/encoding/json"
|
||||
"github.com/grafana/grafana/pkg/coremodel/dashboard"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
"github.com/laher/mergefs"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
p = GetDefaultLoadPaths()
|
||||
update = flag.Bool("update", false, "update golden files")
|
||||
)
|
||||
|
||||
type testfunc func(*testing.T, schema.VersionedCueSchema, []byte, fs.FileInfo, string)
|
||||
|
||||
// for now we keep the validdir as input parameter since for trim apply default we can't use devenv directory yet,
|
||||
// otherwise we can hardcoded validdir and just pass the testtype is more than enough.
|
||||
// TODO: remove validdir once we can test directly with devenv folder
|
||||
var doTestAgainstDevenv = func(sch schema.VersionedCueSchema, validdir string, fn testfunc) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
require.NoError(t, filepath.Walk(validdir, func(path string, d fs.FileInfo, err error) error {
|
||||
require.NoError(t, err)
|
||||
|
||||
if d.IsDir() || filepath.Ext(d.Name()) != ".json" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ignore gosec warning G304 since it's a test
|
||||
// nolint:gosec
|
||||
b, err := os.Open(path)
|
||||
require.NoError(t, err, "failed to open dashboard file")
|
||||
|
||||
jtree := make(map[string]interface{})
|
||||
byt, err := io.ReadAll(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, json.Unmarshal(byt, &jtree))
|
||||
if oldschemav, has := jtree["schemaVersion"]; !has {
|
||||
t.Logf("no schemaVersion in %s", path)
|
||||
return nil
|
||||
} else {
|
||||
if !(oldschemav.(float64) > dashboard.HandoffSchemaVersion-1) {
|
||||
if testing.Verbose() {
|
||||
t.Logf("schemaVersion is %v, older than %v, skipping %s", oldschemav, dashboard.HandoffSchemaVersion-1, path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||
fn(t, sch, byt, d, path)
|
||||
})
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// Basic well-formedness tests on core scuemata.
|
||||
func TestScuemataBasics(t *testing.T) {
|
||||
all := make(map[string]schema.VersionedCueSchema)
|
||||
|
||||
dash, err := BaseDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading base dashboard scuemata")
|
||||
all["basedash"] = dash
|
||||
|
||||
ddash, err := DistDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading dist dashboard scuemata")
|
||||
all["distdash"] = ddash
|
||||
|
||||
for set, sch := range all {
|
||||
t.Run(set, func(t *testing.T) {
|
||||
require.NotNil(t, sch, "scuemata for %q linked to empty chain", set)
|
||||
|
||||
maj, min := sch.Version()
|
||||
t.Run(fmt.Sprintf("%v.%v", maj, min), func(t *testing.T) {
|
||||
cv := sch.CUE()
|
||||
t.Run("Exists", func(t *testing.T) {
|
||||
require.True(t, cv.Exists(), "cue value for schema does not exist")
|
||||
})
|
||||
t.Run("Validate", func(t *testing.T) {
|
||||
require.NoError(t, cv.Validate(), "all schema should be valid with respect to basic CUE rules")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevenvDashboardValidity(t *testing.T) {
|
||||
// TODO will need to expand this appropriately when the scuemata contain
|
||||
// more than one schema
|
||||
var validdir = filepath.Join("..", "..", "..", "devenv", "dev-dashboards")
|
||||
dash, err := BaseDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading base dashboard scuemata")
|
||||
dashboardValidity := func(t *testing.T, sch schema.VersionedCueSchema, byt []byte, d fs.FileInfo, path string) {
|
||||
err := sch.Validate(schema.Resource{Value: string(byt), Name: path})
|
||||
if err != nil {
|
||||
// Testify trims errors to short length. We want the full text
|
||||
errstr := errors.Details(err, nil)
|
||||
t.Log(errstr)
|
||||
if strings.Contains(errstr, "null") {
|
||||
t.Log("validation failure appears to involve nulls - see if scripts/stripnulls.sh has any effect?")
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
t.Run("base", doTestAgainstDevenv(dash, validdir, dashboardValidity))
|
||||
|
||||
ddash, err := DistDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading dist dashboard scuemata")
|
||||
t.Run("dist", doTestAgainstDevenv(ddash, validdir, dashboardValidity))
|
||||
}
|
||||
|
||||
// TO update the golden file located in pkg/schema/testdata/devenvgoldenfiles
|
||||
// run go test -v ./pkg/schema/load/... -update
|
||||
func TestUpdateDevenvDashboardGoldenFiles(t *testing.T) {
|
||||
flag.Parse()
|
||||
if *update {
|
||||
ddash, err := DistDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading dist dashboard scuemata")
|
||||
var validdir = filepath.Join("..", "..", "..", "devenv", "dev-dashboards")
|
||||
goldenFileUpdate := func(t *testing.T, sch schema.VersionedCueSchema, byt []byte, d fs.FileInfo, _ string) {
|
||||
dsSchema, err := schema.SearchAndValidate(sch, string(byt))
|
||||
require.NoError(t, err)
|
||||
|
||||
origin, err := schema.ApplyDefaults(schema.Resource{Value: string(byt)}, dsSchema.CUE())
|
||||
require.NoError(t, err)
|
||||
|
||||
var prettyJSON bytes.Buffer
|
||||
err = json.Indent(&prettyJSON, []byte(origin.Value.(string)), "", "\t")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join("..", "testdata", "devenvgoldenfiles", d.Name()), prettyJSON.Bytes(), 0644)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
t.Run("updategoldenfile", doTestAgainstDevenv(ddash, validdir, goldenFileUpdate))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevenvDashboardTrimApplyDefaults(t *testing.T) {
|
||||
ddash, err := DistDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading dist dashboard scuemata")
|
||||
// TODO will need to expand this appropriately when the scuemata contain
|
||||
// more than one schema
|
||||
validdir := filepath.Join("..", "testdata", "devenvgoldenfiles")
|
||||
trimApplyDefaults := func(t *testing.T, sch schema.VersionedCueSchema, byt []byte, d fs.FileInfo, path string) {
|
||||
dsSchema, err := schema.SearchAndValidate(sch, string(byt))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Trimmed default json file
|
||||
trimmed, err := schema.TrimDefaults(schema.Resource{Value: string(byt)}, dsSchema.CUE())
|
||||
require.NoError(t, err)
|
||||
|
||||
// store the trimmed result into testdata for easy debug
|
||||
out, err := schema.ApplyDefaults(trimmed, dsSchema.CUE())
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, string(byt), out.Value.(string))
|
||||
}
|
||||
t.Run("defaults", doTestAgainstDevenv(ddash, validdir, trimApplyDefaults))
|
||||
}
|
||||
|
||||
func TestPanelValidity(t *testing.T) {
|
||||
t.Skip()
|
||||
validdir := os.DirFS(filepath.Join("testdata", "artifacts", "panels"))
|
||||
|
||||
ddash, err := DistDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading dist dashboard scuemata")
|
||||
|
||||
// TODO hmm, it's awkward for this test's structure to have to pick just one
|
||||
// type of panel plugin, but we can change the test structure. However, is
|
||||
// there any other situation where we want the panel subschema with all
|
||||
// possible disjunctions? If so, maybe the interface needs work. Or maybe
|
||||
// just defer that until the proper generic composite scuemata impl.
|
||||
dpan, err := ddash.(CompositeDashboardSchema).LatestPanelSchemaFor("table")
|
||||
require.NoError(t, err, "error while loading panel subschema")
|
||||
|
||||
require.NoError(t, fs.WalkDir(validdir, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
require.NoError(t, err)
|
||||
|
||||
if d.IsDir() || filepath.Ext(d.Name()) != ".json" {
|
||||
return nil
|
||||
}
|
||||
|
||||
t.Run(path, func(t *testing.T) {
|
||||
// TODO FIXME stop skipping once we actually have the schema filled in
|
||||
// enough that the tests pass, lol
|
||||
|
||||
b, err := validdir.Open(path)
|
||||
require.NoError(t, err, "failed to open panel file")
|
||||
|
||||
err = dpan.Validate(schema.Resource{Value: b})
|
||||
require.NoError(t, err, "panel failed validation")
|
||||
})
|
||||
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
func TestCueErrorWrapper(t *testing.T) {
|
||||
a := fstest.MapFS{
|
||||
filepath.Join(dashboardDir, "dashboard.cue"): &fstest.MapFile{Data: []byte("package dashboard\n{;;;;;;;;}")},
|
||||
}
|
||||
|
||||
filesystem := mergefs.Merge(a, GetDefaultLoadPaths().BaseCueFS)
|
||||
|
||||
var baseLoadPaths = BaseLoadPaths{
|
||||
BaseCueFS: filesystem,
|
||||
DistPluginCueFS: GetDefaultLoadPaths().DistPluginCueFS,
|
||||
}
|
||||
|
||||
_, err := BaseDashboardFamily(baseLoadPaths)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "in file")
|
||||
require.Contains(t, err.Error(), "line: ")
|
||||
|
||||
_, err = DistDashboardFamily(baseLoadPaths)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "in file")
|
||||
require.Contains(t, err.Error(), "line: ")
|
||||
}
|
||||
|
||||
func TestAllPluginsInDist(t *testing.T) {
|
||||
overlay, err := defaultOverlay(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := &load.Config{
|
||||
Overlay: overlay,
|
||||
ModuleRoot: prefix,
|
||||
Module: "github.com/grafana/grafana",
|
||||
Dir: filepath.Join(prefix, dashboardDir, "dist"),
|
||||
Package: "dist",
|
||||
}
|
||||
inst := ctx.BuildInstance(load.Instances(nil, cfg)[0])
|
||||
require.NoError(t, inst.Err())
|
||||
|
||||
dinst := ctx.CompileString(`
|
||||
Family: compose: Panel: {}
|
||||
typs: [for typ, _ in Family.compose.Panel {typ}]
|
||||
`, cue.Filename("str"))
|
||||
require.NoError(t, dinst.Err())
|
||||
|
||||
typs := dinst.Unify(inst).LookupPath(cue.MakePath(cue.Str("typs")))
|
||||
j, err := cuejson.Marshal(typs)
|
||||
require.NoError(t, err)
|
||||
|
||||
var importedPanelTypes, loadedPanelTypes []string
|
||||
require.NoError(t, json.Unmarshal([]byte(j), &importedPanelTypes))
|
||||
|
||||
// TODO a more canonical way of getting all the dist plugin types with
|
||||
// models.cue would be nice.
|
||||
m, err := loadPanelScuemata(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
for typ := range m {
|
||||
loadedPanelTypes = append(loadedPanelTypes, typ)
|
||||
}
|
||||
|
||||
sort.Strings(importedPanelTypes)
|
||||
sort.Strings(loadedPanelTypes)
|
||||
|
||||
require.Equal(t, loadedPanelTypes, importedPanelTypes, "%s/family.cue needs updating, it must compose the same set of panel plugin models that are found by the plugin loader", cfg.Dir)
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
)
|
||||
|
||||
// mapPanelModel maps a schema from the #PanelModel form in which it's declared
|
||||
// in a plugin's model.cue to the structure in which it actually appears in the
|
||||
// dashboard schema.
|
||||
// TODO remove, this is old sloppy hacks
|
||||
func mapPanelModel(id string, vcs schema.VersionedCueSchema) cue.Value {
|
||||
maj, min := vcs.Version()
|
||||
// Ignore err return, this can't fail to compile
|
||||
inter := ctx.CompileString(fmt.Sprintf(`
|
||||
in: {
|
||||
type: %q
|
||||
v: {
|
||||
maj: %d
|
||||
min: %d
|
||||
}
|
||||
model: {...}
|
||||
}
|
||||
result: {
|
||||
type: in.type,
|
||||
panelSchema: maj: in.v.maj
|
||||
panelSchema: min: in.v.min
|
||||
options: in.model.PanelOptions
|
||||
fieldConfig: defaults: custom: {}
|
||||
if in.model.PanelFieldConfig != _|_ {
|
||||
fieldConfig: defaults: custom: in.model.PanelFieldConfig
|
||||
}
|
||||
}
|
||||
`, id, maj, min), cue.Filename(fmt.Sprintf("%s-glue-panelComposition", id)))
|
||||
|
||||
// TODO validate, especially with #PanelModel
|
||||
return inter.FillPath(cue.MakePath(cue.Str("in"), cue.Str("model")), vcs.CUE()).LookupPath(cue.MakePath(cue.Str(("result"))))
|
||||
}
|
||||
|
||||
func loadPanelScuemata(p BaseLoadPaths) (map[string]cue.Value, error) {
|
||||
overlay := make(map[string]load.Source)
|
||||
|
||||
if err := toOverlay(prefix, p.BaseCueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := toOverlay(prefix, p.DistPluginCueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base, err := getBaseScuemata(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pmf := base.LookupPath(cue.MakePath(cue.Def("#PanelFamily")))
|
||||
if !pmf.Exists() {
|
||||
return nil, errors.New("could not locate #PanelFamily definition")
|
||||
}
|
||||
|
||||
all := make(map[string]cue.Value)
|
||||
err = fs.WalkDir(p.DistPluginCueFS, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() || d.Name() != "plugin.json" {
|
||||
return nil
|
||||
}
|
||||
|
||||
dpath := filepath.Dir(path)
|
||||
// For now, skip plugins without a models.cue
|
||||
_, err = p.DistPluginCueFS.Open(filepath.Join(dpath, "models.cue"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fi, err := p.DistPluginCueFS.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := ioutil.ReadAll(fi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jmap := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &jmap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iid, has := jmap["id"]
|
||||
if !has || jmap["type"] != "panel" {
|
||||
return errors.New("no type field in plugin.json or not a panel type plugin")
|
||||
}
|
||||
id := iid.(string)
|
||||
|
||||
cfg := &load.Config{
|
||||
Package: "grafanaschema",
|
||||
Overlay: overlay,
|
||||
}
|
||||
|
||||
li := load.Instances([]string{filepath.Join("/", dpath, "models.cue")}, cfg)
|
||||
imod := ctx.BuildInstance(li[0])
|
||||
if imod.Err() != nil {
|
||||
return imod.Err()
|
||||
}
|
||||
|
||||
// Get the Family declaration in the models.cue file...
|
||||
pmod := imod.LookupPath(cue.MakePath(cue.Str("Panel")))
|
||||
if !pmod.Exists() {
|
||||
return fmt.Errorf("%s does not contain a declaration of its models at path 'Family'", path)
|
||||
}
|
||||
|
||||
// Ensure the declared value is subsumed by/correct wrt #PanelFamily
|
||||
// TODO not actually sure that Final is what we want here.
|
||||
if err := pmf.Subsume(pmod, cue.Final()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
all[id] = pmod
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
3
pkg/schema/load/testdata/artifacts/README.md
vendored
3
pkg/schema/load/testdata/artifacts/README.md
vendored
@ -1,3 +0,0 @@
|
||||
All artifact JSON contained in these subdirectories should be valid. Invalid
|
||||
JSON is handled elsewhere, as it must be coupled with expected error messages
|
||||
for testing purposes.
|
@ -1,152 +0,0 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_GDEV-TESTDATA",
|
||||
"label": "gdev-testdata",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "testdata",
|
||||
"pluginName": "TestData DB"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "7.5.0-pre"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "table",
|
||||
"name": "Table",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "testdata",
|
||||
"name": "TestData DB",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"rawQuery": "wtf",
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"id": 42,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"filterable": false
|
||||
},
|
||||
"decimals": 3,
|
||||
"unit": "watt"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Max"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "lcd-gauge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "A"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"points": [],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk_table",
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "table",
|
||||
"panelSchema": {
|
||||
"maj": 0,
|
||||
"min": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"schemaVersion": 27,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timezone": "browser",
|
||||
"title": "with table",
|
||||
"uid": "emal8gQMz",
|
||||
"version": 2
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"filterable": false
|
||||
},
|
||||
"decimals": 3,
|
||||
"unit": "watt"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Max"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "lcd-gauge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "A"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 200
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"points": [],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk_table",
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "table",
|
||||
"panelSchema": {
|
||||
"maj": 0,
|
||||
"min": 0
|
||||
}
|
||||
}
|
1
pkg/schema/load/testdata/mergefs/b/z/foo.cue
vendored
1
pkg/schema/load/testdata/mergefs/b/z/foo.cue
vendored
@ -1 +0,0 @@
|
||||
foo
|
@ -1,104 +0,0 @@
|
||||
// Copyright 2021 Grafana Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/cue/ui:grafanaschema"
|
||||
)
|
||||
|
||||
// TODO should we remove Family, and make lineages and migrations top-level values?
|
||||
// It's easy to do, and arguably increases clarity of this crucial file by
|
||||
// reducing one layer of nesting. But it sorta requires understanding that CUE
|
||||
// also thinks of an entire file (aka, an "instance") as a struct in order for
|
||||
// it to make sense that the file itself is schematized by #PanelFamily. What's
|
||||
// the best DX here?
|
||||
|
||||
// "Family" must be an instance of the #PanelFamily type, defined in
|
||||
// cue/scuemata/panel-plugin.cue. This ensures some key invariants:
|
||||
//
|
||||
// - lineages is an array of arrays. Outer array is major version, inner is minor.
|
||||
// (This IS NOT semver, though.)
|
||||
// - Within a single seq, each successive schema is backwards compatible with
|
||||
// the prior schema. (See, it's not semver. No special rules for v0.)
|
||||
// - For each seq/major version after the first, there exists a migration
|
||||
// that allows us to transform a resource compliant with the old version of
|
||||
// the schema into one compliant with the new one.
|
||||
//
|
||||
// That's right, we've schematized our schema declarations. Not all above
|
||||
// invariants are enforced right now, but they must be before launch.
|
||||
//
|
||||
// Grafana won't need to rely on multiple versions of schema until after this
|
||||
// system is released with Grafana 8. But it needs to be in place at the moment
|
||||
// Grafana 8 is released - especially for plugins, which have their own release
|
||||
// cycle, and could need to make breaking changes very shortly after v8's release.
|
||||
Family: {
|
||||
lineages: [
|
||||
[
|
||||
{ // v0.0. The actual schema is the contents of this struct.
|
||||
PanelOptions: {
|
||||
frameIndex: number | *0
|
||||
showHeader: bool | *true
|
||||
sortBy?: [...ui.TableSortByFieldState]
|
||||
}
|
||||
PanelFieldConfig: {
|
||||
width?: int
|
||||
align?: *null | string
|
||||
displayMode?: string | *"auto" // TODO? TableCellDisplayMode
|
||||
filterable?: bool
|
||||
}
|
||||
},
|
||||
{ // v0.1
|
||||
lineages[0][0]
|
||||
PanelOptions: foo: string | *"foo"
|
||||
}
|
||||
],
|
||||
[
|
||||
{ // v1.0 - breaking changes vs. v0.1 in this struct.
|
||||
PanelOptions: {
|
||||
frameIndex: number | *0
|
||||
includeHeader: bool | *true
|
||||
sortBy?: [...ui.TableSortByFieldState]
|
||||
}
|
||||
PanelFieldConfig: {
|
||||
width?: int
|
||||
align?: string
|
||||
displayMode?: string
|
||||
}
|
||||
}
|
||||
],
|
||||
]
|
||||
migrations: [
|
||||
{ // maps from v0.1 to v1.0
|
||||
// TODO it's not good that the user has to specify these. Should be
|
||||
// implicit, since we don't want to allow any actual choice here.
|
||||
// But NOT having it also means CUE can't actually tell if the
|
||||
// _rel definition makes any sense at all. UGHHH. Would it be
|
||||
// better to put these directly on the lineages?
|
||||
from: lineages[0][1]
|
||||
to: lineages[1][0]
|
||||
rel: {
|
||||
PanelOptions: {
|
||||
frameIndex: from.PanelOptions.frameIndex
|
||||
includeHeader: from.PanelOptions.showHeader
|
||||
if from.PanelOptions.sortBy != _|_ {
|
||||
sortBy: from.PanelOptions.sortBy | *null
|
||||
}
|
||||
}
|
||||
PanelFieldConfig: from.PanelFieldConfig
|
||||
}
|
||||
result: rel & to
|
||||
}
|
||||
]
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Sample plugin with lineage",
|
||||
"id": "with-lineage",
|
||||
|
||||
"info": {
|
||||
"description": "Show how complex history may work"
|
||||
}
|
||||
}
|
@ -1,573 +0,0 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
errs "cuelang.org/go/cue/errors"
|
||||
cuejson "cuelang.org/go/pkg/encoding/json"
|
||||
)
|
||||
|
||||
// CueError wraps Errors caused by malformed cue files.
|
||||
type CueError struct {
|
||||
ErrorMap map[int]string
|
||||
}
|
||||
|
||||
// Error func needed to implement standard golang error
|
||||
func (cErr *CueError) Error() string {
|
||||
var errorString string
|
||||
if cErr.ErrorMap != nil {
|
||||
for k, v := range cErr.ErrorMap {
|
||||
errorString = errorString + fmt.Sprintf("line: %d, %s \n", k, v)
|
||||
}
|
||||
}
|
||||
return errorString
|
||||
}
|
||||
|
||||
// CueSchema represents a single, complete CUE-based schema that can perform
|
||||
// operations on Resources.
|
||||
//
|
||||
// All CueSchema MUST EITHER:
|
||||
// - Be a VersionedCueSchema, and be the latest version in the latest lineage in a Family
|
||||
// - Return non-nil from Successor(), and a procedure to Migrate() a Resource to that successor schema
|
||||
//
|
||||
// By definition, VersionedCueSchema are within a lineage. As long as lineage
|
||||
// backwards compatibility invariants hold, migration to a VersionedCueSchema to
|
||||
// a successor schema in their lineage is trivial: simply unify the Resource
|
||||
// with the successor schema.
|
||||
type CueSchema interface {
|
||||
// Validate checks that the resource is correct with respect to the schema.
|
||||
Validate(Resource) error
|
||||
|
||||
// Migrate transforms a Resource into a new Resource that is correct with
|
||||
// respect to its Successor schema. It returns the transformed resource,
|
||||
// the schema to which the resource now conforms, and any errors that
|
||||
// may have occurred during the migration.
|
||||
//
|
||||
// No migration occurs and the input Resource is returned in two cases:
|
||||
//
|
||||
// - The migration encountered an error; the third return is non-nil.
|
||||
// - There exists no schema to migrate to; the second and third return are nil.
|
||||
//
|
||||
// Note that the returned schema is always a VersionedCueSchema. This
|
||||
// reflects a key design invariant of the system: all migrations, whether
|
||||
// they begin from a schema inside or outside of the Family, must land
|
||||
// somewhere on a Family's sequence of schemata.
|
||||
Migrate(Resource) (Resource, VersionedCueSchema, error)
|
||||
|
||||
// Successor returns the VersionedCueSchema to which this CueSchema can migrate a
|
||||
// Resource.
|
||||
Successor() VersionedCueSchema
|
||||
|
||||
// CUE returns the cue.Value representing the actual schema.
|
||||
CUE() cue.Value
|
||||
}
|
||||
|
||||
// VersionedCueSchema are CueSchema that are part of a backwards-compatible
|
||||
// versioned lineage.
|
||||
type VersionedCueSchema interface {
|
||||
CueSchema
|
||||
|
||||
// Version reports the major and minor versions of the schema.
|
||||
Version() (major, minor int)
|
||||
}
|
||||
|
||||
// SearchAndValidate traverses the family of schemas reachable from the provided
|
||||
// VersionedCueSchema. For each schema, it attempts to validate the provided
|
||||
// value, which may be a byte slice representing valid JSON (TODO YAML), a Go
|
||||
// struct, or cue.Value. If providing a cue.Value that is not fully concrete,
|
||||
// the result is undefined.
|
||||
//
|
||||
// Traversal is performed from the newest schema to the oldest. However, because
|
||||
// newer VersionedCueSchema have no way of directly accessing their predecessors
|
||||
// (they form a singly-linked list), the oldest possible schema should always be
|
||||
// provided - typically, the one returned from the family loader function.
|
||||
//
|
||||
// Failure to validate against any schema in the family is indicated by a
|
||||
// non-nil error return. Success is indicated by a non-nil VersionedCueSchema.
|
||||
// If successful, the returned VersionedCueSchema will be the first one against
|
||||
// which the provided resource passed validation.
|
||||
func SearchAndValidate(s VersionedCueSchema, v interface{}) (VersionedCueSchema, error) {
|
||||
arr := AsArray(s)
|
||||
|
||||
// Work from latest to earliest
|
||||
var err error
|
||||
for o := len(arr) - 1; o >= 0; o-- {
|
||||
for i := len(arr[o]) - 1; i >= 0; i-- {
|
||||
if err = arr[o][i].Validate(Resource{Value: v}); err == nil {
|
||||
return arr[o][i], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO sloppy, return more than last error. Need our own error type that
|
||||
// collates all the individual errors, relates them to the schema that
|
||||
// produced them, and ideally deduplicates repeated errors across each
|
||||
// schema.
|
||||
cueErrors := WrapCUEError(err)
|
||||
if err != nil {
|
||||
return nil, cueErrors
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// AsArray collates all VersionedCueSchema in a Family into a two-dimensional
|
||||
// array. The outer array index corresponds to major version number and inner
|
||||
// array index to minor version number.
|
||||
func AsArray(sch VersionedCueSchema) [][]VersionedCueSchema {
|
||||
var ret [][]VersionedCueSchema
|
||||
var flat []VersionedCueSchema
|
||||
|
||||
// two loops. lazy day, today
|
||||
for sch != nil {
|
||||
flat = append(flat, sch)
|
||||
sch = sch.Successor()
|
||||
}
|
||||
|
||||
for _, sch := range flat {
|
||||
maj, _ := sch.Version()
|
||||
if len(ret) == maj {
|
||||
ret = append(ret, []VersionedCueSchema{})
|
||||
}
|
||||
ret[maj] = append(ret[maj], sch)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Find traverses the chain of VersionedCueSchema until the criteria in the
|
||||
// SearchOption is met.
|
||||
//
|
||||
// If no schema is found that fulfills the criteria, nil is returned. Latest()
|
||||
// and LatestInCurrentMajor() will always succeed, unless the input schema is
|
||||
// nil.
|
||||
func Find(s VersionedCueSchema, opt SearchOption) VersionedCueSchema {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := &ssopt{}
|
||||
opt(p)
|
||||
if err := p.validate(); err != nil {
|
||||
panic(fmt.Sprint("unreachable:", err))
|
||||
}
|
||||
|
||||
switch {
|
||||
case p.latest:
|
||||
for ; s.Successor() != nil; s = s.Successor() {
|
||||
}
|
||||
return s
|
||||
|
||||
case p.latestInCurrentMajor:
|
||||
p.latestInMajor, _ = s.Version()
|
||||
fallthrough
|
||||
|
||||
case p.hasLatestInMajor:
|
||||
imaj, _ := s.Version()
|
||||
if imaj > p.latestInMajor {
|
||||
return nil
|
||||
}
|
||||
|
||||
var last VersionedCueSchema
|
||||
for imaj <= p.latestInMajor {
|
||||
last, s = s, s.Successor()
|
||||
if s == nil {
|
||||
if imaj == p.latestInMajor {
|
||||
return last
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
imaj, _ = s.Version()
|
||||
}
|
||||
return last
|
||||
|
||||
default: // exact
|
||||
for s != nil {
|
||||
maj, min := s.Version()
|
||||
if p.exact == [2]int{maj, min} {
|
||||
return s
|
||||
}
|
||||
s = s.Successor()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SearchOption indicates how far along a chain of schemas an operation should
|
||||
// proceed.
|
||||
type SearchOption sso
|
||||
|
||||
type sso func(p *ssopt)
|
||||
|
||||
type ssopt struct {
|
||||
latest bool
|
||||
latestInMajor int
|
||||
hasLatestInMajor bool
|
||||
latestInCurrentMajor bool
|
||||
exact [2]int
|
||||
}
|
||||
|
||||
func (p *ssopt) validate() error {
|
||||
var which uint16
|
||||
if p.latest {
|
||||
which = which + 1<<1
|
||||
}
|
||||
if p.exact != [2]int{0, 0} {
|
||||
which = which + 1<<2
|
||||
}
|
||||
if p.hasLatestInMajor {
|
||||
if p.latestInMajor != -1 {
|
||||
which = which + 1<<3
|
||||
}
|
||||
} else if p.latestInMajor != 0 {
|
||||
// Disambiguate real zero from default zero
|
||||
return fmt.Errorf("latestInMajor should never be non-zero if hasLatestInMajor is false, got %v", p.latestInMajor)
|
||||
}
|
||||
if p.latestInCurrentMajor {
|
||||
which = which + 1<<4
|
||||
}
|
||||
|
||||
if bits.OnesCount16(which) != 1 {
|
||||
return errors.New("may only pass one SchemaSearchOption")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Latest indicates that traversal will continue to the newest schema in the
|
||||
// newest lineage.
|
||||
func Latest() SearchOption {
|
||||
return func(p *ssopt) {
|
||||
p.latest = true
|
||||
}
|
||||
}
|
||||
|
||||
// LatestInMajor will find the latest schema within the provided major version
|
||||
// lineage. If no lineage exists corresponding to the provided number, traversal
|
||||
// will terminate with an error.
|
||||
func LatestInMajor(maj int) SearchOption {
|
||||
return func(p *ssopt) {
|
||||
p.latestInMajor = maj
|
||||
}
|
||||
}
|
||||
|
||||
// LatestInCurrentMajor will find the newest schema having the same major
|
||||
// version as the schema from which the search begins.
|
||||
func LatestInCurrentMajor() SearchOption {
|
||||
return func(p *ssopt) {
|
||||
p.latestInCurrentMajor = true
|
||||
}
|
||||
}
|
||||
|
||||
// Exact will find the schema with the exact major and minor version number
|
||||
// provided.
|
||||
func Exact(maj, min int) SearchOption {
|
||||
return func(p *ssopt) {
|
||||
p.exact = [2]int{maj, min}
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyDefaults returns a new, concrete copy of the Resource with all paths
|
||||
// that are 1) missing in the Resource AND 2) specified by the schema,
|
||||
// filled with default values specified by the schema.
|
||||
func ApplyDefaults(r Resource, scue cue.Value) (Resource, error) {
|
||||
name := r.Name
|
||||
if name == "" {
|
||||
name = "resource"
|
||||
}
|
||||
rv := scue.Context().CompileString(r.Value.(string), cue.Filename(name))
|
||||
if rv.Err() != nil {
|
||||
return r, rv.Err()
|
||||
}
|
||||
|
||||
rvUnified, err := applyDefaultHelper(rv, scue)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
re, err := convertCUEValueToString(rvUnified)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
return Resource{Value: re}, nil
|
||||
}
|
||||
|
||||
func applyDefaultHelper(input cue.Value, scue cue.Value) (cue.Value, error) {
|
||||
switch scue.IncompleteKind() {
|
||||
case cue.ListKind:
|
||||
// if list element exist
|
||||
ele := scue.LookupPath(cue.MakePath(cue.AnyIndex))
|
||||
|
||||
// if input is not a concrete list, we must have list elements exist to be used to trim defaults
|
||||
if ele.Exists() {
|
||||
if ele.IncompleteKind() == cue.BottomKind {
|
||||
return input, errors.New("can't get the element of list")
|
||||
}
|
||||
iter, err := input.List()
|
||||
if err != nil {
|
||||
return input, errors.New("can't apply defaults for list")
|
||||
}
|
||||
var iterlist []cue.Value
|
||||
for iter.Next() {
|
||||
ref, err := getBranch(ele, iter.Value())
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
re, err := applyDefaultHelper(iter.Value(), ref)
|
||||
if err == nil {
|
||||
iterlist = append(iterlist, re)
|
||||
}
|
||||
}
|
||||
liInstance := scue.Context().NewList(iterlist...)
|
||||
if liInstance.Err() != nil {
|
||||
return input, liInstance.Err()
|
||||
}
|
||||
return liInstance, nil
|
||||
} else {
|
||||
return input.Unify(scue), nil
|
||||
}
|
||||
case cue.StructKind:
|
||||
iter, err := scue.Fields(cue.Optional(true))
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
for iter.Next() {
|
||||
lable, _ := iter.Value().Label()
|
||||
lv := input.LookupPath(cue.MakePath(cue.Str(lable)))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if lv.Exists() {
|
||||
res, err := applyDefaultHelper(lv, iter.Value())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
input = input.FillPath(cue.MakePath(cue.Str(lable)), res)
|
||||
} else if !iter.IsOptional() {
|
||||
input = input.FillPath(cue.MakePath(cue.Str(lable)), iter.Value().Eval())
|
||||
}
|
||||
}
|
||||
return input, nil
|
||||
default:
|
||||
input = input.Unify(scue)
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func convertCUEValueToString(inputCUE cue.Value) (string, error) {
|
||||
re, err := cuejson.Marshal(inputCUE)
|
||||
if err != nil {
|
||||
return re, err
|
||||
}
|
||||
|
||||
result := []byte(re)
|
||||
result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
|
||||
result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
// TrimDefaults returns a new, concrete copy of the Resource where all paths
|
||||
// in the where the values at those paths are the same as the default value
|
||||
// given in the schema.
|
||||
func TrimDefaults(r Resource, scue cue.Value) (Resource, error) {
|
||||
name := r.Name
|
||||
if name == "" {
|
||||
name = "resource"
|
||||
}
|
||||
rvInstance := scue.Context().CompileString(r.Value.(string), cue.Filename(name))
|
||||
if rvInstance.Err() != nil {
|
||||
return r, rvInstance.Err()
|
||||
}
|
||||
|
||||
rv, _, err := removeDefaultHelper(scue, rvInstance)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
re, err := convertCUEValueToString(rv)
|
||||
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
return Resource{Value: re}, nil
|
||||
}
|
||||
|
||||
func getDefault(icue cue.Value) (cue.Value, bool) {
|
||||
d, exist := icue.Default()
|
||||
if exist && d.Kind() == cue.ListKind {
|
||||
len, err := d.Len().Int64()
|
||||
if err != nil {
|
||||
return d, false
|
||||
}
|
||||
var defaultExist bool
|
||||
if len <= 0 {
|
||||
op, vals := icue.Expr()
|
||||
if op == cue.OrOp {
|
||||
for _, val := range vals {
|
||||
vallen, _ := val.Len().Int64()
|
||||
if val.Kind() == cue.ListKind && vallen <= 0 {
|
||||
defaultExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !defaultExist {
|
||||
exist = false
|
||||
}
|
||||
} else {
|
||||
exist = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return d, exist
|
||||
}
|
||||
|
||||
func isCueValueEqual(inputdef cue.Value, input cue.Value) bool {
|
||||
d, exist := getDefault(inputdef)
|
||||
if exist {
|
||||
return input.Subsume(d) == nil && d.Subsume(input) == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func removeDefaultHelper(inputdef cue.Value, input cue.Value) (cue.Value, bool, error) {
|
||||
// To include all optional fields, we need to use inputdef for iteration,
|
||||
// since the lookuppath with optional field doesn't work very well
|
||||
rv := inputdef.Context().CompileString("", cue.Filename("helper"))
|
||||
if rv.Err() != nil {
|
||||
return input, false, rv.Err()
|
||||
}
|
||||
|
||||
switch inputdef.IncompleteKind() {
|
||||
case cue.StructKind:
|
||||
// Get all fields including optional fields
|
||||
iter, err := inputdef.Fields(cue.Optional(true))
|
||||
if err != nil {
|
||||
return rv, false, err
|
||||
}
|
||||
keySet := make(map[string]bool)
|
||||
for iter.Next() {
|
||||
lable, _ := iter.Value().Label()
|
||||
keySet[lable] = true
|
||||
lv := input.LookupPath(cue.MakePath(cue.Str(lable)))
|
||||
if err != nil {
|
||||
return rv, false, err
|
||||
}
|
||||
if lv.Exists() {
|
||||
re, isEqual, err := removeDefaultHelper(iter.Value(), lv)
|
||||
if err == nil && !isEqual {
|
||||
rv = rv.FillPath(cue.MakePath(cue.Str(lable)), re)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get all the fields that are not defined in schema yet for panel
|
||||
iter, err = input.Fields()
|
||||
if err != nil {
|
||||
return rv, false, err
|
||||
}
|
||||
for iter.Next() {
|
||||
lable, _ := iter.Value().Label()
|
||||
if exists := keySet[lable]; !exists {
|
||||
rv = rv.FillPath(cue.MakePath(cue.Str(lable)), iter.Value())
|
||||
}
|
||||
}
|
||||
return rv, false, nil
|
||||
case cue.ListKind:
|
||||
if isCueValueEqual(inputdef, input) {
|
||||
return rv, true, nil
|
||||
}
|
||||
|
||||
// take every element of the list
|
||||
ele := inputdef.LookupPath(cue.MakePath(cue.AnyIndex))
|
||||
|
||||
// if input is not a concrete list, we must have list elements exist to be used to trim defaults
|
||||
if ele.Exists() {
|
||||
if ele.IncompleteKind() == cue.BottomKind {
|
||||
return rv, true, nil
|
||||
}
|
||||
|
||||
iter, err := input.List()
|
||||
if err != nil {
|
||||
return rv, true, nil
|
||||
}
|
||||
var iterlist []cue.Value
|
||||
for iter.Next() {
|
||||
ref, err := getBranch(ele, iter.Value())
|
||||
if err != nil {
|
||||
iterlist = append(iterlist, iter.Value())
|
||||
continue
|
||||
}
|
||||
re, isEqual, err := removeDefaultHelper(ref, iter.Value())
|
||||
if err == nil && !isEqual {
|
||||
iterlist = append(iterlist, re)
|
||||
} else {
|
||||
iterlist = append(iterlist, iter.Value())
|
||||
}
|
||||
}
|
||||
liInstance := inputdef.Context().NewList(iterlist...)
|
||||
return liInstance, false, liInstance.Err()
|
||||
}
|
||||
// now when ele is empty, we don't trim anything
|
||||
return input, false, nil
|
||||
|
||||
default:
|
||||
if isCueValueEqual(inputdef, input) {
|
||||
return input, true, nil
|
||||
}
|
||||
return input, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getBranch(schemaObj cue.Value, concretObj cue.Value) (cue.Value, error) {
|
||||
op, defs := schemaObj.Expr()
|
||||
if op == cue.OrOp {
|
||||
for _, def := range defs {
|
||||
err := def.Unify(concretObj).Validate(cue.Concrete(true))
|
||||
if err == nil {
|
||||
return def, nil
|
||||
}
|
||||
}
|
||||
// no matching branches? wtf
|
||||
return schemaObj, errors.New("no branch is found for list")
|
||||
}
|
||||
return schemaObj, nil
|
||||
}
|
||||
|
||||
// A Resource represents a concrete data object - e.g., JSON
|
||||
// representing a dashboard.
|
||||
//
|
||||
// This type mostly exists to improve readability for users. Having a type that
|
||||
// differentiates cue.Value that represent a schema from cue.Value that
|
||||
// represent a concrete object is quite helpful. It also gives us a working type
|
||||
// for a resource that can be reused across multiple calls, so that re-parsing
|
||||
// isn't necessary.
|
||||
//
|
||||
// TODO this is a terrible way to do this, refactor
|
||||
type Resource struct {
|
||||
Value interface{}
|
||||
Name string
|
||||
}
|
||||
|
||||
// WrapCUEError is a wrapper for cueErrors that occur and are not self explanatory.
|
||||
// If an error is of type cueErr, then iterate through the error array, export line number
|
||||
// and filename, otherwise return usual error.
|
||||
func WrapCUEError(err error) error {
|
||||
var cErr errs.Error
|
||||
m := make(map[int]string)
|
||||
if ok := errors.As(err, &cErr); ok {
|
||||
for _, e := range errs.Errors(cErr) {
|
||||
if e.Position().File() != nil {
|
||||
line := e.Position().Line()
|
||||
m[line] = fmt.Sprintf("%q: in file %s", err, e.Position().File().Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(m) != 0 {
|
||||
return &CueError{m}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO add migrator with SearchOption for stopping criteria
|
@ -1,129 +0,0 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/tools/txtar"
|
||||
)
|
||||
|
||||
var CasesDir = filepath.Join("testdata", "trimapplydefaults")
|
||||
|
||||
type Case struct {
|
||||
Name string
|
||||
CUE string
|
||||
Full string
|
||||
Trimmed string
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
cases, err := loadCases(CasesDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.Name+" apply defaults", func(t *testing.T) {
|
||||
ctx := cuecontext.New()
|
||||
scmInstance := ctx.CompileString(c.CUE, cue.Filename(c.Name+".cue"))
|
||||
if scmInstance.Err() != nil {
|
||||
t.Fatal(scmInstance.Err())
|
||||
}
|
||||
inputResource := Resource{Value: c.Trimmed}
|
||||
out, err := ApplyDefaults(inputResource, scmInstance)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := []byte(out.Value.(string))
|
||||
b, _ = JsonRemarshal(b)
|
||||
|
||||
if s := cmp.Diff(c.Full, string(b)); s != "" {
|
||||
t.Fatal(s)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Name+" trim defaults", func(t *testing.T) {
|
||||
ctx := cuecontext.New()
|
||||
scmInstance := ctx.CompileString(c.CUE, cue.Filename(c.Name+".cue"))
|
||||
if scmInstance.Err() != nil {
|
||||
t.Fatal(scmInstance.Err())
|
||||
}
|
||||
inputResource := Resource{Value: c.Full}
|
||||
out, err := TrimDefaults(inputResource, scmInstance)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := []byte(out.Value.(string))
|
||||
b, _ = JsonRemarshal(b)
|
||||
|
||||
if s := cmp.Diff(c.Trimmed, string(b)); s != "" {
|
||||
t.Fatal(s)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func JsonRemarshal(bytes []byte) ([]byte, error) {
|
||||
var ifce interface{}
|
||||
err := json.Unmarshal(bytes, &ifce)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
output, err := json.Marshal(ifce)
|
||||
outputstring := string(output)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
outputstring = strings.Replace(outputstring, "\\u003c", "<", -1)
|
||||
outputstring = strings.Replace(outputstring, "\\u003e", ">", -1)
|
||||
outputstring = strings.Replace(outputstring, "\\u0026", "&", -1)
|
||||
return []byte(outputstring), nil
|
||||
}
|
||||
|
||||
func loadCases(dir string) ([]Case, error) {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cases []Case
|
||||
for _, fi := range files {
|
||||
file := filepath.Join(dir, fi.Name())
|
||||
a, err := txtar.ParseFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(a.Files) != 3 {
|
||||
return nil, fmt.Errorf("Malformed test case '%s': Must contain exactly three files (CUE, Full and Trimed), but has %d", file, len(a.Files))
|
||||
}
|
||||
|
||||
fullBuffer := new(bytes.Buffer)
|
||||
fullJson := a.Files[1].Data
|
||||
if err := json.Compact(fullBuffer, fullJson); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trimBuffer := new(bytes.Buffer)
|
||||
trimedJson := a.Files[2].Data
|
||||
if err := json.Compact(trimBuffer, trimedJson); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cases = append(cases, Case{
|
||||
Name: strings.TrimSuffix(fi.Name(), filepath.Ext(fi.Name())),
|
||||
CUE: string(a.Files[0].Data),
|
||||
Full: fullBuffer.String(),
|
||||
Trimmed: trimBuffer.String(),
|
||||
})
|
||||
}
|
||||
return cases, nil
|
||||
}
|
1163
pkg/schema/testdata/devenvgoldenfiles/all-panels.json
vendored
1163
pkg/schema/testdata/devenvgoldenfiles/all-panels.json
vendored
File diff suppressed because it is too large
Load Diff
@ -1,631 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 0
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"barWidth": 1,
|
||||
"groupWidth": 0.82,
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"max"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "right",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "auto",
|
||||
"showValue": "auto",
|
||||
"stacking": "none",
|
||||
"text": {},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Time,Name,Stat1,Stat2\n2020-01-01T00:00:00Z,Stockholm, 10, 15\n2020-01-01T00:00:00Z,New York, 19, 5\n2020-01-01T00:00:00Z,London, 10, 1\n2020-01-01T00:00:00Z,Negative, 15, -5\n2020-01-01T00:00:00Z,Long value, 15,10",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Auto sizing & auto show values",
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"description": "Should be smaller given the longer value",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 0
|
||||
},
|
||||
"decimals": 2,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 15,
|
||||
"options": {
|
||||
"barWidth": 1,
|
||||
"groupWidth": 0.82,
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"max"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "right",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "auto",
|
||||
"showValue": "auto",
|
||||
"stacking": "none",
|
||||
"text": {},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, -5\nLong value, 15,10",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Auto sizing & auto show values",
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 0
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
"id": 16,
|
||||
"options": {
|
||||
"barWidth": 1,
|
||||
"groupWidth": 0.89,
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"max"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "right",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "auto",
|
||||
"showValue": "auto",
|
||||
"stacking": "none",
|
||||
"text": {},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "auto show values & No room for value",
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 0
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 10
|
||||
},
|
||||
"id": 17,
|
||||
"options": {
|
||||
"barWidth": 1,
|
||||
"groupWidth": 0.89,
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"max"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "right",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "auto",
|
||||
"showValue": "always",
|
||||
"stacking": "none",
|
||||
"text": {},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "auto show values & Always show value",
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 0
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 10
|
||||
},
|
||||
"id": 10,
|
||||
"options": {
|
||||
"barWidth": 1,
|
||||
"groupWidth": 1,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "auto",
|
||||
"showValue": "auto",
|
||||
"stacking": "none",
|
||||
"text": {
|
||||
"titleSize": 10,
|
||||
"valueSize": 25
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 9,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Fixed value sizing",
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 0
|
||||
},
|
||||
"decimals": 7,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 21
|
||||
},
|
||||
"id": 18,
|
||||
"options": {
|
||||
"barWidth": 1,
|
||||
"groupWidth": 0.82,
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"max"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "right",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "horizontal",
|
||||
"showValue": "auto",
|
||||
"stacking": "none",
|
||||
"text": {},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, -5\nLondon, 10, 1\nLong value, 15,10",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Auto sizing & auto show values",
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 0
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 21
|
||||
},
|
||||
"id": 19,
|
||||
"options": {
|
||||
"barWidth": 1,
|
||||
"groupWidth": 0.89,
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"max"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "right",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "horizontal",
|
||||
"showValue": "auto",
|
||||
"stacking": "none",
|
||||
"text": {},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8,Stat9,Stat10\nA, 10, 15,8,3,4,12,14,1,5,10\nB, 19, 5,8,3,4,12,14,6,7,2\nC, 15, 5,8,3,4,10,4,6,7,2\n",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "auto show values & little room",
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"panel-tests",
|
||||
"barchart"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-5m",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "BarChart - Panel Tests - Value sizing",
|
||||
"uid": "WFlOM-jM1",
|
||||
"version": 9
|
||||
}
|
@ -1,619 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "",
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"thresholdsStyle": {
|
||||
"mode": "line"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"hide": false,
|
||||
"max": 100,
|
||||
"min": 1,
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"startValue": 50
|
||||
},
|
||||
{
|
||||
"alias": "",
|
||||
"csvContent": "min,max,threshold1\n1000,1000,8000\n0,100,80\n\n",
|
||||
"refId": "config",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Min, max, threshold from separate query",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "configFromData",
|
||||
"options": {
|
||||
"configRefId": "config",
|
||||
"mappings": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "left",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "SensorA"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "color-text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name, Value, SensorA, MyUnit, MyColor\nGoogle, 10, 50, km/h, blue\nGoogle, 100, 100,km/h, orange\n",
|
||||
"hide": false,
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Custom mappings and apply to self",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "configFromData",
|
||||
"options": {
|
||||
"applyTo": {
|
||||
"id": "byName",
|
||||
"options": "SensorA"
|
||||
},
|
||||
"applyToConfig": true,
|
||||
"configRefId": "A",
|
||||
"mappings": [
|
||||
{
|
||||
"configProperty": "unit",
|
||||
"fieldName": "MyUnit",
|
||||
"handlerKey": "unit"
|
||||
},
|
||||
{
|
||||
"fieldName": "MyColor",
|
||||
"handlerKey": "color"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "center",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Value"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "color-background-solid"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 9
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "ID, DisplayText\n21412312312, Homer\n12421412413, Simpsons \n12321312313, Bart",
|
||||
"hide": false,
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Mapping data",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "configFromData",
|
||||
"options": {
|
||||
"applyToConfig": true,
|
||||
"configRefId": "A",
|
||||
"mappings": [
|
||||
{
|
||||
"fieldName": "Color",
|
||||
"handlerKey": "mappings.color"
|
||||
},
|
||||
{
|
||||
"fieldName": "Value",
|
||||
"handlerKey": "mappings.value"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "center",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Value"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "color-background-solid"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 9
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Value, Color\nOK, blue\nPretty bad, red\nYay it's green, green\nSomething is off, orange\nNo idea, #88AA00\nAm I purple?, purple",
|
||||
"hide": false,
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Value mappings from query result applied to itself",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "configFromData",
|
||||
"options": {
|
||||
"applyTo": {
|
||||
"id": "byName",
|
||||
"options": "Value"
|
||||
},
|
||||
"applyToConfig": true,
|
||||
"configRefId": "A",
|
||||
"mappings": [
|
||||
{
|
||||
"fieldName": "Color",
|
||||
"handlerKey": "mappings.color"
|
||||
},
|
||||
{
|
||||
"fieldName": "Value",
|
||||
"handlerKey": "mappings.value"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "center",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 14
|
||||
},
|
||||
"id": 8,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "ID, Value\n21412312312, 100\n12421412413, 20\n12321312313, 10",
|
||||
"hide": false,
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Display data",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "configFromData",
|
||||
"options": {
|
||||
"applyToConfig": true,
|
||||
"configRefId": "A",
|
||||
"mappings": [
|
||||
{
|
||||
"fieldName": "Color",
|
||||
"handlerKey": "mappings.color"
|
||||
},
|
||||
{
|
||||
"fieldName": "Value",
|
||||
"handlerKey": "mappings.value"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 19
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"barWidth": 0.97,
|
||||
"groupWidth": 0.7,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"orientation": "horizontal",
|
||||
"stacking": "none",
|
||||
"showValue": "auto",
|
||||
"text": {},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "ID, Value\nA21412312312, 100\nA12421412413, 20\nA12321312313, 10\n",
|
||||
"hide": false,
|
||||
"refId": "data",
|
||||
"scenarioId": "csv_content"
|
||||
},
|
||||
{
|
||||
"csvContent": "ID, DisplayText\nA21412312312, Homer\nA12421412413, Marge \nA12321312313, Bart",
|
||||
"hide": false,
|
||||
"refId": "mappings",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Value mapping ID -> DisplayText from separate query",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "configFromData",
|
||||
"options": {
|
||||
"applyTo": {
|
||||
"id": "byName",
|
||||
"options": "ID"
|
||||
},
|
||||
"applyToConfig": false,
|
||||
"configRefId": "mappings",
|
||||
"mappings": [
|
||||
{
|
||||
"fieldName": "ID",
|
||||
"handlerKey": "mappings.value"
|
||||
},
|
||||
{
|
||||
"fieldName": "DisplayText",
|
||||
"handlerKey": "mappings.text"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "barchart",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"transform"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Transforms - Config from query",
|
||||
"uid": "Juj4_7ink",
|
||||
"version": 1
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,965 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 37,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 3,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 15
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 11,
|
||||
"maxDataPoints": 45,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "hidden",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "1,10,20,30,40,50"
|
||||
}
|
||||
],
|
||||
"title": "15 orange, 30 red",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 37,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 3,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 20,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 15
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 0
|
||||
},
|
||||
"id": 12,
|
||||
"maxDataPoints": 45,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "1,10,20,30,40,50"
|
||||
}
|
||||
],
|
||||
"title": "15 orange, 30 red",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 37,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 2,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 20,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 15
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 50
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 0
|
||||
},
|
||||
"id": 13,
|
||||
"maxDataPoints": 45,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "1,10,20,30,40,50"
|
||||
}
|
||||
],
|
||||
"title": "15 orange, 50 red",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 5,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 3,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 20
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 7
|
||||
},
|
||||
"id": 9,
|
||||
"maxDataPoints": 45,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 4,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Color line by discrete tresholds",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "bars",
|
||||
"fillOpacity": 84,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 0,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 20
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 7
|
||||
},
|
||||
"id": 4,
|
||||
"interval": "80s",
|
||||
"maxDataPoints": 42,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"max": 40,
|
||||
"min": 0,
|
||||
"noise": 1,
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"spread": 20,
|
||||
"startValue": 1
|
||||
}
|
||||
],
|
||||
"title": "Color bars by discrete thresholds",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 3,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "blue"
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 20
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 14
|
||||
},
|
||||
"id": 6,
|
||||
"maxDataPoints": 50,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 4,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Color line by color scale",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "bars",
|
||||
"fillOpacity": 64,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "blue"
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 20
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 14
|
||||
},
|
||||
"id": 10,
|
||||
"maxDataPoints": 45,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 4,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Color bars by color scale",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 64,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "blue"
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 20
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 21
|
||||
},
|
||||
"id": 7,
|
||||
"maxDataPoints": 50,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 4,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Color line by color scale",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"custom": {
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "points",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "scheme",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"lineInterpolation": "smooth",
|
||||
"lineWidth": 3,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"max": 50,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "blue"
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "orange",
|
||||
"value": 20
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "degree"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 21
|
||||
},
|
||||
"id": 8,
|
||||
"maxDataPoints": 250,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"max": 45,
|
||||
"min": 20,
|
||||
"noise": 0,
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"spread": 12,
|
||||
"startValue": 40
|
||||
},
|
||||
{
|
||||
"hide": false,
|
||||
"max": 20,
|
||||
"min": 1,
|
||||
"noise": 0,
|
||||
"refId": "B",
|
||||
"scenarioId": "random_walk",
|
||||
"spread": 10
|
||||
}
|
||||
],
|
||||
"title": "Color line by color scale",
|
||||
"type": "timeseries",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
}
|
||||
],
|
||||
"refresh": false,
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"panel-tests"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Panel Tests - Graph NG - By value color schemes",
|
||||
"uid": "aBXrJ0R7z",
|
||||
"version": 11
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,500 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"id": 632,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"viz": false,
|
||||
"legend": false,
|
||||
"tooltip": false
|
||||
},
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"bucketOffset": 0,
|
||||
"combine": false,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "hidden",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"spread": 10
|
||||
}
|
||||
],
|
||||
"title": "Time series + Auto buckets",
|
||||
"type": "histogram",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"viz": false,
|
||||
"legend": false,
|
||||
"tooltip": false
|
||||
},
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"bucketOffset": 0,
|
||||
"bucketSize": 3,
|
||||
"combine": false,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "hidden",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 4,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Time series + bucket size 3",
|
||||
"type": "histogram",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"viz": false,
|
||||
"legend": false,
|
||||
"tooltip": false
|
||||
},
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"bucketOffset": 0,
|
||||
"bucketSize": 1,
|
||||
"combine": false,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "hidden",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "weight_height.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "People height distribution",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "filterFieldsByName",
|
||||
"options": {
|
||||
"include": {
|
||||
"names": [
|
||||
"Height"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "histogram",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 80,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"viz": false,
|
||||
"legend": false,
|
||||
"tooltip": false
|
||||
},
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 8
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"bucketOffset": 0,
|
||||
"bucketSize": 5,
|
||||
"combine": false,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "hidden",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "weight_height.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "People weight distribution",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "filterFieldsByName",
|
||||
"options": {
|
||||
"include": {
|
||||
"names": [
|
||||
"Weight"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "histogram",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 16
|
||||
},
|
||||
"id": 8,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "weight_height.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Standalone transform - Height",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "filterFieldsByName",
|
||||
"options": {
|
||||
"include": {
|
||||
"names": [
|
||||
"Height"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "histogram",
|
||||
"options": {
|
||||
"combine": true,
|
||||
"fields": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "auto",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 16
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "weight_height.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Standalone transform - Weight",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "filterFieldsByName",
|
||||
"options": {
|
||||
"include": {
|
||||
"names": [
|
||||
"Weight"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "histogram",
|
||||
"options": {
|
||||
"combine": true,
|
||||
"fields": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"panel-tests"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Panel Tests - Histogram",
|
||||
"uid": "UTv--wqMk",
|
||||
"version": 4
|
||||
}
|
313
pkg/schema/testdata/devenvgoldenfiles/home.json
vendored
313
pkg/schema/testdata/devenvgoldenfiles/home.json
vendored
@ -1,313 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 26,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 7,
|
||||
"links": [],
|
||||
"options": {
|
||||
"maxItems": 100,
|
||||
"query": "",
|
||||
"showHeadings": true,
|
||||
"showRecentlyViewed": true,
|
||||
"showSearch": false,
|
||||
"showStarred": true,
|
||||
"tags": []
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"tags": [],
|
||||
"title": "Starred",
|
||||
"type": "dashlist",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": [],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 13,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"links": [],
|
||||
"options": {
|
||||
"maxItems": 1000,
|
||||
"query": "",
|
||||
"showHeadings": false,
|
||||
"showRecentlyViewed": false,
|
||||
"showSearch": true,
|
||||
"showStarred": false,
|
||||
"tags": [
|
||||
"panel-tests"
|
||||
]
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"tags": [
|
||||
"panel-tests"
|
||||
],
|
||||
"title": "tag: panel-tests",
|
||||
"type": "dashlist",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": [],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 13,
|
||||
"w": 6,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 3,
|
||||
"links": [],
|
||||
"options": {
|
||||
"maxItems": 1000,
|
||||
"query": "",
|
||||
"showHeadings": false,
|
||||
"showRecentlyViewed": false,
|
||||
"showSearch": true,
|
||||
"showStarred": false,
|
||||
"tags": [
|
||||
"gdev",
|
||||
"demo"
|
||||
]
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"demo"
|
||||
],
|
||||
"title": "tag: dashboard-demo",
|
||||
"type": "dashlist",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": [],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 26,
|
||||
"w": 6,
|
||||
"x": 18,
|
||||
"y": 0
|
||||
},
|
||||
"id": 5,
|
||||
"links": [],
|
||||
"options": {
|
||||
"maxItems": 1000,
|
||||
"query": "",
|
||||
"showHeadings": false,
|
||||
"showRecentlyViewed": false,
|
||||
"showSearch": true,
|
||||
"showStarred": false,
|
||||
"tags": [
|
||||
"gdev",
|
||||
"datasource-test"
|
||||
]
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"datasource-test"
|
||||
],
|
||||
"title": "Data source tests",
|
||||
"type": "dashlist",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": [],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 13,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 13
|
||||
},
|
||||
"id": 4,
|
||||
"links": [],
|
||||
"options": {
|
||||
"maxItems": 1000,
|
||||
"query": "",
|
||||
"showHeadings": false,
|
||||
"showRecentlyViewed": false,
|
||||
"showSearch": true,
|
||||
"showStarred": false,
|
||||
"tags": [
|
||||
"templating",
|
||||
"gdev"
|
||||
]
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"tags": [
|
||||
"templating",
|
||||
"gdev"
|
||||
],
|
||||
"title": "tag: templating ",
|
||||
"type": "dashlist",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": [],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 13,
|
||||
"w": 6,
|
||||
"x": 12,
|
||||
"y": 13
|
||||
},
|
||||
"id": 8,
|
||||
"links": [],
|
||||
"options": {
|
||||
"maxItems": 1000,
|
||||
"query": "",
|
||||
"showHeadings": false,
|
||||
"showRecentlyViewed": false,
|
||||
"showSearch": true,
|
||||
"showStarred": false,
|
||||
"tags": [
|
||||
"gdev",
|
||||
"transform"
|
||||
]
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"demo"
|
||||
],
|
||||
"title": "tag: transforms",
|
||||
"type": "dashlist",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": [],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Grafana Dev Overview & Home",
|
||||
"uid": "j6T00KRZz",
|
||||
"version": 2
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,252 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"id": 3151,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "gdev-opentsdb-v2.3",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"links": []
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 2,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_hostname",
|
||||
"currentFilterGroupBy": false,
|
||||
"currentFilterKey": "",
|
||||
"currentFilterType": "literal_or",
|
||||
"currentFilterValue": "",
|
||||
"disableDownsampling": false,
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"explicitTags": false,
|
||||
"filters": [
|
||||
{
|
||||
"filter": "*",
|
||||
"groupBy": true,
|
||||
"tagk": "hostname",
|
||||
"type": "wildcard"
|
||||
}
|
||||
],
|
||||
"metric": "cpu",
|
||||
"refId": "A",
|
||||
"shouldComputeRate": false
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeRegions": [],
|
||||
"title": "CPU per host",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"mode": "time",
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "gdev-opentsdb-v2.3",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"links": []
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 4,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_hostname",
|
||||
"currentFilterGroupBy": false,
|
||||
"currentFilterKey": "",
|
||||
"currentFilterType": "literal_or",
|
||||
"currentFilterValue": "",
|
||||
"downsampleAggregator": "avg",
|
||||
"downsampleFillPolicy": "none",
|
||||
"filters": [
|
||||
{
|
||||
"filter": "*",
|
||||
"groupBy": true,
|
||||
"tagk": "hostname",
|
||||
"type": "wildcard"
|
||||
}
|
||||
],
|
||||
"metric": "logins.count",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeRegions": [],
|
||||
"title": "Login Count per host",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"mode": "time",
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Datasource tests - OpenTSDB v2.3",
|
||||
"uid": "rZRUGik7k",
|
||||
"version": 3
|
||||
}
|
@ -1,414 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 9,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 62,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"color": {
|
||||
"field": "Price",
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"fillOpacity": 0.4,
|
||||
"shape": "circle",
|
||||
"showLegend": true,
|
||||
"size": {
|
||||
"field": "Count",
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "auto"
|
||||
},
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 38.297683,
|
||||
"lon": -99.228359,
|
||||
"shared": true,
|
||||
"zoom": 3.98
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "flight_info_by_state.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Size, color mapped to different fields + share view",
|
||||
"type": "geomap",
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
},
|
||||
{
|
||||
"color": "#EAB839",
|
||||
"value": 90
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 9,
|
||||
"x": 9,
|
||||
"y": 0
|
||||
},
|
||||
"id": 66,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"color": {
|
||||
"field": "Price",
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"fillOpacity": 0.4,
|
||||
"shape": "circle",
|
||||
"showLegend": true,
|
||||
"size": {
|
||||
"field": "Count",
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "auto"
|
||||
},
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 38.297683,
|
||||
"lon": -99.228359,
|
||||
"shared": true,
|
||||
"zoom": 3.98
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "flight_info_by_state.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Thresholds legend",
|
||||
"type": "geomap",
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-BlYlRd"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 9,
|
||||
"x": 0,
|
||||
"y": 11
|
||||
},
|
||||
"id": 63,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"blur": 27,
|
||||
"radius": 25,
|
||||
"weight": {
|
||||
"field": "Count",
|
||||
"fixed": 1,
|
||||
"max": 1,
|
||||
"min": 0
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "auto"
|
||||
},
|
||||
"type": "heatmap"
|
||||
}
|
||||
],
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 38.251497,
|
||||
"lon": -100.932144,
|
||||
"shared": false,
|
||||
"zoom": 4.15
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "flight_info_by_state.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Heatmap data layer",
|
||||
"transformations": [],
|
||||
"type": "geomap",
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 9,
|
||||
"x": 9,
|
||||
"y": 11
|
||||
},
|
||||
"id": 65,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {
|
||||
"server": "world-imagery"
|
||||
},
|
||||
"type": "esri-xyz"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"color": {
|
||||
"fixed": "#ff001e"
|
||||
},
|
||||
"fillOpacity": 0.4,
|
||||
"shape": "star",
|
||||
"showLegend": true,
|
||||
"size": {
|
||||
"field": "Count",
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"gazetteer": "public/gazetteer/usa-states.json",
|
||||
"lookup": "State",
|
||||
"mode": "auto"
|
||||
},
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"view": {
|
||||
"id": "coords",
|
||||
"lat": 40.159084,
|
||||
"lon": -96.508021,
|
||||
"shared": true,
|
||||
"zoom": 3.83
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvFileName": "flight_info_by_state.csv",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_file"
|
||||
}
|
||||
],
|
||||
"title": "Base layer ArcGIS wold imagery + star shape + share view",
|
||||
"type": "geomap",
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"panel-tests"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Panel Tests - Geomap",
|
||||
"uid": "2xuwrgV7z",
|
||||
"version": 5
|
||||
}
|
@ -1,700 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "left",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 8,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 2,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Raw data",
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "left",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Value"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 82
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Unit"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 108
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 3,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Raw data",
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 5
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"text": {},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Value,Unit,Color\nTemperature,10,degree,green\nPressure,100,bar,blue\nSpeed,30,km/h,red",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Unit and color from data",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "rowsToFields",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"type": "stat",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 5
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showThresholdLabels": true,
|
||||
"showThresholdMarkers": true,
|
||||
"text": {}
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Value,Unit,min,max, threshold1\nTemperature,10,degree,0,50,30\nPressure,100,Pa,0,300,200\nSpeed,30,km/h,0,150,110",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Min, Max & Thresholds from data",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "rowsToFields",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"type": "gauge",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "left",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 12
|
||||
},
|
||||
"id": 10,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 9,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Raw data",
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"align": "left",
|
||||
"displayMode": "auto"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Value"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 82
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Unit"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 108
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 12
|
||||
},
|
||||
"id": 12,
|
||||
"options": {
|
||||
"frameIndex": 0,
|
||||
"showHeader": true,
|
||||
"showTypeIcons": false,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"panelId": 11,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Raw data (Custom mapping)",
|
||||
"type": "table",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 17
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"displayMode": "gradient",
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showUnfilled": true,
|
||||
"text": {}
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Value,Unit,Min,Max\nTemperature,20,degree,0,50\nPressure,150,Pa,0,300\nSpeed,100,km/h,0,110",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Min max from data",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "rowsToFields",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"type": "bargauge",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 17
|
||||
},
|
||||
"id": 11,
|
||||
"options": {
|
||||
"orientation": "auto",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showThresholdLabels": true,
|
||||
"showThresholdMarkers": true,
|
||||
"text": {}
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name,Value,Type,Quota, Warning\nTemperature,25,degree,50,30\nPressure,100,Pa,300,200\nSpeed,30,km/h,150,130",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Custom mapping",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "rowsToFields",
|
||||
"options": {
|
||||
"mappings": [
|
||||
{
|
||||
"configProperty": "unit",
|
||||
"fieldName": "Type",
|
||||
"handlerKey": "unit"
|
||||
},
|
||||
{
|
||||
"configProperty": "max",
|
||||
"fieldName": "Quota",
|
||||
"handlerKey": "max"
|
||||
},
|
||||
{
|
||||
"configProperty": "threshold1",
|
||||
"fieldName": "Warning",
|
||||
"handlerKey": "threshold1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "gauge",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 24
|
||||
},
|
||||
"id": 13,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "auto",
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"text": {},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "Name, City, Country, Value\nSensorA, Stockholm, Sweden, 20\nSensorB, London, England, 50\nSensorC, New York, USA,100",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Extra string fields to labels",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "rowsToFields",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"type": "stat",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"transform"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Transforms - Rows to fields",
|
||||
"uid": "PMtIInink",
|
||||
"version": 1
|
||||
}
|
@ -1,490 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 80,
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"options": {
|
||||
"CRITICAL": {
|
||||
"color": "red",
|
||||
"index": 3
|
||||
},
|
||||
"HIGH": {
|
||||
"color": "orange",
|
||||
"index": 2
|
||||
},
|
||||
"LOW": {
|
||||
"color": "blue",
|
||||
"index": 0
|
||||
},
|
||||
"NORMAL": {
|
||||
"color": "green",
|
||||
"index": 1
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"alignValue": "center",
|
||||
"colWidth": 0.9,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"mergeValues": true,
|
||||
"rowHeight": 0.98,
|
||||
"showValue": "always",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "SensorA",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "LOW,HIGH,NORMAL,NORMAL,NORMAL,LOW,LOW,NORMAL,HIGH,CRITICAL"
|
||||
},
|
||||
{
|
||||
"alias": "SensorB",
|
||||
"hide": false,
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "NORMAL,LOW,LOW,CRITICAL,CRITICAL,LOW,LOW,NORMAL,HIGH,CRITICAL"
|
||||
},
|
||||
{
|
||||
"alias": "SensorA",
|
||||
"hide": false,
|
||||
"refId": "C",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "NORMAL,NORMAL,NORMAL,NORMAL,CRITICAL,LOW,NORMAL,NORMAL,NORMAL,LOW"
|
||||
}
|
||||
],
|
||||
"title": "State changes strings",
|
||||
"type": "state-timeline",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 70,
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"options": {
|
||||
"match": "true",
|
||||
"result": {
|
||||
"color": "semi-dark-green",
|
||||
"index": 0,
|
||||
"text": "ON"
|
||||
}
|
||||
},
|
||||
"type": "special"
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"match": "false",
|
||||
"result": {
|
||||
"color": "red",
|
||||
"index": 1,
|
||||
"text": "OFF"
|
||||
}
|
||||
},
|
||||
"type": "special"
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 13,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 13,
|
||||
"options": {
|
||||
"alignValue": "center",
|
||||
"colWidth": 1,
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false,
|
||||
"calcs": []
|
||||
},
|
||||
"mergeValues": true,
|
||||
"mode": "changes",
|
||||
"rowHeight": 0.98,
|
||||
"showValue": "always",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "true,false,true,true,true,true,false,false"
|
||||
},
|
||||
{
|
||||
"hide": false,
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "false,true,false,true,true,false,false,false,true,true"
|
||||
},
|
||||
{
|
||||
"hide": false,
|
||||
"refId": "C",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "true,false,true,true"
|
||||
},
|
||||
{
|
||||
"hide": false,
|
||||
"refId": "D",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "false,true,false,true,true"
|
||||
}
|
||||
],
|
||||
"title": "State changes with boolean values",
|
||||
"type": "state-timeline",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"description": "Should show gaps",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 80,
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"options": {
|
||||
"match": "true",
|
||||
"result": {
|
||||
"color": "semi-dark-green",
|
||||
"index": 0,
|
||||
"text": "ON"
|
||||
}
|
||||
},
|
||||
"type": "special"
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"match": "false",
|
||||
"result": {
|
||||
"color": "red",
|
||||
"index": 1,
|
||||
"text": "OFF"
|
||||
}
|
||||
},
|
||||
"type": "special"
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 11,
|
||||
"x": 13,
|
||||
"y": 8
|
||||
},
|
||||
"id": 12,
|
||||
"options": {
|
||||
"alignValue": "center",
|
||||
"colWidth": 1,
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false,
|
||||
"calcs": []
|
||||
},
|
||||
"mergeValues": true,
|
||||
"mode": "changes",
|
||||
"rowHeight": 0.98,
|
||||
"showValue": "always",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "true,false,true,true,true,true,false,false"
|
||||
},
|
||||
{
|
||||
"hide": false,
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "false,true,false,true,true,false,false,false,true,true"
|
||||
},
|
||||
{
|
||||
"hide": false,
|
||||
"refId": "C",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "true,false,null,true,true"
|
||||
},
|
||||
{
|
||||
"hide": false,
|
||||
"refId": "D",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "false,null,null,false,true,true"
|
||||
}
|
||||
],
|
||||
"title": "State changes with nulls",
|
||||
"type": "state-timeline",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-GrYlRd"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 96,
|
||||
"lineWidth": 0
|
||||
},
|
||||
"decimals": 0,
|
||||
"mappings": [],
|
||||
"max": 30,
|
||||
"min": -10,
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 12,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 19
|
||||
},
|
||||
"id": 4,
|
||||
"maxDataPoints": 20,
|
||||
"options": {
|
||||
"alignValue": "center",
|
||||
"colWidth": 0.96,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"rowHeight": 0.98,
|
||||
"showValue": "always",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"max": 30,
|
||||
"min": -10,
|
||||
"noise": 2,
|
||||
"points": [],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 4,
|
||||
"spread": 15,
|
||||
"startValue": 5,
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"title": "Status map",
|
||||
"type": "status-history",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
}
|
||||
],
|
||||
"refresh": false,
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"demo"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "utc",
|
||||
"title": "Timeline Demo",
|
||||
"uid": "mIJjFy8Kz",
|
||||
"version": 3
|
||||
}
|
@ -1,427 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"id": 329,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 70,
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 8,
|
||||
"options": {
|
||||
"alignValue": "left",
|
||||
"colWidth": 0.9,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"mergeValues": true,
|
||||
"rowHeight": 0.9,
|
||||
"showValue": "always",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
1616551651000
|
||||
],
|
||||
[
|
||||
1,
|
||||
1616556554000
|
||||
],
|
||||
[
|
||||
2,
|
||||
1616559873000
|
||||
],
|
||||
[
|
||||
0,
|
||||
1616561077000
|
||||
],
|
||||
[
|
||||
3,
|
||||
1616563090000
|
||||
]
|
||||
],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "manual_entry",
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
},
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"hide": false,
|
||||
"lines": 10,
|
||||
"points": [
|
||||
[
|
||||
4,
|
||||
1616555060000
|
||||
],
|
||||
[
|
||||
5,
|
||||
1616560081000
|
||||
],
|
||||
[
|
||||
4,
|
||||
1616562217000
|
||||
],
|
||||
[
|
||||
5,
|
||||
1616565458000
|
||||
]
|
||||
],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "B",
|
||||
"scenarioId": "manual_entry",
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
},
|
||||
{
|
||||
"points": [
|
||||
[
|
||||
4,
|
||||
1616557148000
|
||||
],
|
||||
[
|
||||
1616558756000
|
||||
],
|
||||
[
|
||||
4,
|
||||
1616561658000
|
||||
],
|
||||
[
|
||||
1616562446000
|
||||
],
|
||||
[
|
||||
4,
|
||||
1616564104000
|
||||
],
|
||||
[
|
||||
1616564548000
|
||||
],
|
||||
[
|
||||
4,
|
||||
1616564871000
|
||||
]
|
||||
],
|
||||
"refId": "C",
|
||||
"scenarioId": "manual_entry"
|
||||
}
|
||||
],
|
||||
"title": "State timeline",
|
||||
"type": "state-timeline",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 70,
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"alignValue": "left",
|
||||
"colWidth": 0.9,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"mergeValues": true,
|
||||
"rowHeight": 0.9,
|
||||
"showValue": "always",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "a,a,b,b,b,b,c,a,a,d,d,d,d,d"
|
||||
},
|
||||
{
|
||||
"alias": "",
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "null,null,e,e,e,null,null,e,null,null,e,null,e,e,e,e"
|
||||
},
|
||||
{
|
||||
"refId": "C",
|
||||
"scenarioId": "csv_metric_values",
|
||||
"stringInput": "true,null,false,null,true,false"
|
||||
}
|
||||
],
|
||||
"title": "State timeline (strings & booleans)",
|
||||
"type": "state-timeline",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
},
|
||||
{
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"fillOpacity": 70,
|
||||
"lineWidth": 1
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "percentage",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "#EAB839",
|
||||
"value": 60
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 21
|
||||
},
|
||||
"id": 4,
|
||||
"maxDataPoints": 20,
|
||||
"options": {
|
||||
"colWidth": 0.9,
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"asTable": false,
|
||||
"isVisible": false
|
||||
},
|
||||
"rowHeight": 0.9,
|
||||
"showValue": "always",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
},
|
||||
"alignValue": "left"
|
||||
},
|
||||
"pluginVersion": "7.5.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"alias": "",
|
||||
"csvWave": {
|
||||
"timeStep": 60,
|
||||
"valuesCSV": "0,0,2,2,1,1"
|
||||
},
|
||||
"lines": 10,
|
||||
"points": [],
|
||||
"pulseWave": {
|
||||
"offCount": 3,
|
||||
"offValue": 1,
|
||||
"onCount": 3,
|
||||
"onValue": 2,
|
||||
"timeStep": 60
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 4,
|
||||
"spread": 14.9,
|
||||
"stream": {
|
||||
"bands": 1,
|
||||
"noise": 2.2,
|
||||
"speed": 250,
|
||||
"spread": 3.5,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"title": "Status grid",
|
||||
"type": "status-history",
|
||||
"panelSchema": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"transparent": false,
|
||||
"repeatDirection": "h",
|
||||
"transformations": []
|
||||
}
|
||||
],
|
||||
"refresh": false,
|
||||
"schemaVersion": 33,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"gdev",
|
||||
"panel-tests"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "2021-03-24T03:00:00.000Z",
|
||||
"to": "2021-03-24T07:00:00.000Z"
|
||||
},
|
||||
"timepicker": {
|
||||
"collapse": false,
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "utc",
|
||||
"title": "Timeline Modes",
|
||||
"uid": "mIJjFy8Gz",
|
||||
"version": 13
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
Verifies common usecases for trimdefault/applydefault functions:
|
||||
* Basic cases
|
||||
* Real dashboard
|
||||
|
||||
-- CUE --
|
||||
{
|
||||
timepicker?: {
|
||||
collapse: bool | *false
|
||||
enable: bool | *true
|
||||
hidden: bool | *false
|
||||
refresh_intervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
||||
}
|
||||
}
|
||||
|
||||
-- Full --
|
||||
{
|
||||
"timepicker": {
|
||||
"collapse": true,
|
||||
"enable":true,
|
||||
"hidden":false,
|
||||
"refresh_intervals":[
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
-- Trimmed --
|
||||
{
|
||||
"timepicker": {
|
||||
"collapse": true
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
Verifies common usecases for trimdefault/applydefault functions:
|
||||
* Basic cases
|
||||
* Real dashboard
|
||||
|
||||
-- CUE --
|
||||
{
|
||||
id?: number
|
||||
uid: string
|
||||
gnetId?: string
|
||||
style: *"light" | "dark"
|
||||
timezone?: *"browser" | "utc"
|
||||
editable: bool | *true
|
||||
schemaVersion: number | *25
|
||||
version?: number
|
||||
graphTooltip: >=0 & <=2 | *0
|
||||
}
|
||||
|
||||
-- Full --
|
||||
{
|
||||
"editable": true,
|
||||
"graphTooltip": 0,
|
||||
"id": 42,
|
||||
"schemaVersion": 27,
|
||||
"style": "light",
|
||||
"uid": "emal8gQMz",
|
||||
"version": 2
|
||||
}
|
||||
|
||||
-- Trimmed --
|
||||
{
|
||||
"id": 42,
|
||||
"schemaVersion": 27,
|
||||
"uid": "emal8gQMz",
|
||||
"version": 2
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
Verifies common usecases for trimdefault/applydefault functions:
|
||||
* open structure should be kept when fields not present
|
||||
|
||||
-- CUE --
|
||||
#ListA: {
|
||||
datasource: "gdev-postgres"
|
||||
hide: number | *0
|
||||
includeAll : bool | *true
|
||||
label: string | *"Datacenter"
|
||||
}
|
||||
#ListB: {
|
||||
datasource: "gdev-mysql"
|
||||
hide: number | *1
|
||||
includeAll : bool | *false
|
||||
label: string | *"Datacenter"
|
||||
}
|
||||
#ListC: {
|
||||
datasource: !=""
|
||||
hide: number | *2
|
||||
includeAll : bool | *false
|
||||
label: string | *"Awesome"
|
||||
}
|
||||
{
|
||||
templating?: list: [...#ListA | #ListB | #ListC]
|
||||
}
|
||||
|
||||
-- Full --
|
||||
{
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"datasource": "gdev-postgres",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Datacenter"
|
||||
},
|
||||
{
|
||||
"datasource": "gdev-mysql",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Datacenter"
|
||||
},
|
||||
{
|
||||
"datasource": "gdev-random",
|
||||
"hide": 2,
|
||||
"includeAll": false,
|
||||
"label": "Datacenter"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
-- Trimmed --
|
||||
{
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"datasource": "gdev-postgres",
|
||||
"includeAll": false
|
||||
},
|
||||
{
|
||||
"datasource": "gdev-mysql",
|
||||
"hide": 0
|
||||
},
|
||||
{
|
||||
"datasource": "gdev-random",
|
||||
"label": "Datacenter"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,46 +0,0 @@
|
||||
Verifies common usecases for trimdefault/applydefault functions:
|
||||
* Nested struct
|
||||
* Simple array
|
||||
|
||||
-- CUE --
|
||||
{
|
||||
annotations?: list: [...{
|
||||
builtIn: number | *0
|
||||
datasource: string
|
||||
enable?: bool | *true
|
||||
hide?: bool | *false
|
||||
iconColor?: string
|
||||
name?: string
|
||||
type: string | *"dashboard"
|
||||
rawQuery?: string
|
||||
showIn: number | *0
|
||||
}]
|
||||
}
|
||||
|
||||
-- Full --
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"name": "Annotations & Alerts",
|
||||
"showIn": 0,
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
-- Trimmed --
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"name": "Annotations & Alerts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
Verifies common usecases for trimdefault/applydefault functions:
|
||||
* open structure should be kept when fields not present
|
||||
|
||||
-- CUE --
|
||||
{
|
||||
templating?: list: [...{...}]
|
||||
}
|
||||
|
||||
-- Full --
|
||||
{
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"allValue": null,
|
||||
"current": {
|
||||
"text": "America",
|
||||
"value": "America"
|
||||
},
|
||||
"datasource": "gdev-postgres",
|
||||
"definition": "",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Datacenter",
|
||||
"multi": false,
|
||||
"name": "datacenter",
|
||||
"options": [],
|
||||
"query": "SELECT DISTINCT datacenter FROM grafana_metric",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 1,
|
||||
"tagValuesQuery": "",
|
||||
"tags": [],
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
-- Trimmed --
|
||||
{
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"allValue": null,
|
||||
"current": {
|
||||
"text": "America",
|
||||
"value": "America"
|
||||
},
|
||||
"datasource": "gdev-postgres",
|
||||
"definition": "",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Datacenter",
|
||||
"multi": false,
|
||||
"name": "datacenter",
|
||||
"options": [],
|
||||
"query": "SELECT DISTINCT datacenter FROM grafana_metric",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 1,
|
||||
"tagValuesQuery": "",
|
||||
"tags": [],
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -78,7 +78,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/queryhistory"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/services/schemaloader"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
@ -190,7 +189,6 @@ var wireBasicSet = wire.NewSet(
|
||||
contexthandler.ProvideService,
|
||||
jwt.ProvideService,
|
||||
wire.Bind(new(models.JWTService), new(*jwt.AuthService)),
|
||||
schemaloader.ProvideService,
|
||||
ngalert.ProvideService,
|
||||
librarypanels.ProvideService,
|
||||
wire.Bind(new(librarypanels.Service), new(*librarypanels.LibraryPanelService)),
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/apierrors"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
@ -19,17 +18,15 @@ import (
|
||||
type ImportDashboardAPI struct {
|
||||
dashboardImportService dashboardimport.Service
|
||||
quotaService QuotaService
|
||||
schemaLoaderService SchemaLoaderService
|
||||
pluginStore plugins.Store
|
||||
ac accesscontrol.AccessControl
|
||||
}
|
||||
|
||||
func New(dashboardImportService dashboardimport.Service, quotaService QuotaService,
|
||||
schemaLoaderService SchemaLoaderService, pluginStore plugins.Store, ac accesscontrol.AccessControl) *ImportDashboardAPI {
|
||||
pluginStore plugins.Store, ac accesscontrol.AccessControl) *ImportDashboardAPI {
|
||||
return &ImportDashboardAPI{
|
||||
dashboardImportService: dashboardImportService,
|
||||
quotaService: quotaService,
|
||||
schemaLoaderService: schemaLoaderService,
|
||||
pluginStore: pluginStore,
|
||||
ac: ac,
|
||||
}
|
||||
@ -65,14 +62,6 @@ func (api *ImportDashboardAPI) ImportDashboard(c *models.ReqContext) response.Re
|
||||
return response.Error(403, "Quota reached", nil)
|
||||
}
|
||||
|
||||
trimDefaults := c.QueryBoolWithDefault("trimdefaults", true)
|
||||
if trimDefaults && !api.schemaLoaderService.IsDisabled() {
|
||||
req.Dashboard, err = api.schemaLoaderService.DashboardApplyDefaults(req.Dashboard)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Error while applying default value to the dashboard json", err)
|
||||
}
|
||||
}
|
||||
|
||||
req.User = c.SignedInUser
|
||||
resp, err := api.dashboardImportService.ImportDashboard(c.Req.Context(), &req)
|
||||
if err != nil {
|
||||
@ -91,8 +80,3 @@ type quotaServiceFunc func(c *models.ReqContext, target string) (bool, error)
|
||||
func (fn quotaServiceFunc) QuotaReached(c *models.ReqContext, target string) (bool, error) {
|
||||
return fn(c, target)
|
||||
}
|
||||
|
||||
type SchemaLoaderService interface {
|
||||
IsDisabled() bool
|
||||
DashboardApplyDefaults(input *simplejson.Json) (*simplejson.Json, error)
|
||||
}
|
||||
|
@ -26,15 +26,7 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
schemaLoaderServiceCalled := false
|
||||
schemaLoaderService := &schemaLoaderServiceMock{
|
||||
dashboardApplyDefaultsFunc: func(input *simplejson.Json) (*simplejson.Json, error) {
|
||||
schemaLoaderServiceCalled = true
|
||||
return input, nil
|
||||
},
|
||||
}
|
||||
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), schemaLoaderService, nil, acmock.New().WithDisabled())
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, acmock.New().WithDisabled())
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
s := webtest.NewServer(t, routeRegister)
|
||||
@ -98,7 +90,6 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
require.False(t, schemaLoaderServiceCalled)
|
||||
require.True(t, importDashboardServiceCalled)
|
||||
})
|
||||
})
|
||||
@ -112,16 +103,7 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
schemaLoaderServiceCalled := false
|
||||
schemaLoaderService := &schemaLoaderServiceMock{
|
||||
enabled: true,
|
||||
dashboardApplyDefaultsFunc: func(input *simplejson.Json) (*simplejson.Json, error) {
|
||||
schemaLoaderServiceCalled = true
|
||||
return input, nil
|
||||
},
|
||||
}
|
||||
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), schemaLoaderService, nil, acmock.New().WithDisabled())
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaNotReached), nil, acmock.New().WithDisabled())
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
s := webtest.NewServer(t, routeRegister)
|
||||
@ -140,15 +122,13 @@ func TestImportDashboardAPI(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
require.True(t, schemaLoaderServiceCalled)
|
||||
require.True(t, importDashboardServiceCalled)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Quota reached", func(t *testing.T) {
|
||||
service := &serviceMock{}
|
||||
schemaLoaderService := &schemaLoaderServiceMock{}
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaReached), schemaLoaderService, nil, acmock.New().WithDisabled())
|
||||
importDashboardAPI := New(service, quotaServiceFunc(quotaReached), nil, acmock.New().WithDisabled())
|
||||
|
||||
routeRegister := routing.NewRouteRegister()
|
||||
importDashboardAPI.RegisterAPIEndpoints(routeRegister)
|
||||
@ -184,23 +164,6 @@ func (s *serviceMock) ImportDashboard(ctx context.Context, req *dashboardimport.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type schemaLoaderServiceMock struct {
|
||||
enabled bool
|
||||
dashboardApplyDefaultsFunc func(input *simplejson.Json) (*simplejson.Json, error)
|
||||
}
|
||||
|
||||
func (s *schemaLoaderServiceMock) IsDisabled() bool {
|
||||
return !s.enabled
|
||||
}
|
||||
|
||||
func (s *schemaLoaderServiceMock) DashboardApplyDefaults(input *simplejson.Json) (*simplejson.Json, error) {
|
||||
if s.dashboardApplyDefaultsFunc != nil {
|
||||
return s.dashboardApplyDefaultsFunc(input)
|
||||
}
|
||||
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func quotaReached(c *models.ReqContext, target string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
@ -14,11 +14,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/schemaloader"
|
||||
)
|
||||
|
||||
func ProvideService(routeRegister routing.RouteRegister,
|
||||
quotaService *quota.QuotaService, schemaLoaderService *schemaloader.SchemaLoaderService,
|
||||
quotaService *quota.QuotaService,
|
||||
pluginDashboardService plugindashboards.Service, pluginStore plugins.Store,
|
||||
libraryPanelService librarypanels.Service, dashboardService dashboards.DashboardService,
|
||||
ac accesscontrol.AccessControl,
|
||||
@ -29,7 +28,7 @@ func ProvideService(routeRegister routing.RouteRegister,
|
||||
libraryPanelService: libraryPanelService,
|
||||
}
|
||||
|
||||
dashboardImportAPI := api.New(s, quotaService, schemaLoaderService, pluginStore, ac)
|
||||
dashboardImportAPI := api.New(s, quotaService, pluginStore, ac)
|
||||
dashboardImportAPI.RegisterAPIEndpoints(routeRegister)
|
||||
|
||||
return s
|
||||
|
@ -1,144 +0,0 @@
|
||||
package schemaloader
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
"github.com/grafana/grafana/pkg/schema/load"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
const ServiceName = "SchemaLoader"
|
||||
|
||||
var baseLoadPath load.BaseLoadPaths = load.BaseLoadPaths{
|
||||
BaseCueFS: grafana.CoreSchema,
|
||||
DistPluginCueFS: grafana.PluginSchema,
|
||||
}
|
||||
|
||||
type RenderUser struct {
|
||||
OrgID int64
|
||||
UserID int64
|
||||
OrgRole string
|
||||
}
|
||||
|
||||
func ProvideService(features featuremgmt.FeatureToggles) (*SchemaLoaderService, error) {
|
||||
dashFam, err := load.BaseDashboardFamily(baseLoadPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load dashboard cue schema from path %q: %w", baseLoadPath, err)
|
||||
}
|
||||
s := &SchemaLoaderService{
|
||||
features: features,
|
||||
DashFamily: dashFam,
|
||||
log: log.New("schemaloader"),
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type SchemaLoaderService struct {
|
||||
log log.Logger
|
||||
DashFamily schema.VersionedCueSchema
|
||||
features featuremgmt.FeatureToggles
|
||||
}
|
||||
|
||||
func (rs *SchemaLoaderService) IsDisabled() bool {
|
||||
if rs.features == nil {
|
||||
return true
|
||||
}
|
||||
return !rs.features.IsEnabled(featuremgmt.FlagTrimDefaults)
|
||||
}
|
||||
|
||||
func (rs *SchemaLoaderService) DashboardApplyDefaults(input *simplejson.Json) (*simplejson.Json, error) {
|
||||
val, _ := input.Map()
|
||||
val = removeNils(val)
|
||||
data, _ := json.Marshal(val)
|
||||
dsSchema := schema.Find(rs.DashFamily, schema.Latest())
|
||||
result, err := schema.ApplyDefaults(schema.Resource{Value: data}, dsSchema.CUE())
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
output, err := simplejson.NewJson([]byte(result.Value.(string)))
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (rs *SchemaLoaderService) DashboardTrimDefaults(input simplejson.Json) (simplejson.Json, error) {
|
||||
val, _ := input.Map()
|
||||
val = removeNils(val)
|
||||
data, _ := json.Marshal(val)
|
||||
|
||||
dsSchema, err := schema.SearchAndValidate(rs.DashFamily, string(data))
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
|
||||
result, err := schema.TrimDefaults(schema.Resource{Value: data}, dsSchema.CUE())
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
output, err := simplejson.NewJson([]byte(result.Value.(string)))
|
||||
if err != nil {
|
||||
return input, err
|
||||
}
|
||||
return *output, nil
|
||||
}
|
||||
|
||||
func removeNils(initialMap map[string]interface{}) map[string]interface{} {
|
||||
withoutNils := map[string]interface{}{}
|
||||
for key, value := range initialMap {
|
||||
_, ok := value.(map[string]interface{})
|
||||
if ok {
|
||||
value = removeNils(value.(map[string]interface{}))
|
||||
withoutNils[key] = value
|
||||
continue
|
||||
}
|
||||
_, ok = value.([]interface{})
|
||||
if ok {
|
||||
value = removeNilArray(value.([]interface{}))
|
||||
withoutNils[key] = value
|
||||
continue
|
||||
}
|
||||
if value != nil {
|
||||
if val, ok := value.(string); ok {
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
withoutNils[key] = value
|
||||
}
|
||||
}
|
||||
return withoutNils
|
||||
}
|
||||
|
||||
func removeNilArray(initialArray []interface{}) []interface{} {
|
||||
withoutNils := []interface{}{}
|
||||
for _, value := range initialArray {
|
||||
_, ok := value.(map[string]interface{})
|
||||
if ok {
|
||||
value = removeNils(value.(map[string]interface{}))
|
||||
withoutNils = append(withoutNils, value)
|
||||
continue
|
||||
}
|
||||
_, ok = value.([]interface{})
|
||||
if ok {
|
||||
value = removeNilArray(value.([]interface{}))
|
||||
withoutNils = append(withoutNils, value)
|
||||
continue
|
||||
}
|
||||
if value != nil {
|
||||
if val, ok := value.(string); ok {
|
||||
if val == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
withoutNils = append(withoutNils, value)
|
||||
}
|
||||
}
|
||||
return withoutNils
|
||||
}
|
@ -46,6 +46,10 @@ func main() {
|
||||
}
|
||||
|
||||
wd, err := codegen.CuetsifyPlugins(cuecontext.New(), groot)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error while generating code:\n%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if _, set := os.LookupEnv("CODEGEN_VERIFY"); set {
|
||||
err = wd.Verify()
|
||||
|
@ -14,24 +14,28 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
onlyFromThisDashboard: bool | *false
|
||||
onlyInTimeRange: bool | *false
|
||||
tags: [...string]
|
||||
limit: uint32 | *10
|
||||
showUser: bool | *true
|
||||
showTime: bool | *true
|
||||
showTags: bool | *true
|
||||
navigateToPanel: bool | *true
|
||||
navigateBefore: string | *"10m"
|
||||
navigateAfter: string | *"10m"
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
import "github.com/grafana/thema"
|
||||
|
||||
Panel: thema.#Lineage & {
|
||||
name: "annolist"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
onlyFromThisDashboard: bool | *false
|
||||
onlyInTimeRange: bool | *false
|
||||
tags: [...string]
|
||||
limit: uint32 | *10
|
||||
showUser: bool | *true
|
||||
showTime: bool | *true
|
||||
showTags: bool | *true
|
||||
navigateToPanel: bool | *true
|
||||
navigateBefore: string | *"10m"
|
||||
navigateAfter: string | *"10m"
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -15,33 +15,36 @@
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
ui.OptionsWithTextFormatting
|
||||
orientation: ui.VizOrientation
|
||||
// TODO this default is a guess based on common devenv values
|
||||
stacking: ui.StackingMode | *"none"
|
||||
showValue: ui.VisibilityMode
|
||||
barWidth: number
|
||||
groupWidth: number
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
ui.AxisConfig
|
||||
ui.HideableFieldConfig
|
||||
lineWidth?: number
|
||||
fillOpacity?: number
|
||||
gradientMode?: ui.GraphGradientMode
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "barchart"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
ui.OptionsWithTextFormatting
|
||||
orientation: ui.VizOrientation
|
||||
// TODO this default is a guess based on common devenv values
|
||||
stacking: ui.StackingMode | *"none"
|
||||
showValue: ui.VisibilityMode
|
||||
barWidth: number
|
||||
groupWidth: number
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
ui.AxisConfig
|
||||
ui.HideableFieldConfig
|
||||
lineWidth?: number
|
||||
fillOpacity?: number
|
||||
gradientMode?: ui.GraphGradientMode
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,21 +14,26 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
import ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
import (
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.SingleStatBaseOptions
|
||||
displayMode: ui.BarGaugeDisplayMode | *"gradient"
|
||||
showUnfilled: bool | *true
|
||||
minVizWidth: uint32 | *0
|
||||
minVizHeight: uint32 | *10
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "bargauge"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.SingleStatBaseOptions
|
||||
displayMode: ui.BarGaugeDisplayMode | *"gradient"
|
||||
showUnfilled: bool | *true
|
||||
minVizWidth: uint32 | *0
|
||||
minVizHeight: uint32 | *10
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,20 +14,22 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "candlestick"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,16 +14,20 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
import "github.com/grafana/thema"
|
||||
|
||||
Panel: thema.#Lineage & {
|
||||
name: "canvas"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,24 +14,28 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelLayout: "list" | "previews" @cuetsy(kind="enum")
|
||||
PanelOptions: {
|
||||
layout?: PanelLayout | *"list"
|
||||
showStarred: bool | *true
|
||||
showRecentlyViewed: bool | *false
|
||||
showSearch: bool | *false
|
||||
showHeadings: bool | *true
|
||||
maxItems: int | *10
|
||||
query: string | *""
|
||||
folderId?: int
|
||||
tags: [...string] | *[]
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
import "github.com/grafana/thema"
|
||||
|
||||
Panel: thema.#Lineage & {
|
||||
name: "dashlist"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelLayout: "list" | "previews" @cuetsy(kind="enum")
|
||||
PanelOptions: {
|
||||
layout?: PanelLayout | *"list"
|
||||
showStarred: bool | *true
|
||||
showRecentlyViewed: bool | *false
|
||||
showSearch: bool | *false
|
||||
showHeadings: bool | *true
|
||||
maxItems: int | *10
|
||||
query: string | *""
|
||||
folderId?: int
|
||||
tags: [...string] | *[]
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,19 +14,24 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
import ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
import (
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.SingleStatBaseOptions
|
||||
showThresholdLabels: bool | *false
|
||||
showThresholdMarkers: bool | *true
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "gauge"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.SingleStatBaseOptions
|
||||
showThresholdLabels: bool | *false
|
||||
showThresholdMarkers: bool | *true
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,20 +14,24 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
import "github.com/grafana/thema"
|
||||
|
||||
Panel: thema.#Lineage & {
|
||||
name: "heatmap-new"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
// anything for now
|
||||
...
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,23 +14,28 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
import ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
import (
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
bucketSize?: int
|
||||
bucketOffset: int | *0
|
||||
combine?: bool
|
||||
} @cuetsy(kind="interface")
|
||||
Panel: thema.#Lineage & {
|
||||
name: "histogram"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
bucketSize?: int
|
||||
bucketOffset: int | *0
|
||||
combine?: bool
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
PanelFieldConfig: ui.GraphFieldConfig & {} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
PanelFieldConfig: ui.GraphFieldConfig & {} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,17 +14,21 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
// empty/missing will default to grafana blog
|
||||
feedUrl?: string
|
||||
showImage?: bool | *true
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
import "github.com/grafana/thema"
|
||||
|
||||
Panel: thema.#Lineage & {
|
||||
name: "news"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
// empty/missing will default to grafana blog
|
||||
feedUrl?: string
|
||||
showImage?: bool | *true
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,21 +14,26 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
import ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
import (
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.SingleStatBaseOptions
|
||||
graphMode: ui.BigValueGraphMode | *"area"
|
||||
colorMode: ui.BigValueColorMode | *"value"
|
||||
justifyMode: ui.BigValueJustifyMode | *"auto"
|
||||
textMode: ui.BigValueTextMode | *"auto"
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "stat"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.SingleStatBaseOptions
|
||||
graphMode: ui.BigValueGraphMode | *"area"
|
||||
colorMode: ui.BigValueColorMode | *"value"
|
||||
justifyMode: ui.BigValueJustifyMode | *"auto"
|
||||
textMode: ui.BigValueTextMode | *"auto"
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -15,33 +15,36 @@
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
TimelineMode: "changes" | "samples" @cuetsy(kind="enum")
|
||||
TimelineValueAlignment: "center" | "left" | "right" @cuetsy(kind="type")
|
||||
PanelOptions: {
|
||||
// FIXME ts comments indicate this shouldn't be in the saved model, but currently is emitted
|
||||
mode?: TimelineMode
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
showValue: ui.VisibilityMode | *"auto"
|
||||
rowHeight: number | *0.9
|
||||
colWidth?: number
|
||||
mergeValues?: bool | *true
|
||||
alignValue?: TimelineValueAlignment | *"left"
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
ui.HideableFieldConfig
|
||||
lineWidth?: number | *0
|
||||
fillOpacity?: number | *70
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "state-timeline"
|
||||
lineages: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
TimelineMode: "changes" | "samples" @cuetsy(kind="enum")
|
||||
TimelineValueAlignment: "center" | "left" | "right" @cuetsy(kind="type")
|
||||
PanelOptions: {
|
||||
// FIXME ts comments indicate this shouldn't be in the saved model, but currently is emitted
|
||||
mode?: TimelineMode
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
showValue: ui.VisibilityMode | *"auto"
|
||||
rowHeight: number | *0.9
|
||||
colWidth?: number
|
||||
mergeValues?: bool | *true
|
||||
alignValue?: TimelineValueAlignment | *"left"
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
ui.HideableFieldConfig
|
||||
lineWidth?: number | *0
|
||||
fillOpacity?: number | *70
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -15,28 +15,31 @@
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
showValue: ui.VisibilityMode
|
||||
rowHeight: number
|
||||
colWidth?: number
|
||||
alignValue: "center" | *"left" | "right"
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
ui.HideableFieldConfig
|
||||
lineWidth?: number | *1
|
||||
fillOpacity?: number | *70
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "status-history"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
ui.OptionsWithLegend
|
||||
ui.OptionsWithTooltip
|
||||
showValue: ui.VisibilityMode
|
||||
rowHeight: number
|
||||
colWidth?: number
|
||||
alignValue: "center" | *"left" | "right"
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: {
|
||||
ui.HideableFieldConfig
|
||||
lineWidth?: number | *1
|
||||
fillOpacity?: number | *70
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -15,22 +15,25 @@
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
frameIndex: number | *0
|
||||
showHeader: bool | *true
|
||||
showTypeIcons: bool | *false
|
||||
sortBy?: [...ui.TableSortByFieldState]
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: ui.TableFieldOptions & {} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "table"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
frameIndex: number | *0
|
||||
showHeader: bool | *true
|
||||
showTypeIcons: bool | *false
|
||||
sortBy?: [...ui.TableSortByFieldState]
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: ui.TableFieldOptions & {} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -14,21 +14,25 @@
|
||||
|
||||
package grafanaschema
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
TextMode: "html" | "markdown" @cuetsy(kind="enum",memberNames="HTML|Markdown")
|
||||
PanelOptions: {
|
||||
mode: TextMode | *"markdown"
|
||||
content: string | *"""
|
||||
import "github.com/grafana/thema"
|
||||
|
||||
Panel: thema.#Lineage & {
|
||||
name: "text"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
TextMode: "html" | "markdown" @cuetsy(kind="enum",memberNames="HTML|Markdown")
|
||||
PanelOptions: {
|
||||
mode: TextMode | *"markdown"
|
||||
content: string | *"""
|
||||
# Title
|
||||
|
||||
For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)
|
||||
"""
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -15,20 +15,23 @@
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
"github.com/grafana/thema"
|
||||
ui "github.com/grafana/grafana/packages/grafana-schema/src/schema"
|
||||
)
|
||||
|
||||
Panel: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
legend: ui.VizLegendOptions
|
||||
tooltip: ui.VizTooltipOptions
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: ui.GraphFieldConfig & {} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
Panel: thema.#Lineage & {
|
||||
name: "timeseries"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
PanelOptions: {
|
||||
legend: ui.VizLegendOptions
|
||||
tooltip: ui.VizTooltipOptions
|
||||
} @cuetsy(kind="interface")
|
||||
PanelFieldConfig: ui.GraphFieldConfig & {} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -39,8 +39,6 @@ load(
|
||||
'upload_packages_step',
|
||||
'store_packages_step',
|
||||
'upload_cdn_step',
|
||||
'validate_scuemata_step',
|
||||
'ensure_cuetsified_step',
|
||||
'verify_gen_cue_step',
|
||||
'test_a11y_frontend_step',
|
||||
'trigger_oss'
|
||||
@ -135,8 +133,6 @@ def get_steps(edition):
|
||||
build_frontend_step(edition=edition, ver_mode=ver_mode),
|
||||
build_frontend_package_step(edition=edition, ver_mode=ver_mode),
|
||||
build_plugins_step(edition=edition, sign=True),
|
||||
validate_scuemata_step(),
|
||||
ensure_cuetsified_step(),
|
||||
verify_gen_cue_step(),
|
||||
]
|
||||
integration_test_steps = [
|
||||
|
@ -29,8 +29,6 @@ load(
|
||||
'redis_integration_tests_step',
|
||||
'memcached_integration_tests_step',
|
||||
'benchmark_ldap_step',
|
||||
'validate_scuemata_step',
|
||||
'ensure_cuetsified_step',
|
||||
'verify_gen_cue_step',
|
||||
'test_a11y_frontend_step',
|
||||
'enterprise_downstream_step',
|
||||
@ -125,8 +123,6 @@ def pr_pipelines(edition):
|
||||
build_frontend_step(edition=edition, ver_mode=ver_mode),
|
||||
build_frontend_package_step(edition=edition, ver_mode=ver_mode),
|
||||
build_plugins_step(edition=edition),
|
||||
validate_scuemata_step(),
|
||||
ensure_cuetsified_step(),
|
||||
verify_gen_cue_step(),
|
||||
]
|
||||
integration_test_steps = [
|
||||
|
@ -39,8 +39,6 @@ load(
|
||||
'upload_packages_step',
|
||||
'store_packages_step',
|
||||
'upload_cdn_step',
|
||||
'validate_scuemata_step',
|
||||
'ensure_cuetsified_step',
|
||||
'verify_gen_cue_step',
|
||||
'publish_images_step',
|
||||
'trigger_oss'
|
||||
@ -182,8 +180,6 @@ def get_steps(edition, ver_mode):
|
||||
build_frontend_step(edition=edition, ver_mode=ver_mode),
|
||||
build_frontend_package_step(edition=edition, ver_mode=ver_mode),
|
||||
build_plugins_step(edition=edition, sign=True),
|
||||
validate_scuemata_step(),
|
||||
ensure_cuetsified_step(),
|
||||
verify_gen_cue_step(),
|
||||
]
|
||||
|
||||
@ -192,7 +188,6 @@ def get_steps(edition, ver_mode):
|
||||
mysql_integration_tests_step(edition=edition, ver_mode=ver_mode),
|
||||
]
|
||||
|
||||
|
||||
if include_enterprise2:
|
||||
test_steps.extend([
|
||||
lint_backend_step(edition=edition2),
|
||||
|
@ -1149,36 +1149,6 @@ def get_windows_steps(edition, ver_mode):
|
||||
|
||||
return steps
|
||||
|
||||
|
||||
def validate_scuemata_step():
|
||||
return {
|
||||
'name': 'validate-scuemata',
|
||||
'image': build_image,
|
||||
'depends_on': [
|
||||
'build-backend',
|
||||
],
|
||||
'commands': [
|
||||
'./bin/linux-amd64/grafana-cli cue validate-schema --grafana-root .',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def ensure_cuetsified_step():
|
||||
return {
|
||||
'name': 'ensure-cuetsified',
|
||||
'image': build_image,
|
||||
'depends_on': [
|
||||
'validate-scuemata',
|
||||
],
|
||||
'commands': [
|
||||
'# It is required that the generated Typescript be in sync with the input CUE files.',
|
||||
'# To enforce this, the following command will attempt to generate Typescript from all',
|
||||
'# appropriate .cue files, then compare with the corresponding (*.gen.ts) file the generated',
|
||||
'# code would have been written to. It exits 1 if any diffs are found.',
|
||||
'./bin/linux-amd64/grafana-cli cue gen-ts --grafana-root . --diff',
|
||||
],
|
||||
}
|
||||
|
||||
def verify_gen_cue_step():
|
||||
return {
|
||||
'name': 'verify-gen-cue',
|
||||
|
Loading…
Reference in New Issue
Block a user