mirror of
https://github.com/grafana/grafana.git
synced 2024-11-21 00:24:46 -06:00
Introduce "scuemata" system for CUE-based specification of Grafana objects (#32527)
This commit is contained in:
parent
7351645d63
commit
bba4d9bd7f
64
.drone.yml
64
.drone.yml
@ -51,13 +51,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -114,7 +107,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
|
||||
- name: package
|
||||
image: grafana/build-container:1.4.1
|
||||
@ -305,13 +297,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -382,7 +367,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
|
||||
- name: package
|
||||
image: grafana/build-container:1.4.1
|
||||
@ -761,13 +745,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -830,7 +807,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
|
||||
- name: package
|
||||
image: grafana/build-container:1.4.1
|
||||
@ -1137,13 +1113,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -1237,7 +1206,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
- build-backend-enterprise2
|
||||
- test-backend-enterprise2
|
||||
|
||||
@ -1712,13 +1680,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -1781,7 +1742,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
|
||||
- name: package
|
||||
image: grafana/build-container:1.4.1
|
||||
@ -2077,13 +2037,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -2177,7 +2130,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
- build-backend-enterprise2
|
||||
- test-backend-enterprise2
|
||||
|
||||
@ -2645,13 +2597,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -2711,7 +2656,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
|
||||
- name: package
|
||||
image: grafana/build-container:1.4.1
|
||||
@ -2981,13 +2925,6 @@ steps:
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: check-dashboard-schemas
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
- cue export --out openapi -o - ./dashboard-schemas/...
|
||||
depends_on:
|
||||
- initialize
|
||||
|
||||
- name: test-backend
|
||||
image: grafana/build-container:1.4.1
|
||||
commands:
|
||||
@ -3075,7 +3012,6 @@ steps:
|
||||
- test-frontend
|
||||
- codespell
|
||||
- shellcheck
|
||||
- check-dashboard-schemas
|
||||
- build-backend-enterprise2
|
||||
- test-backend-enterprise2
|
||||
|
||||
|
1
cue.mod/module.cue
Normal file
1
cue.mod/module.cue
Normal file
@ -0,0 +1 @@
|
||||
module: "github.com/grafana/grafana"
|
213
cue/data/gen.cue
Normal file
213
cue/data/gen.cue
Normal file
@ -0,0 +1,213 @@
|
||||
package grafanaschema
|
||||
|
||||
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
|
||||
panels?: [...#Panel]
|
||||
|
||||
// Dashboard panels. Panels are canonically defined inline
|
||||
// because they share a version timeline with the dashboard
|
||||
// schema; they do not vary independently. We create a separate,
|
||||
// synthetic Family to represent them in Go, for ease of generating
|
||||
// e.g. JSON Schema.
|
||||
#Panel: {
|
||||
// The panel plugin type id.
|
||||
type: !=""
|
||||
|
||||
// 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 }
|
||||
|
||||
// Panel title.
|
||||
title?: string
|
||||
// Description.
|
||||
description?: string
|
||||
// Whether to display the panel without a background.
|
||||
transparent: bool | *false
|
||||
// Name of default datasource.
|
||||
datasource?: 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.
|
||||
// links?: [..._panelLink]
|
||||
// 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"
|
||||
// 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.
|
||||
targets?: [...{}]
|
||||
|
||||
// The values depend on panel type
|
||||
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
|
||||
// mappings?: ValueMapping[];
|
||||
|
||||
// // 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?: DataLink[];
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string
|
||||
|
||||
// Can always exist. Valid fields within this are
|
||||
// defined by the panel plugin - that's the
|
||||
// PanelFieldConfig that comes from the plugin.
|
||||
custom?: {...}
|
||||
}
|
||||
overrides: [...{
|
||||
matcher: {
|
||||
id: string | *""
|
||||
options?: _
|
||||
}
|
||||
properties: [...{
|
||||
id: string | *""
|
||||
value?: _
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
#Latest: {
|
||||
#Dashboard: Family.latest
|
||||
#Panel: Family.latest._Panel
|
||||
}
|
21
cue/scuemata/panel-plugin.cue
Normal file
21
cue/scuemata/panel-plugin.cue
Normal file
@ -0,0 +1,21 @@
|
||||
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: {
|
||||
PanelOptions: {...}
|
||||
PanelFieldConfig: {...}
|
||||
}
|
||||
|
||||
// A lineage of panel schema
|
||||
#PanelLineage: [#PanelSchema, ...#PanelSchema]
|
||||
|
||||
// Panel plugin-specific Family
|
||||
#PanelFamily: {
|
||||
lineages: [#PanelLineage, ...#PanelLineage]
|
||||
migrations: [...#Migration]
|
||||
}
|
60
cue/scuemata/scuemata.cue
Normal file
60
cue/scuemata/scuemata.cue
Normal file
@ -0,0 +1,60 @@
|
||||
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: {
|
||||
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
|
||||
}
|
93
cue/ui/gen.cue
Normal file
93
cue/ui/gen.cue
Normal file
@ -0,0 +1,93 @@
|
||||
package grafanaschema
|
||||
|
||||
TableCellDisplayMode: {
|
||||
Auto: "auto",
|
||||
ColorText: "color-text",
|
||||
ColorBackground: "color-background",
|
||||
GradientGauge: "gradient-gauge",
|
||||
LcdGauge: "lcd-gauge",
|
||||
JSONView: "json-view",
|
||||
BasicGauge: "basic",
|
||||
Image: "image",
|
||||
} @cuetsy(targetType="enum")
|
||||
|
||||
TableFieldOptions: {
|
||||
width?: number
|
||||
align: FieldTextAlignment | *"auto"
|
||||
displayMode: TableCellDisplayMode | *"auto"
|
||||
hidden?: bool // ?? default is missing or false ??
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
TableSortByFieldState: {
|
||||
displayName: string
|
||||
desc?: bool
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
TooltipMode: "single" | "multi" | "none" @cuetsy(targetType="type")
|
||||
FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(targetType="type")
|
||||
AxisPlacement: "auto" | "top" | "right" | "bottom" | "left" | "hidden" @cuetsy(targetType="enum")
|
||||
PointVisibility: "auto" | "never" | "always" @cuetsy(targetType="enum")
|
||||
DrawStyle: "line" | "bars" | "points" @cuetsy(targetType="enum")
|
||||
LineInterpolation: "linear" | "smooth" | "stepBefore" | "stepAfter" @cuetsy(targetType="enum")
|
||||
ScaleDistribution: "linear" | "log" @cuetsy(targetType="enum")
|
||||
GraphGradientMode: "none" | "opacity" | "hue" | "scheme" @cuetsy(targetType="enum")
|
||||
LineStyle: {
|
||||
fill?: "solid" | "dash" | "dot" | "square"
|
||||
dash?: [number]
|
||||
} @cuetsy(targetType="interface")
|
||||
LineConfig: {
|
||||
lineColor?: string
|
||||
lineWidth?: number
|
||||
lineInterpolation?: LineInterpolation
|
||||
lineStyle?: LineStyle
|
||||
spanNulls?: bool
|
||||
} @cuetsy(targetType="interface")
|
||||
FillConfig: {
|
||||
fillColor?: string
|
||||
fillOpacity?: number
|
||||
fillBelowTo?: string
|
||||
} @cuetsy(targetType="interface")
|
||||
PointsConfig: {
|
||||
showPoints?: PointVisibility
|
||||
pointSize?: number
|
||||
pointColor?: string
|
||||
pointSymbol?: string
|
||||
} @cuetsy(targetType="interface")
|
||||
ScaleDistributionConfig: {
|
||||
type: ScaleDistribution
|
||||
log?: number
|
||||
} @cuetsy(targetType="interface")
|
||||
AxisConfig: {
|
||||
axisPlacement?: AxisPlacement
|
||||
axisLabel?: string
|
||||
axisWidth?: number
|
||||
axisSoftMin?: number
|
||||
axisSoftMax?: number
|
||||
scaleDistribution?: ScaleDistributionConfig
|
||||
} @cuetsy(targetType="interface")
|
||||
HideSeriesConfig: {
|
||||
tooltip: bool
|
||||
legend: bool
|
||||
graph: bool
|
||||
} @cuetsy(targetType="interface")
|
||||
LegendPlacement: "bottom" | "right" @cuetsy(targetType="type")
|
||||
LegendDisplayMode: "list" | "table" | "hidden" @cuetsy(targetType="enum")
|
||||
GraphTooltipOptions: {
|
||||
mode: TooltipMode
|
||||
} @cuetsy(targetType="interface")
|
||||
TableFieldOptions: {
|
||||
width?: number
|
||||
align: FieldTextAlignment | *"auto"
|
||||
displayMode: TableCellDisplayMode | *"auto"
|
||||
hidden?: bool
|
||||
} @cuetsy(targetType="interface")
|
||||
GraphFieldConfig: LineConfig & FillConfig & PointsConfig & AxisConfig & {
|
||||
drawStyle?: DrawStyle
|
||||
gradientMode?: GraphGradientMode
|
||||
hideFrom?: HideSeriesConfig
|
||||
} @cuetsy(targetType="interface")
|
||||
VizLegendOptions: {
|
||||
displayMode: LegendDisplayMode
|
||||
placement: LegendPlacement
|
||||
calcs: [string]
|
||||
} @cuetsy(targetType="interface")
|
@ -1,68 +0,0 @@
|
||||
package main
|
||||
|
||||
#Dashboard: {
|
||||
// Unique numeric identifier for the dashboard. (generated by the db)
|
||||
id: int
|
||||
// Unique dashboard identifier that can be generated by anyone. string (8-40)
|
||||
uid: string
|
||||
// Title of dashboard.
|
||||
title?: string
|
||||
// Description of dashboard.
|
||||
description?: 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: int >= 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: int | *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
|
||||
// Query for annotation data.
|
||||
rawQuery: string
|
||||
showIn: int | *0
|
||||
}] | *[]
|
||||
// Auto-refresh interval.
|
||||
refresh: string
|
||||
// Version of the JSON schema, incremented each time a Grafana update brings
|
||||
// changes to said schema.
|
||||
schemaVersion: int | *25
|
||||
// Version of the dashboard, incremented each time the dashboard is updated.
|
||||
version: string
|
||||
// Dashboard panels.
|
||||
panels?: [...{}]
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
# Dashboard Schemas
|
||||
|
||||
Schema description documents for [Grafana Dashboard
|
||||
JSON](https://grafana.com/docs/grafana/latest/reference/dashboard/) and core
|
||||
panels.
|
||||
|
||||
> **Note:** This directory is experimental. The schemas are not currently
|
||||
> implemented or enforced in Grafana.
|
||||
|
||||
Schemas are defined in [Cue](https://cuelang.org/). Cue was chosen because it
|
||||
strongly facilitates our primary use cases - [schema
|
||||
definition](https://cuelang.org/docs/usecases/datadef/), [data
|
||||
validation](https://cuelang.org/docs/usecases/validation/), and [code
|
||||
generation/extraction](https://cuelang.org/docs/usecases/generate/).
|
||||
|
||||
## Schema Organization
|
||||
|
||||
Each schema describes part of a dashboard. `Dashboard.cue` is the main dashboard
|
||||
schema object. All other schemas describe nested objects within a dashboard.
|
||||
They are grouped in the following directories:
|
||||
|
||||
* `panels` - schemas for
|
||||
[panels](https://grafana.com/docs/grafana/latest/panels/panels-overview/).
|
||||
* `targets` - targets represent
|
||||
[queries](https://grafana.com/docs/grafana/latest/panels/queries/). Each [data
|
||||
source](https://grafana.com/docs/grafana/latest/datasources/) type has a
|
||||
unique target schema.
|
||||
* `variables` - schemas for
|
||||
[variables](https://grafana.com/docs/grafana/latest/variables/variable-types/).
|
||||
* `transformations` - schemas for
|
||||
[transformations](https://grafana.com/docs/grafana/latest/panels/transformations/types-options/).
|
||||
|
||||
The following somewhat conveys how they fit together when constructing a
|
||||
dashboard:
|
||||
|
||||
```
|
||||
+-----------+ +-----------+
|
||||
| Dashboard +------> Variables |
|
||||
+---------+-+ +-----------+
|
||||
| +--------+ +---------+
|
||||
+----> Panels +----> Targets |
|
||||
+------+-+ +---------+
|
||||
| +-----------------+
|
||||
+------> Transformations |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
## Definitions
|
||||
|
||||
All schemas are [Cue
|
||||
definitions](https://cuelang.org/docs/references/spec/#definitions-and-hidden-fields).
|
||||
Schemas intended to be exported must begin with a capital letter. For example,
|
||||
[Gauge](./panels/Gauge.cue). Definitions beginning with a lowercase letter will
|
||||
not be exported. These are reusable components for constructing the exported
|
||||
definitions. For example, [`#panel`](./panels/panel.cue) is intended to
|
||||
be a base schema for panels. `#Gauge` extends `#panel` with the following:
|
||||
|
||||
```
|
||||
#Gauge: panel & {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Exporting OpenAPI
|
||||
|
||||
[OpenAPI](https://www.openapis.org/) schemas can be exported from these CUE
|
||||
sources.
|
||||
|
||||
### Command Line
|
||||
|
||||
While you can use `cue export` to output OpenAPI documents, it does not expand
|
||||
references which makes the output unusable.
|
||||
|
||||
```
|
||||
cue export --out openapi -o - ./...
|
||||
```
|
||||
|
||||
### Using Go
|
||||
|
||||
You need to use Go to generate useable OpenAPI schemas. This directory contains
|
||||
a Go program that will output just the OpenAPI schemas for one or many Cue
|
||||
packages.
|
||||
|
||||
```
|
||||
go run . <entrypoint> ...
|
||||
```
|
@ -1 +0,0 @@
|
||||
module: "github.com/grafana/grafana/dashboard-schemas"
|
@ -1,5 +0,0 @@
|
||||
module github.com/grafana/grafana/dashboard-schemas
|
||||
|
||||
go 1.15
|
||||
|
||||
require cuelang.org/go v0.2.2
|
@ -1,185 +0,0 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cuelang.org/go v0.2.2 h1:i/wFo48WDibGHKQTRZ08nB8PqmGpVpQ2sRflZPj73nQ=
|
||||
cuelang.org/go v0.2.2/go.mod h1:Dyjk8Y/B3CfFT1jQKJU0g5PpCeMiDe0yMOhk57oXwqo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE=
|
||||
github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw=
|
||||
github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200612220849-54c614fe050c h1:g6oFfz6Cmw68izP3xsdud3Oxu145IPkeFzyRg58AKHM=
|
||||
golang.org/x/tools v0.0.0-20200612220849-54c614fe050c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
@ -1,58 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/load"
|
||||
"cuelang.org/go/encoding/openapi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b, err := openAPISchemas(os.Args[1:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
// openAPISchemas returns OpenAPI schema JSON of the Cue entrypoints passed to
|
||||
// it. It is not a valid OpenAPI document - just the schemas.
|
||||
func openAPISchemas(entrypoints []string) ([]byte, error) {
|
||||
|
||||
var r cue.Runtime
|
||||
cfg := openapi.Config{
|
||||
ExpandReferences: true,
|
||||
}
|
||||
bis := load.Instances(entrypoints, nil)
|
||||
|
||||
// collect all schemas
|
||||
var pairs []openapi.KeyValue
|
||||
for _, bi := range bis {
|
||||
if bi.Err != nil {
|
||||
return nil, bi.Err
|
||||
}
|
||||
inst, err := r.Build(bi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
om, err := cfg.Schemas(inst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pairs = append(pairs, om.Pairs()...)
|
||||
}
|
||||
|
||||
// add all schemas to new ordered map
|
||||
om := openapi.OrderedMap{}
|
||||
om.SetAll(pairs)
|
||||
|
||||
j, err := om.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return j, nil
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOpenAPISchemas(t *testing.T) {
|
||||
|
||||
tests := map[string]struct {
|
||||
entrypoints []string
|
||||
}{
|
||||
"All packages": {
|
||||
entrypoints: []string{"./..."},
|
||||
},
|
||||
"One package": {
|
||||
entrypoints: []string{"./panels"},
|
||||
},
|
||||
"Many packags": {
|
||||
entrypoints: []string{
|
||||
"./panels",
|
||||
"./targets",
|
||||
"./transformations",
|
||||
"./variables",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for testName, test := range tests {
|
||||
|
||||
t.Logf("Running test case %s...", testName)
|
||||
|
||||
j, err := openAPISchemas(test.entrypoints)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// We don't want to validate the JSON content since it's expected to change
|
||||
// often. Only that it is valid JSON by unmarshalling it.
|
||||
|
||||
var iface interface{}
|
||||
err = json.Unmarshal(j, &iface)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
package panels
|
||||
|
||||
// Gauge is a single value panel that can repeat a gauge for every series,
|
||||
// column or row.
|
||||
#Gauge: _panel & {
|
||||
// Field config.
|
||||
fieldConfig: {
|
||||
// Defaults.
|
||||
defaults: {
|
||||
// Custom.
|
||||
custom: {}
|
||||
// Unit.
|
||||
unit: string
|
||||
// Min.
|
||||
min: int
|
||||
// Max.
|
||||
max: int
|
||||
// Decimals.
|
||||
decimals: int
|
||||
// Change the field or series name.
|
||||
displayName: string
|
||||
// What to show when there is no value.
|
||||
noValue: string
|
||||
// Threshold config.
|
||||
thresholds: _thresholds
|
||||
// Mappings.
|
||||
mappings: [..._mapping]
|
||||
// Data Links.
|
||||
links: [..._dataLink]
|
||||
}
|
||||
// Overrides.
|
||||
overrides: [..._override]
|
||||
}
|
||||
// Options.
|
||||
options: {
|
||||
// Reduce options.
|
||||
reduceOptions: {
|
||||
// * `true` - Show a calculated value based on all rows.
|
||||
// * `false` - Show a separate stat for every row.
|
||||
values: bool | *false
|
||||
// If values is false, sets max number of rows to
|
||||
// display.
|
||||
limit: int
|
||||
// Reducer function/calculation.
|
||||
calcs: [
|
||||
"allIsZero",
|
||||
"allIsNull",
|
||||
"changeCount",
|
||||
"count",
|
||||
"delta",
|
||||
"diff",
|
||||
"distinctCount",
|
||||
"first",
|
||||
"firstNotNull",
|
||||
"lastNotNull",
|
||||
"last",
|
||||
"logmin",
|
||||
"max",
|
||||
"min",
|
||||
"range",
|
||||
"step",
|
||||
"sum",
|
||||
] | *["mean"]
|
||||
// Fields that should be included in the panel.
|
||||
fields: string | *""
|
||||
}
|
||||
// Render the threshold values around the gauge bar.
|
||||
showThresholdLabels: bool | *false
|
||||
// Render the thresholds as an outer bar.
|
||||
showThresholdMarkers: bool | *true
|
||||
}
|
||||
// Panel type.
|
||||
type: string | *"gauge"
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
package panels
|
||||
|
||||
#Graph: _panel & {
|
||||
// Display values as a bar chart.
|
||||
bars: bool | *false
|
||||
// Dashed line length.
|
||||
dashLength: int | *10
|
||||
// Show line with dashes.
|
||||
dashes: bool | *false
|
||||
// Dashed line spacing when `dashes` is true.
|
||||
spaceLength: int | *10
|
||||
// Controls how many decimals are displayed for legend values and graph hover
|
||||
// tooltips.
|
||||
decimals: int
|
||||
// Field config.
|
||||
fieldConfig: {
|
||||
// Defaults.
|
||||
defaults: custom: {}
|
||||
// Overrides.
|
||||
overrides: [..._override]
|
||||
}
|
||||
// Amount of color fill for a series. Expects a value between 0 and 1.
|
||||
fill: number >= 0 <= 1 | *1
|
||||
// Degree of gradient on the area fill. 0 is no gradient, 10 is a steep
|
||||
// gradient.
|
||||
fillGradient: int >= 0 <= 10 | *0
|
||||
// Hide the series.
|
||||
hiddenSeries: bool | *false
|
||||
// Lengend options.
|
||||
legend: {
|
||||
// Whether to display legend in table.
|
||||
alignAsTable: bool | *false
|
||||
// Average of all values returned from the metric query.
|
||||
avg: bool | *false
|
||||
// Last value returned from the metric query.
|
||||
current: bool | *false
|
||||
// Maximum of all values returned from the metric query.
|
||||
max: bool | *false
|
||||
// Minimum of all values returned from the metric query.
|
||||
min: bool | *false
|
||||
// Display legend to the right.
|
||||
rightSide: bool | *false
|
||||
// Show or hide the legend.
|
||||
show: bool | *true
|
||||
// Available when `rightSide` is true. The minimum width for the legend in
|
||||
// pixels.
|
||||
sideWidth?: int
|
||||
// Sum of all values returned from the metric query.
|
||||
total: bool | *false
|
||||
// Values.
|
||||
values: bool | *true
|
||||
}
|
||||
// Display values as a line graph.
|
||||
lines: bool | *true
|
||||
// The width of the line for a series.
|
||||
linewidth: int | *1
|
||||
// How null values are displayed.
|
||||
// * 'null' - If there is a gap in the series, meaning a null value, then the
|
||||
// line in the graph will be broken and show the gap.
|
||||
// * 'null as zero' - If there is a gap in the series, meaning a null value,
|
||||
// then it will be displayed as a zero value in the graph panel.
|
||||
// * 'connected' - If there is a gap in the series, meaning a null value or
|
||||
// values, then the line will skip the gap and connect to the next non-null
|
||||
// value.
|
||||
nullPointMode: string | *"null"
|
||||
// Options.
|
||||
options: {
|
||||
// Data links.
|
||||
dataLinks: [..._dataLink]
|
||||
}
|
||||
// Available when `stack` is true. Each series is drawn as a percentage of the
|
||||
// total of all series.
|
||||
percentage: bool | *false
|
||||
// Controls how large the points are.
|
||||
pointradius: int
|
||||
// Display points for values.
|
||||
points: bool | *true
|
||||
// Renderer.
|
||||
renderer: string | *"flot"
|
||||
// Series overrides allow a series in a graph panel to be rendered
|
||||
// differently from the others. You can customize display options on a
|
||||
// per-series bases or by using regex rules. For example, one series can have
|
||||
// a thicker line width to make it stand out or be moved to the right Y-axis.
|
||||
seriesOverrides: [...{
|
||||
// Alias or regex matching the series you'd like to target.
|
||||
alias?: string
|
||||
bars?: bool
|
||||
lines?: bool
|
||||
fill?: int
|
||||
fillGradient?: int
|
||||
linewidth?: int
|
||||
nullPointMode?: string
|
||||
fillBelowTo?: string
|
||||
steppedLine?: bool
|
||||
dashes?: bool
|
||||
hiddenSeries?: bool
|
||||
dashLength?: int
|
||||
spaceLength?: int
|
||||
points?: bool
|
||||
pointradius?: int
|
||||
stack?: int
|
||||
color?: string
|
||||
yaxis?: int
|
||||
zindex?: int
|
||||
transform?: string
|
||||
legend?: bool
|
||||
hideTooltip?: bool
|
||||
}]
|
||||
// Each series is stacked on top of another.
|
||||
stack: bool | *false
|
||||
// Draws adjacent points as staircase.
|
||||
steppedLine: bool | *false
|
||||
// Threshold config.
|
||||
thresholds: _thresholds
|
||||
// Time from.
|
||||
timeFrom: string
|
||||
// Time regions.
|
||||
timeRegions: [...string]
|
||||
// Time shift
|
||||
timeShift: string
|
||||
// Tooltip settings.
|
||||
tooltip: {
|
||||
// * true - The hover tooltip shows all series in the graph. Grafana
|
||||
// highlights the series that you are hovering over in bold in the series
|
||||
// list in the tooltip.
|
||||
// * false - The hover tooltip shows only a single series, the one that you
|
||||
// are hovering over on the graph.
|
||||
shared: bool | *true
|
||||
// * 0 (none) - The order of the series in the tooltip is determined by the
|
||||
// sort order in your query. For example, they could be alphabetically
|
||||
// sorted by series name.
|
||||
// * 1 (increasing) - The series in the hover tooltip are sorted by value
|
||||
// and in increasing order, with the lowest value at the top of the list.
|
||||
// * 2 (decreasing) - The series in the hover tooltip are sorted by value
|
||||
// and in decreasing order, with the highest value at the top of the list.
|
||||
sort: int >= 0 <= 2 | *2
|
||||
// Value type.
|
||||
value_type: string | *"individual"
|
||||
}
|
||||
// Panel type.
|
||||
type: string | *"graph"
|
||||
xaxis: {
|
||||
// Buckets.
|
||||
buckets: string
|
||||
// The display mode completely changes the visualization of the graph
|
||||
// panel. It’s like three panels in one. The main mode is the time series
|
||||
// mode with time on the X-axis. The other two modes are a basic bar chart
|
||||
// mode with series on the X-axis instead of time and a histogram mode.
|
||||
// * 'time' - The X-axis represents time and that the data is grouped by
|
||||
// time (for example, by hour, or by minute).
|
||||
// * 'series' - The data is grouped by series and not by time. The Y-axis
|
||||
// still represents the value.
|
||||
// * 'histogram' - Converts the graph into a histogram. A histogram is a
|
||||
// kind of bar chart that groups numbers into ranges, often called buckets
|
||||
// or bins. Taller bars show that more data falls in that range.
|
||||
mode: string | *"time"
|
||||
// Name.
|
||||
name: string
|
||||
// Show or hide the axis.
|
||||
show: bool | *true
|
||||
// Values
|
||||
values: [...number]
|
||||
}
|
||||
yaxes: [...{
|
||||
// Defines how many decimals are displayed for Y value.
|
||||
decimals: int
|
||||
// The display unit for the Y value.
|
||||
format: string | *"short"
|
||||
// The Y axis label.
|
||||
label: string
|
||||
// The scale to use for the Y value - linear, or logarithmic.
|
||||
// * 1 - linear
|
||||
// * 2 - log (base 2)
|
||||
// * 10 - log (base 10)
|
||||
// * 32 - log (base 32)
|
||||
// * 1024 - log (base 1024)
|
||||
logBase: int | *1
|
||||
// The maximum Y value.
|
||||
max?: int
|
||||
// The minimum Y value.
|
||||
min?: int
|
||||
// Show or hide the axis.
|
||||
show: bool | *true
|
||||
}]
|
||||
yaxis: {
|
||||
// Align left and right Y-axes by value.
|
||||
align: bool | *false
|
||||
// Available when align is true. Value to use for alignment of left and
|
||||
// right Y-axes, starting from Y=0.
|
||||
alignLevel: int | *0
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package panels
|
||||
|
||||
// A row is a logical divider within a dashboard. It is used
|
||||
// to group panels together.
|
||||
#Row: {
|
||||
// Whether the row is collapsed or not.
|
||||
collapsed: bool | *true
|
||||
// Name of default data source.
|
||||
datasource?: string
|
||||
// Grid position.
|
||||
gridPos?: _gridPos
|
||||
// Dashboard panels.
|
||||
panels?: [...{}]
|
||||
// Name of template variable to repeat for.
|
||||
repeat?: string
|
||||
// Whether to display the title.
|
||||
showTitle: bool | *true
|
||||
// Title.
|
||||
title?: string
|
||||
// Size of title.
|
||||
titleSize: string | *"h6"
|
||||
// Panel type.
|
||||
type: string | *"row"
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package panels
|
||||
|
||||
_gridPos: {
|
||||
// Panel height.
|
||||
h?: int > 0 | *9
|
||||
// Panel width.
|
||||
w?: int > 0 <= 24 | *12
|
||||
// Panel x position.
|
||||
x?: int >= 0 < 24 | *0
|
||||
// Panel y position.
|
||||
y?: int >= 0 | *0
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package panels
|
||||
|
||||
_link: {
|
||||
// Link title.
|
||||
title?: string
|
||||
// Whether to open link in new browser tab.
|
||||
targetBlank: bool | *true
|
||||
// URL of link.
|
||||
url: string
|
||||
}
|
||||
|
||||
_panelLink: _link
|
||||
|
||||
_dataLink: _link
|
@ -1,11 +0,0 @@
|
||||
package panels
|
||||
|
||||
_mapping: {
|
||||
id: int
|
||||
from: string
|
||||
operator: string
|
||||
to: string
|
||||
text: string
|
||||
type: int
|
||||
value: string
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package panels
|
||||
|
||||
_override: {
|
||||
matcher: {
|
||||
id: string
|
||||
options: string
|
||||
}
|
||||
properties: [...{
|
||||
id: string
|
||||
value: int
|
||||
}]
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package panels
|
||||
|
||||
_panel: {
|
||||
// Panel title.
|
||||
title?: string
|
||||
// Description.
|
||||
description?: string
|
||||
// Whether to display the panel without a background.
|
||||
transparent: bool | *false
|
||||
// Name of default datasource.
|
||||
datasource?: string
|
||||
// Grid position.
|
||||
gridPos?: _gridPos
|
||||
// Panel links.
|
||||
links?: [..._panelLink]
|
||||
// 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"
|
||||
// Panel targets - datasource and query configurations to use as
|
||||
// a basis for vizualization.
|
||||
targets?: [...{}]
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package panels
|
||||
|
||||
_thresholds: {
|
||||
// Threshold mode.
|
||||
mode: string | *"absolute"
|
||||
// Threshold steps.
|
||||
steps: [...{
|
||||
color: string
|
||||
value: number
|
||||
}]
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package targets
|
||||
|
||||
#Prometheus: {
|
||||
// Query expression.
|
||||
expr: string
|
||||
// Controls the name of the time series, using name or pattern.
|
||||
legendFormat?: string
|
||||
// Interval.
|
||||
interval?: int | *1
|
||||
// Target reference ID.
|
||||
refId: string
|
||||
// Perform an “instant” query, to return only the latest value that
|
||||
// Prometheus has scraped for the requested time series.
|
||||
instant: bool | *false
|
||||
// Resolution.
|
||||
intervalFactor?: int
|
||||
// Format.
|
||||
format: *"time_series" | "table" | "heat_map"
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package transformations
|
||||
|
||||
// Add field from calculation.
|
||||
#CalculateField: {
|
||||
// Transformation ID.
|
||||
id: string | *"calculateField"
|
||||
// Configuration options.
|
||||
options: {
|
||||
// The name of your new field. If you leave this blank, then the field will
|
||||
// be named to match the calculation.
|
||||
alias: string
|
||||
// Binary options.
|
||||
binary: {
|
||||
// Field or number for left side of equation.
|
||||
left: string
|
||||
// Field or number for right side of equation.
|
||||
right: string
|
||||
// Operator.
|
||||
operator: string | *"+"
|
||||
// Calculation to use.
|
||||
reducer: string | *"sum"
|
||||
}
|
||||
// 'reduceRow' - apply selected calculation on each row of selected fields
|
||||
// independently.
|
||||
// 'binary' - apply basic math operation(sum, multiply, etc) on values in a
|
||||
// single row from two selected fields.
|
||||
mode: *"reduceRow" | "binary"
|
||||
// Reduce options.
|
||||
reduce: {
|
||||
// Calculation to use.
|
||||
reducer: string
|
||||
// Fields to include in calculation.
|
||||
include: [...string]
|
||||
}
|
||||
// Hide all other fields and display only your calculated field in the
|
||||
// visualization.
|
||||
replaceFields: bool | *false
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package transformations
|
||||
|
||||
// Reorder, hide, or rename fields/columns.
|
||||
#Organize: {
|
||||
// Transformation ID.
|
||||
id: string | *"organize"
|
||||
// Configuration options.
|
||||
options: {
|
||||
// Exclude fields by name.
|
||||
excludeByName: {}
|
||||
// Set field order by name.
|
||||
indexByName: {}
|
||||
// Rename a field by name.
|
||||
renameByName: {}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package variables
|
||||
|
||||
// Custom variables are for values that do not change.
|
||||
#Custom: _variable & {
|
||||
// Options as comma separated values.
|
||||
query: string
|
||||
// Variable type.
|
||||
type: string | *"custom"
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package variables
|
||||
|
||||
// Data source variables allow you to quickly change the data source for an
|
||||
// entire dashboard.
|
||||
#Datasource: _variable & {
|
||||
// Data source type.
|
||||
query: string
|
||||
// Query value.
|
||||
queryValue: string | *""
|
||||
// Refresh.
|
||||
refresh: int | *1
|
||||
// Regex filter for which data source instances to choose
|
||||
// from in the variable value dropdown. Leave empty for
|
||||
// all.
|
||||
regex: string
|
||||
// Variable type.
|
||||
type: string | *"datasource"
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package variables
|
||||
|
||||
// Query variables allow you to write a data source query that can return a
|
||||
// list of metric names, tag values, or keys.
|
||||
#Query: _variable & {
|
||||
// Data source to use.
|
||||
datasource: string
|
||||
// Definition.
|
||||
definition?: string
|
||||
// Query.
|
||||
query: string
|
||||
// Refresh.
|
||||
refresh: int | *1
|
||||
// Regex.
|
||||
regex?: string
|
||||
// * 0 - Disabled.
|
||||
// * 1 - Alphabetical (asc).
|
||||
// * 2 - Alphabetical (desc).
|
||||
// * 3 - Numerical (asc).
|
||||
// * 4 - Numerical (desc).
|
||||
// * 5 - Alphabetical (case-insensitive, asc).
|
||||
// * 6 - Alphabetical (case-insensitive, desc).
|
||||
sort: int >= 0 <= 6 | *0
|
||||
tagValuesQuery?: string
|
||||
tags: [...string] | *[]
|
||||
tagsQuery?: string
|
||||
// Variable type.
|
||||
type: "query"
|
||||
useTags: bool | *false
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package variables
|
||||
|
||||
_variable: {
|
||||
// Currently selected value.
|
||||
current: {
|
||||
selected: bool | *false
|
||||
text: string | [...string]
|
||||
value: string | [...string]
|
||||
}
|
||||
// Whether to hide the label and variable.
|
||||
// * 0 - Show all.
|
||||
// * 1 - Hide label.
|
||||
// * 2 - Hide label and variable.
|
||||
hide: int >= 0 <= 2 | *0
|
||||
// Enable include all option.
|
||||
includeAll: bool | *false
|
||||
// When includeAll is enabled, this sets its value.
|
||||
allValue?: string
|
||||
// Optional display name.
|
||||
label?: string
|
||||
// Allows mutltiple values to be selected at the same time.
|
||||
multi: bool | *false
|
||||
// Variable name.
|
||||
name: string
|
||||
// Options for variable.
|
||||
options: [...{
|
||||
selected: bool
|
||||
text: string
|
||||
value: string
|
||||
}]
|
||||
// Skip URL sync.
|
||||
skipUrlSync: bool | *false
|
||||
}
|
21
embed.go
Normal file
21
embed.go
Normal file
@ -0,0 +1,21 @@
|
||||
package grafana
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
// CoreSchema embeds all CUE files within the cue/ subdirectory.
|
||||
//
|
||||
// TODO good rule about where to search
|
||||
//
|
||||
//go:embed cue/*/*.cue
|
||||
var CoreSchema embed.FS
|
||||
|
||||
// TODO good rule about where to search
|
||||
//
|
||||
//go:embed public/app/plugins/*/*/*.cue public/app/plugins/*/*/plugin.json
|
||||
var base embed.FS
|
||||
|
||||
// PluginSchema embeds all CUE files within the public/ subdirectory.
|
||||
var PluginSchema, _ = fs.Sub(base, "public/app/plugins")
|
4
go.mod
4
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
// Override xorm's outdated go-mssqldb dependency, since we can't upgrade to current xorm (due to breaking changes).
|
||||
// We need a more current go-mssqldb so we get rid of a version of apache/thrift with vulnerabilities.
|
||||
@ -13,6 +13,7 @@ replace k8s.io/client-go => k8s.io/client-go v0.18.8
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.14.0
|
||||
cuelang.org/go v0.3.0-beta.6
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f
|
||||
github.com/aws/aws-sdk-go v1.38.12
|
||||
@ -83,6 +84,7 @@ require (
|
||||
github.com/yudai/gojsondiff v1.0.0
|
||||
go.opentelemetry.io/collector v0.22.0
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/exp v0.0.0-20210220032938-85be41e4509f // indirect
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
|
25
go.sum
25
go.sum
@ -50,7 +50,10 @@ code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk
|
||||
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.2.0/go.mod h1:TYmVAyE8Tn1lyPcltF5IYYfWp2KHu7lQGIZnj8iZMys=
|
||||
cuelang.org/go v0.3.0-beta.6 h1:od1S/Hbl2S45TLSONl95X3O4TXN1za6CUSD13bTxCVk=
|
||||
cuelang.org/go v0.3.0-beta.6/go.mod h1:Ikvs157igkGV5gFUdYSFa+lWp/CDteVhubPTXyvPRtA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
@ -275,7 +278,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE=
|
||||
github.com/cockroachdb/apd/v2 v2.0.1/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
|
||||
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190531201743-edce55837238/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
@ -389,6 +395,8 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/ema/qdisc v0.0.0-20190904071900-b82c76788043/go.mod h1:ix4kG2zvdUd8kEKSW0ZTr1XLks0epFpI4j745DXxlNE=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw=
|
||||
github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@ -1028,7 +1036,6 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
|
||||
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
|
||||
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
|
||||
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
@ -1222,6 +1229,8 @@ github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOA
|
||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/mozillazg/go-cos v0.13.0/go.mod h1:Zp6DvvXn0RUOXGJ2chmWt2bLEqRAnJnS3DnAZsJsoaE=
|
||||
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@ -1451,6 +1460,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.2-0.20200830194709-1115b6af0369 h1:wdCVGtPadWC/ZuuLC7Hv58VQ5UF7V98ewE71n5mJfrM=
|
||||
github.com/rogpeppe/go-internal v1.6.2-0.20200830194709-1115b6af0369/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
@ -1530,6 +1541,7 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
@ -1759,6 +1771,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191029154019-8994fa331a53/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
@ -1768,8 +1781,10 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp v0.0.0-20200821190819-94841d0725da h1:vfV2BR+q1+/jmgJR30Ms3RHbryruQ3Yd83lLAAue9cs=
|
||||
golang.org/x/exp v0.0.0-20200821190819-94841d0725da/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20210126221216-84987778548c/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4=
|
||||
golang.org/x/exp v0.0.0-20210220032938-85be41e4509f h1:GrkO5AtFUU9U/1f5ctbIBXtBGeSJbWwIYfIsTcFMaX4=
|
||||
golang.org/x/exp v0.0.0-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
@ -1788,12 +1803,15 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@ -2071,6 +2089,7 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@ -2090,6 +2109,7 @@ golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200513201620-d5fe73897c97/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200603131246-cc40288be839/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200612220849-54c614fe050c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200710042808-f1c4188a97a1/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
@ -2329,6 +2349,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
213
packages/grafana-data/src/types/dashboard.cue
Normal file
213
packages/grafana-data/src/types/dashboard.cue
Normal file
@ -0,0 +1,213 @@
|
||||
package grafanaschema
|
||||
|
||||
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
|
||||
panels?: [...#Panel]
|
||||
|
||||
// Dashboard panels. Panels are canonically defined inline
|
||||
// because they share a version timeline with the dashboard
|
||||
// schema; they do not vary independently. We create a separate,
|
||||
// synthetic Family to represent them in Go, for ease of generating
|
||||
// e.g. JSON Schema.
|
||||
#Panel: {
|
||||
// The panel plugin type id.
|
||||
type: !=""
|
||||
|
||||
// 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 }
|
||||
|
||||
// Panel title.
|
||||
title?: string
|
||||
// Description.
|
||||
description?: string
|
||||
// Whether to display the panel without a background.
|
||||
transparent: bool | *false
|
||||
// Name of default datasource.
|
||||
datasource?: 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.
|
||||
// links?: [..._panelLink]
|
||||
// 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"
|
||||
// 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.
|
||||
targets?: [...{}]
|
||||
|
||||
// The values depend on panel type
|
||||
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
|
||||
// mappings?: ValueMapping[];
|
||||
|
||||
// // 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?: DataLink[];
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string
|
||||
|
||||
// Can always exist. Valid fields within this are
|
||||
// defined by the panel plugin - that's the
|
||||
// PanelFieldConfig that comes from the plugin.
|
||||
custom?: {...}
|
||||
}
|
||||
overrides: [...{
|
||||
matcher: {
|
||||
id: string | *""
|
||||
options?: _
|
||||
}
|
||||
properties: [...{
|
||||
id: string | *""
|
||||
value?: _
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
#Latest: {
|
||||
#Dashboard: dashboardFamily.latest
|
||||
#Panel: dashboardFamily.latest._Panel
|
||||
}
|
@ -2,7 +2,7 @@ import { toDataFrame, FieldType, VizOrientation } from '@grafana/data';
|
||||
import React from 'react';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { BarChart } from './BarChart';
|
||||
import { LegendDisplayMode } from '../VizLegend/types';
|
||||
import { LegendDisplayMode } from '../VizLegend/models.gen';
|
||||
import { prepDataForStorybook } from '../../utils/storybook/data';
|
||||
import { useTheme } from '../../themes';
|
||||
import { select } from '@storybook/addon-knobs';
|
||||
|
@ -10,7 +10,7 @@ import { BarChartOptions } from './types';
|
||||
import { withTheme } from '../../themes';
|
||||
import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
|
||||
import { preparePlotData } from '../uPlot/utils';
|
||||
import { LegendDisplayMode } from '../VizLegend/types';
|
||||
import { LegendDisplayMode } from '../VizLegend/models.gen';
|
||||
import { PlotLegend } from '../uPlot/PlotLegend';
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { VizOrientation } from '@grafana/data';
|
||||
import { AxisConfig, GraphGradientMode, HideableFieldConfig } from '../uPlot/config';
|
||||
import { VizLegendOptions } from '../VizLegend/types';
|
||||
import { VizLegendOptions } from '../VizLegend/models.gen';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
|
@ -2,7 +2,7 @@ import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
|
||||
import { FieldConfig, FieldType, GrafanaTheme, MutableDataFrame, VizOrientation } from '@grafana/data';
|
||||
import { BarChartFieldConfig, BarChartOptions, BarStackingMode, BarValueVisibility } from './types';
|
||||
import { GraphGradientMode } from '../uPlot/config';
|
||||
import { LegendDisplayMode } from '../VizLegend/types';
|
||||
import { LegendDisplayMode } from '../VizLegend/models.gen';
|
||||
|
||||
function mockDataFrame() {
|
||||
const df1 = new MutableDataFrame({
|
||||
|
@ -5,8 +5,7 @@ import { Dimensions, TimeZone } from '@grafana/data';
|
||||
import { FlotPosition } from '../Graph/types';
|
||||
import { TooltipContainer } from './TooltipContainer';
|
||||
import { useStyles } from '../../themes';
|
||||
|
||||
export type TooltipMode = 'single' | 'multi' | 'none';
|
||||
import { TooltipMode } from './models.gen';
|
||||
|
||||
// Describes active dimensions user interacts with
|
||||
// It's a key-value pair where:
|
||||
|
3
packages/grafana-ui/src/components/Chart/models.cue
Normal file
3
packages/grafana-ui/src/components/Chart/models.cue
Normal file
@ -0,0 +1,3 @@
|
||||
package grafanaschema
|
||||
|
||||
TooltipMode: "single" | "multi" | "none" @cuetsy(targetType="type")
|
6
packages/grafana-ui/src/components/Chart/models.gen.ts
Normal file
6
packages/grafana-ui/src/components/Chart/models.gen.ts
Normal file
@ -0,0 +1,6 @@
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// NOTE: This file will be auto generated from models.cue
|
||||
// It is currenty hand written but will serve as the target for cuetsy
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
export type TooltipMode = 'single' | 'multi' | 'none';
|
@ -4,7 +4,8 @@ import Chart from '../Chart';
|
||||
import { dateTime, ArrayVector, FieldType, GraphSeriesXY, FieldColorModeId } from '@grafana/data';
|
||||
import { Story } from '@storybook/react';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { TooltipContentProps, TooltipMode } from '../Chart/Tooltip';
|
||||
import { TooltipContentProps } from '../Chart/Tooltip';
|
||||
import { TooltipMode } from '../Chart/models.gen';
|
||||
import { JSONFormatter } from '../JSONFormatter/JSONFormatter';
|
||||
import { GraphProps } from './Graph';
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
package grafanaschema
|
||||
|
||||
|
||||
// TODO Relative imports are flatly disallowed by CUE, but that's what's
|
||||
// currently done in the corresponding typescript code. We'll have to make
|
||||
// cuetsy handle this with import mappings.
|
||||
import tooltip "github.com/grafana/grafana/packages/grafana-ui/src/components/Chart:grafanaschema"
|
||||
|
||||
GraphTooltipOptions: {
|
||||
mode: tooltip.TooltipMode
|
||||
} @cuetsy(targetType="interface")
|
@ -0,0 +1,10 @@
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// NOTE: This file will be auto generated from models.cue
|
||||
// It is currenty hand written but will serve as the target for cuetsy
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
import { TooltipMode } from '../../Chart/models.gen';
|
||||
|
||||
export interface GraphTooltipOptions {
|
||||
mode: TooltipMode;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { ActiveDimensions, TooltipMode } from '../../Chart/Tooltip';
|
||||
import { ActiveDimensions } from '../../Chart/Tooltip';
|
||||
import { Dimension, Dimensions, TimeZone } from '@grafana/data';
|
||||
import { TooltipMode } from '../../Chart/models.gen';
|
||||
|
||||
export interface GraphTooltipOptions {
|
||||
mode: TooltipMode;
|
||||
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||
import { Story } from '@storybook/react';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { GraphWithLegend, GraphWithLegendProps } from './GraphWithLegend';
|
||||
import { LegendDisplayMode } from '../VizLegend/types';
|
||||
import { LegendDisplayMode } from '../VizLegend/models.gen';
|
||||
import { GraphSeriesXY, FieldType, ArrayVector, dateTime, FieldColorModeId } from '@grafana/data';
|
||||
|
||||
export default {
|
||||
|
@ -5,7 +5,8 @@ import { css } from '@emotion/css';
|
||||
import { GraphSeriesValue } from '@grafana/data';
|
||||
|
||||
import { Graph, GraphProps } from './Graph';
|
||||
import { VizLegendItem, LegendDisplayMode, SeriesColorChangeHandler, LegendPlacement } from '../VizLegend/types';
|
||||
import { VizLegendItem, SeriesColorChangeHandler } from '../VizLegend/types';
|
||||
import { LegendDisplayMode, LegendPlacement } from '../VizLegend/models.gen';
|
||||
import { VizLegend } from '../VizLegend/VizLegend';
|
||||
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
|
||||
import { stylesFactory } from '../../themes';
|
||||
|
@ -2,7 +2,7 @@ import { FieldColorModeId, toDataFrame, dateTime } from '@grafana/data';
|
||||
import React from 'react';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { GraphNG, GraphNGProps } from './GraphNG';
|
||||
import { LegendDisplayMode, LegendPlacement } from '../VizLegend/types';
|
||||
import { LegendDisplayMode, LegendPlacement } from '../VizLegend/models.gen';
|
||||
import { prepDataForStorybook } from '../../utils/storybook/data';
|
||||
import { useTheme } from '../../themes';
|
||||
import { Story } from '@storybook/react';
|
||||
|
@ -20,7 +20,7 @@ import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
|
||||
import { preparePlotData } from '../uPlot/utils';
|
||||
import { PlotLegend } from '../uPlot/PlotLegend';
|
||||
import { UPlotChart } from '../uPlot/Plot';
|
||||
import { LegendDisplayMode, VizLegendOptions } from '../VizLegend/types';
|
||||
import { LegendDisplayMode, VizLegendOptions } from '../VizLegend/models.gen';
|
||||
import { VizLayout } from '../VizLayout/VizLayout';
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ import { useComponentInstanceId } from '../../utils/useComponetInstanceId';
|
||||
import { css } from '@emotion/css';
|
||||
import { VizLegend, VizLegendItem } from '..';
|
||||
import { VizLayout } from '../VizLayout/VizLayout';
|
||||
import { LegendDisplayMode, VizLegendOptions } from '../VizLegend/types';
|
||||
import { LegendDisplayMode, VizLegendOptions } from '../VizLegend/models.gen';
|
||||
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
|
||||
import { UseTooltipParams } from '@visx/tooltip/lib/hooks/useTooltip';
|
||||
|
||||
|
28
packages/grafana-ui/src/components/Table/models.cue
Normal file
28
packages/grafana-ui/src/components/Table/models.cue
Normal file
@ -0,0 +1,28 @@
|
||||
package grafanaschema
|
||||
|
||||
FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(targetType="type")
|
||||
|
||||
TableCellDisplayMode: {
|
||||
Auto: "auto",
|
||||
ColorText: "color-text",
|
||||
ColorBackground: "color-background",
|
||||
GradientGauge: "gradient-gauge",
|
||||
LcdGauge: "lcd-gauge",
|
||||
JSONView: "json-view",
|
||||
BasicGauge: "basic",
|
||||
Image: "image",
|
||||
} @cuetsy(targetType="enum")
|
||||
|
||||
|
||||
TableFieldOptions: {
|
||||
width?: number
|
||||
align: FieldTextAlignment | *"auto"
|
||||
displayMode: TableCellDisplayMode | *"auto"
|
||||
hidden?: bool // ?? default is missing or false ??
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
|
||||
TableSortByFieldState: {
|
||||
displayName: string
|
||||
desc?: bool
|
||||
} @cuetsy(targetType="interface")
|
@ -7,7 +7,7 @@ import { preparePlotConfigBuilder, preparePlotFrame } from './utils'; // << prep
|
||||
import { preparePlotData } from '../uPlot/utils';
|
||||
import { PlotLegend } from '../uPlot/PlotLegend';
|
||||
import { UPlotChart } from '../uPlot/Plot';
|
||||
import { LegendDisplayMode } from '../VizLegend/types';
|
||||
import { LegendDisplayMode } from '../VizLegend/models.gen';
|
||||
import { VizLayout } from '../VizLayout/VizLayout';
|
||||
import { TimelineProps } from './types';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { GraphNGProps } from '../GraphNG/GraphNG';
|
||||
import { GraphGradientMode, HideableFieldConfig } from '../uPlot/config';
|
||||
import { VizLegendOptions } from '../VizLegend/types';
|
||||
import { VizLegendOptions } from '../VizLegend/models.gen';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
|
@ -4,7 +4,8 @@ import { number, select } from '@storybook/addon-knobs';
|
||||
import {} from './VizLegendListItem';
|
||||
import { DisplayValue, getColorForTheme, GrafanaTheme } from '@grafana/data';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { LegendDisplayMode, VizLegendItem, LegendPlacement } from './types';
|
||||
import { VizLegendItem } from './types';
|
||||
import { LegendDisplayMode, LegendPlacement } from './models.gen';
|
||||
|
||||
const getStoriesKnobs = (table = false) => {
|
||||
const seriesCount = number('Number of series', 5);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { LegendProps, LegendDisplayMode } from './types';
|
||||
import { LegendProps } from './types';
|
||||
import { LegendDisplayMode } from './models.gen';
|
||||
import { VizLegendTable } from './VizLegendTable';
|
||||
import { VizLegendList } from './VizLegendList';
|
||||
|
||||
|
12
packages/grafana-ui/src/components/VizLegend/models.cue
Normal file
12
packages/grafana-ui/src/components/VizLegend/models.cue
Normal file
@ -0,0 +1,12 @@
|
||||
package grafanaschema
|
||||
|
||||
LegendPlacement: "bottom" | "right" @cuetsy(targetType="type")
|
||||
LegendDisplayMode: "list" | "table" | "hidden" @cuetsy(targetType="enum")
|
||||
|
||||
VizLegendOptions: {
|
||||
displayMode: LegendDisplayMode
|
||||
placement: LegendPlacement
|
||||
calcs: [...string]
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
// TODO this excludes all the types that include function definitions
|
16
packages/grafana-ui/src/components/VizLegend/models.gen.ts
Normal file
16
packages/grafana-ui/src/components/VizLegend/models.gen.ts
Normal file
@ -0,0 +1,16 @@
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// NOTE: This file will be auto generated from models.cue
|
||||
// It is currenty hand written but will serve as the target for cuetsy
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
export type LegendPlacement = 'bottom' | 'right';
|
||||
export enum LegendDisplayMode {
|
||||
Hidden = 'hidden',
|
||||
List = 'list',
|
||||
Table = 'table',
|
||||
}
|
||||
export interface VizLegendOptions {
|
||||
calcs: string[];
|
||||
displayMode: LegendDisplayMode;
|
||||
placement: LegendPlacement;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import { DataFrameFieldIndex, DisplayValue } from '@grafana/data';
|
||||
import { LegendDisplayMode, LegendPlacement } from './models.gen';
|
||||
|
||||
export interface VizLegendBaseProps {
|
||||
placement: LegendPlacement;
|
||||
@ -30,19 +31,5 @@ export interface VizLegendItem {
|
||||
fieldIndex?: DataFrameFieldIndex;
|
||||
}
|
||||
|
||||
export enum LegendDisplayMode {
|
||||
List = 'list',
|
||||
Table = 'table',
|
||||
Hidden = 'hidden',
|
||||
}
|
||||
|
||||
export type LegendPlacement = 'bottom' | 'right';
|
||||
|
||||
export interface VizLegendOptions {
|
||||
displayMode: LegendDisplayMode;
|
||||
placement: LegendPlacement;
|
||||
calcs: string[];
|
||||
}
|
||||
|
||||
export type SeriesOptionChangeHandler<TOption> = (label: string, option: TOption) => void;
|
||||
export type SeriesColorChangeHandler = SeriesOptionChangeHandler<string>;
|
||||
|
@ -83,7 +83,8 @@ export { VizRepeater, VizRepeaterRenderValueProps } from './VizRepeater/VizRepea
|
||||
export { graphTimeFormat, graphTickFormatter } from './Graph/utils';
|
||||
export { PanelChrome, PanelChromeProps, PanelPadding, PanelChromeType } from './PanelChrome';
|
||||
export { VizLayout, VizLayoutComponentType, VizLayoutLegendProps, VizLayoutProps } from './VizLayout/VizLayout';
|
||||
export { VizLegendItem, LegendPlacement, LegendDisplayMode, VizLegendOptions } from './VizLegend/types';
|
||||
export { VizLegendItem } from './VizLegend/types';
|
||||
export { LegendPlacement, LegendDisplayMode, VizLegendOptions } from './VizLegend/models.gen';
|
||||
export { VizLegend } from './VizLegend/VizLegend';
|
||||
|
||||
export { Alert, AlertVariant } from './Alert/Alert';
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { DataFrame, DisplayValue, fieldReducers, getFieldDisplayName, reduceField } from '@grafana/data';
|
||||
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
|
||||
import { VizLegendItem, VizLegendOptions } from '../VizLegend/types';
|
||||
import { VizLegendItem } from '../VizLegend/types';
|
||||
import { VizLegendOptions } from '../VizLegend/models.gen';
|
||||
import { AxisPlacement } from './config';
|
||||
import { VizLayout, VizLayoutLegendProps } from '../VizLayout/VizLayout';
|
||||
import { mapMouseEventToMode } from '../GraphNG/utils';
|
||||
|
60
packages/grafana-ui/src/components/uPlot/models.cue
Normal file
60
packages/grafana-ui/src/components/uPlot/models.cue
Normal file
@ -0,0 +1,60 @@
|
||||
package grafanaschema
|
||||
|
||||
AxisPlacement: "auto" | "top" | "right" | "bottom" | "left" | "hidden" @cuetsy(targetType="enum")
|
||||
PointVisibility: "auto" | "never" | "always" @cuetsy(targetType="enum")
|
||||
DrawStyle: "line" | "bars" | "points" @cuetsy(targetType="enum")
|
||||
LineInterpolation: "linear" | "smooth" | "stepBefore" | "stepAfter" @cuetsy(targetType="enum")
|
||||
ScaleDistribution: "linear" | "log" @cuetsy(targetType="enum")
|
||||
GraphGradientMode: "none" | "opacity" | "hue" | "scheme" @cuetsy(targetType="enum")
|
||||
|
||||
LineStyle: {
|
||||
fill?: "solid" | "dash" | "dot" | "square"
|
||||
dash?: [...number]
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
LineConfig: {
|
||||
lineColor?: string
|
||||
lineWidth?: number
|
||||
lineInterpolation?: LineInterpolation
|
||||
lineStyle?: LineStyle
|
||||
spanNulls?: bool
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
FillConfig: {
|
||||
fillColor?: string
|
||||
fillOpacity?: number
|
||||
fillBelowTo?: string
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
PointsConfig: {
|
||||
showPoints?: PointVisibility
|
||||
pointSize?: number
|
||||
pointColor?: string
|
||||
pointSymbol?: string
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
ScaleDistributionConfig: {
|
||||
type: ScaleDistribution
|
||||
log?: number
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
AxisConfig: {
|
||||
axisPlacement?: AxisPlacement
|
||||
axisLabel?: string
|
||||
axisWidth?: number
|
||||
axisSoftMin?: number
|
||||
axisSoftMax?: number
|
||||
scaleDistribution?: ScaleDistributionConfig
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
HideSeriesConfig: {
|
||||
tooltip: bool
|
||||
legend: bool
|
||||
graph: bool
|
||||
} @cuetsy(targetType="interface")
|
||||
|
||||
GraphFieldConfig: LineConfig & FillConfig & PointsConfig & AxisConfig & {
|
||||
drawStyle?: DrawStyle
|
||||
gradientMode?: GraphGradientMode
|
||||
hideFrom?: HideSeriesConfig
|
||||
} @cuetsy(targetType="interface")
|
83
packages/grafana-ui/src/components/uPlot/models.gen.ts
Normal file
83
packages/grafana-ui/src/components/uPlot/models.gen.ts
Normal file
@ -0,0 +1,83 @@
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// NOTE: This file will be auto generated from models.cue
|
||||
// It is currenty hand written but will serve as the target for cuetsy
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
export enum AxisPlacement {
|
||||
Auto = 'auto',
|
||||
Bottom = 'bottom',
|
||||
Hidden = 'hidden',
|
||||
Left = 'left',
|
||||
Right = 'right',
|
||||
Top = 'top',
|
||||
}
|
||||
export enum PointVisibility {
|
||||
Always = 'always',
|
||||
Auto = 'auto',
|
||||
Never = 'never',
|
||||
}
|
||||
export enum DrawStyle {
|
||||
Bars = 'bars',
|
||||
Line = 'line',
|
||||
Points = 'points',
|
||||
}
|
||||
export enum LineInterpolation {
|
||||
Linear = 'linear',
|
||||
Smooth = 'smooth',
|
||||
StepAfter = 'stepAfter',
|
||||
StepBefore = 'stepBefore',
|
||||
}
|
||||
export enum ScaleDistribution {
|
||||
Linear = 'linear',
|
||||
Log = 'log',
|
||||
}
|
||||
export enum GraphGradientMode {
|
||||
Hue = 'hue',
|
||||
None = 'none',
|
||||
Opacity = 'opacity',
|
||||
Scheme = 'scheme',
|
||||
}
|
||||
export interface LineStyle {
|
||||
dash?: number[];
|
||||
fill?: 'solid' | 'dash' | 'dot' | 'square';
|
||||
}
|
||||
export interface LineConfig {
|
||||
lineColor?: string;
|
||||
lineInterpolation?: LineInterpolation;
|
||||
lineStyle?: LineStyle;
|
||||
lineWidth?: number;
|
||||
spanNulls?: boolean;
|
||||
}
|
||||
export interface FillConfig {
|
||||
fillBelowTo?: string;
|
||||
fillColor?: string;
|
||||
fillOpacity?: number;
|
||||
}
|
||||
export interface PointsConfig {
|
||||
pointColor?: string;
|
||||
pointSize?: number;
|
||||
pointSymbol?: string;
|
||||
showPoints?: PointVisibility;
|
||||
}
|
||||
export interface ScaleDistributionConfig {
|
||||
log?: number;
|
||||
type: ScaleDistribution;
|
||||
}
|
||||
export interface AxisConfig {
|
||||
axisLabel?: string;
|
||||
axisPlacement?: AxisPlacement;
|
||||
axisSoftMax?: number;
|
||||
axisSoftMin?: number;
|
||||
axisWidth?: number;
|
||||
scaleDistribution?: ScaleDistributionConfig;
|
||||
}
|
||||
export interface HideSeriesConfig {
|
||||
graph: boolean;
|
||||
legend: boolean;
|
||||
tooltip: boolean;
|
||||
}
|
||||
export interface GraphFieldConfig extends LineConfig, FillConfig, PointsConfig, AxisConfig {
|
||||
drawStyle?: DrawStyle;
|
||||
gradientMode?: GraphGradientMode;
|
||||
hideFrom?: HideSeriesConfig;
|
||||
}
|
@ -12,7 +12,7 @@ import {
|
||||
TimeZone,
|
||||
} from '@grafana/data';
|
||||
import { TooltipContainer } from '../../Chart/TooltipContainer';
|
||||
import { TooltipMode } from '../../Chart/Tooltip';
|
||||
import { TooltipMode } from '../../Chart/models.gen';
|
||||
import { useGraphNGContext } from '../../GraphNG/hooks';
|
||||
|
||||
interface TooltipPluginProps {
|
||||
|
87
pkg/schema/load/common.go
Normal file
87
pkg/schema/load/common.go
Normal file
@ -0,0 +1,87 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/load"
|
||||
)
|
||||
|
||||
var rt = &cue.Runtime{}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
200
pkg/schema/load/dashboard.go
Normal file
200
pkg/schema/load/dashboard.go
Normal file
@ -0,0 +1,200 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
)
|
||||
|
||||
var panelSubpath cue.Path = cue.MakePath(cue.Def("#Panel"))
|
||||
|
||||
func defaultOverlay(p BaseLoadPaths) (map[string]load.Source, error) {
|
||||
overlay := make(map[string]load.Source)
|
||||
if err := toOverlay("/", p.BaseCueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := toOverlay("/", 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) {
|
||||
overlay, err := defaultOverlay(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &load.Config{Overlay: overlay}
|
||||
inst, err := rt.Build(load.Instances([]string{"/cue/data/gen.cue"}, cfg)[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
famval := inst.Value().LookupPath(cue.MakePath(cue.Str("Family")))
|
||||
if !famval.Exists() {
|
||||
return nil, errors.New("dashboard schema family did not exist at expected path in expected file")
|
||||
}
|
||||
|
||||
return buildGenericScuemata(famval)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
head, err := BaseDashboardFamily(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scuemap, err := readPanelModels(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dj, err := disjunctPanelScuemata(scuemap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stick this into a dummy struct so that we can unify it into place, as
|
||||
// Value.Fill() can't target definitions. Need new method based on cue.Path;
|
||||
// a CL has been merged that creates FillPath and will be in the next
|
||||
// release of CUE.
|
||||
dummy, _ := rt.Compile("mergeStruct", `
|
||||
obj: {}
|
||||
dummy: {
|
||||
#Panel: obj
|
||||
}
|
||||
`)
|
||||
filled := dummy.Value().Fill(dj, "obj")
|
||||
ddj := filled.LookupPath(cue.MakePath(cue.Str("dummy")))
|
||||
|
||||
var first, prev *compositeDashboardSchema
|
||||
for head != nil {
|
||||
cds := &compositeDashboardSchema{
|
||||
base: head,
|
||||
actual: head.CUE().Unify(ddj),
|
||||
panelFams: scuemap,
|
||||
// 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 {
|
||||
rv, err := rt.Compile("resource", r.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cds.actual.Unify(rv.Value()).Validate(cue.Concrete(true))
|
||||
}
|
||||
|
||||
// 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 (cds *compositeDashboardSchema) ApplyDefaults(_ schema.Resource) (schema.Resource, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// 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 (cds *compositeDashboardSchema) TrimDefaults(_ schema.Resource) (schema.Resource, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// 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())
|
||||
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)
|
||||
}
|
180
pkg/schema/load/generic.go
Normal file
180
pkg/schema/load/generic.go
Normal file
@ -0,0 +1,180 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"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.Instance, error) {
|
||||
overlay := make(map[string]load.Source)
|
||||
if err := toOverlay("/grafana", p.BaseCueFS, overlay); err != nil {
|
||||
return nil, 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: "/",
|
||||
}
|
||||
return rt.Build(load.Instances([]string{"/grafana/cue/scuemata"}, cfg)[0])
|
||||
}
|
||||
|
||||
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 {
|
||||
rv, err := rt.Compile("resource", r.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return gvs.actual.Unify(rv.Value()).Validate(cue.Concrete(true))
|
||||
}
|
||||
|
||||
// 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 (gvs *genericVersionedSchema) ApplyDefaults(_ schema.Resource) (schema.Resource, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// 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 (gvs *genericVersionedSchema) TrimDefaults(_ schema.Resource) (schema.Resource, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
// 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.Fill(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()
|
||||
}
|
||||
}
|
125
pkg/schema/load/load_test.go
Normal file
125
pkg/schema/load/load_test.go
Normal file
@ -0,0 +1,125 @@
|
||||
package load
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana"
|
||||
"github.com/grafana/grafana/pkg/schema"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var p BaseLoadPaths = BaseLoadPaths{
|
||||
BaseCueFS: grafana.CoreSchema,
|
||||
DistPluginCueFS: grafana.PluginSchema,
|
||||
}
|
||||
|
||||
// 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 TestDashboardValidity(t *testing.T) {
|
||||
// TODO FIXME remove this once we actually have dashboard schema filled in
|
||||
// enough that the tests pass, lol
|
||||
t.Skip()
|
||||
validdir := os.DirFS(filepath.Join("testdata", "artifacts", "dashboards"))
|
||||
|
||||
dash, err := BaseDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading base dashboard scuemata")
|
||||
|
||||
ddash, err := DistDashboardFamily(p)
|
||||
require.NoError(t, err, "error while loading dist dashboard scuemata")
|
||||
|
||||
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) {
|
||||
b, err := validdir.Open(path)
|
||||
require.NoError(t, err, "failed to open dashboard file")
|
||||
|
||||
t.Run("base", func(t *testing.T) {
|
||||
_, err := schema.SearchAndValidate(dash, b)
|
||||
require.NoError(t, err, "dashboard failed validation")
|
||||
})
|
||||
t.Run("dist", func(t *testing.T) {
|
||||
_, err := schema.SearchAndValidate(ddash, b)
|
||||
require.NoError(t, err, "dashboard failed validation")
|
||||
})
|
||||
})
|
||||
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
func TestPanelValidity(t *testing.T) {
|
||||
validdir := os.DirFS(filepath.Join("testdata", "artifacts", "panels"))
|
||||
|
||||
// dash, err := BaseDashboardFamily(p)
|
||||
// require.NoError(t, err, "error while loading base dashboard scuemata")
|
||||
|
||||
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
|
||||
t.Skip()
|
||||
|
||||
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
|
||||
}))
|
||||
}
|
161
pkg/schema/load/panel.go
Normal file
161
pkg/schema/load/panel.go
Normal file
@ -0,0 +1,161 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// Returns a disjunction of structs representing each panel schema version
|
||||
// (post-mapping from on-disk #PanelModel form) from each scuemata in the map.
|
||||
func disjunctPanelScuemata(scuemap map[string]schema.VersionedCueSchema) (cue.Value, error) {
|
||||
partsi, err := rt.Compile("panelDisjunction", `
|
||||
allPanels: [Name=_]: {}
|
||||
parts: or([for v in allPanels { v }])
|
||||
`)
|
||||
if err != nil {
|
||||
return cue.Value{}, err
|
||||
}
|
||||
|
||||
parts := partsi.Value()
|
||||
for id, sch := range scuemap {
|
||||
for sch != nil {
|
||||
cv := mapPanelModel(id, sch)
|
||||
|
||||
mjv, miv := sch.Version()
|
||||
parts = parts.Fill(cv, "allPanels", fmt.Sprintf("%s@%v.%v", id, mjv, miv))
|
||||
sch = sch.Successor()
|
||||
}
|
||||
}
|
||||
|
||||
return parts.LookupPath(cue.MakePath(cue.Str("parts"))), nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func mapPanelModel(id string, vcs schema.VersionedCueSchema) cue.Value {
|
||||
maj, min := vcs.Version()
|
||||
// Ignore err return, this can't fail to compile
|
||||
inter, _ := rt.Compile("typedPanel", 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: in.model.PanelFieldConfig
|
||||
}
|
||||
`, id, maj, min))
|
||||
|
||||
// TODO validate, especially with #PanelModel
|
||||
return inter.Value().Fill(vcs.CUE(), "in", "model").LookupPath(cue.MakePath(cue.Str(("result"))))
|
||||
}
|
||||
|
||||
func readPanelModels(p BaseLoadPaths) (map[string]schema.VersionedCueSchema, error) {
|
||||
overlay := make(map[string]load.Source)
|
||||
if err := toOverlay("/", p.BaseCueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := toOverlay("/", p.DistPluginCueFS, overlay); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base, err := getBaseScuemata(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pmf := base.Value().LookupPath(cue.MakePath(cue.Def("#PanelFamily")))
|
||||
if !pmf.Exists() {
|
||||
return nil, errors.New("could not locate #PanelFamily definition")
|
||||
}
|
||||
|
||||
all := make(map[string]schema.VersionedCueSchema)
|
||||
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, err := rt.Build(li[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the Family declaration in the models.cue file...
|
||||
pmod := imod.Value().LookupPath(cue.MakePath(cue.Str("Family")))
|
||||
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
|
||||
if err := pmf.Subsume(pmod); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a generic schema family to represent the whole of the
|
||||
fam, err := buildGenericScuemata(pmod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
all[id] = fam
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
3
pkg/schema/load/testdata/artifacts/README.md
vendored
Normal file
3
pkg/schema/load/testdata/artifacts/README.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
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.
|
153
pkg/schema/load/testdata/artifacts/dashboards/basic.json
vendored
Normal file
153
pkg/schema/load/testdata/artifacts/dashboards/basic.json
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
{
|
||||
"__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,
|
||||
"mappings": [],
|
||||
"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
|
||||
}
|
0
pkg/schema/load/testdata/artifacts/panels/.gitkeep
vendored
Normal file
0
pkg/schema/load/testdata/artifacts/panels/.gitkeep
vendored
Normal file
85
pkg/schema/load/testdata/artifacts/panels/basic-table.json
vendored
Normal file
85
pkg/schema/load/testdata/artifacts/panels/basic-table.json
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
{
|
||||
"datasource": "${DS_GDEV-TESTDATA}",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": "right",
|
||||
"filterable": false
|
||||
},
|
||||
"decimals": 3,
|
||||
"mappings": [],
|
||||
"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
|
||||
}
|
||||
}
|
90
pkg/schema/load/testdata/plugins/panel/with-lineage/models.cue
vendored
Normal file
90
pkg/schema/load/testdata/plugins/panel/with-lineage/models.cue
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
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
|
||||
}
|
||||
]
|
||||
}
|
9
pkg/schema/load/testdata/plugins/panel/with-lineage/plugin.json
vendored
Normal file
9
pkg/schema/load/testdata/plugins/panel/with-lineage/plugin.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Sample plugin with lineage",
|
||||
"id": "with-lineage",
|
||||
|
||||
"info": {
|
||||
"description": "Show how complex history may work"
|
||||
}
|
||||
}
|
274
pkg/schema/schema.go
Normal file
274
pkg/schema/schema.go
Normal file
@ -0,0 +1,274 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// 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.
|
||||
ApplyDefaults(Resource) (Resource, error)
|
||||
|
||||
// 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.
|
||||
TrimDefaults(Resource) (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.
|
||||
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}
|
||||
}
|
||||
}
|
||||
|
||||
// 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{}
|
||||
}
|
||||
|
||||
// TODO add migrator with SearchOption for stopping criteria
|
4
pkg/schema/schema_test.go
Normal file
4
pkg/schema/schema_test.go
Normal file
@ -0,0 +1,4 @@
|
||||
package schema
|
||||
|
||||
// TODO tests for this stuff! Everything in this package is totally generic,
|
||||
// nothing is specific to Grafana
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
|
||||
import { Select, Table } from '@grafana/ui';
|
||||
import { DataFrame, FieldMatcherID, getFrameDisplayName, PanelProps, SelectableValue } from '@grafana/data';
|
||||
import { Options } from './types';
|
||||
import { PanelOptions } from './models.gen';
|
||||
import { css } from '@emotion/css';
|
||||
import { config } from 'app/core/config';
|
||||
import { FilterItem, TableSortByFieldState } from '@grafana/ui/src/components/Table/types';
|
||||
@ -10,7 +10,7 @@ import { dispatch } from '../../../store/store';
|
||||
import { applyFilterFromTable } from '../../../features/variables/adhoc/actions';
|
||||
import { getDashboardSrv } from '../../../features/dashboard/services/DashboardSrv';
|
||||
|
||||
interface Props extends PanelProps<Options> {}
|
||||
interface Props extends PanelProps<PanelOptions> {}
|
||||
|
||||
export class TablePanel extends Component<Props> {
|
||||
constructor(props: Props) {
|
||||
|
@ -11,14 +11,14 @@ import omitBy from 'lodash/omitBy';
|
||||
import isNil from 'lodash/isNil';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import defaultTo from 'lodash/defaultTo';
|
||||
import { Options } from './types';
|
||||
import { PanelOptions } from './models.gen';
|
||||
|
||||
/**
|
||||
* At 7.0, the `table` panel was swapped from an angular implementation to a react one.
|
||||
* The models do not match, so this process will delegate to the old implementation when
|
||||
* a saved table configuration exists.
|
||||
*/
|
||||
export const tableMigrationHandler = (panel: PanelModel<Options>): Partial<Options> => {
|
||||
export const tableMigrationHandler = (panel: PanelModel<PanelOptions>): Partial<PanelOptions> => {
|
||||
// Table was saved as an angular table, lets just swap to the 'table-old' panel
|
||||
if (!panel.pluginVersion && (panel as any).columns) {
|
||||
console.log('Was angular table', panel);
|
||||
@ -74,7 +74,7 @@ const generateThresholds = (thresholds: string[], colors: string[]) => {
|
||||
};
|
||||
|
||||
const migrateTransformations = (
|
||||
panel: PanelModel<Partial<Options>> | any,
|
||||
panel: PanelModel<Partial<PanelOptions>> | any,
|
||||
oldOpts: { columns: any; transform: Transformations }
|
||||
) => {
|
||||
const transformations: Transformation[] = panel.transformations ?? [];
|
||||
@ -221,7 +221,7 @@ const migrateDefaults = (prevDefaults: Style) => {
|
||||
* This is called when the panel changes from another panel
|
||||
*/
|
||||
export const tablePanelChangedHandler = (
|
||||
panel: PanelModel<Partial<Options>> | any,
|
||||
panel: PanelModel<Partial<PanelOptions>> | any,
|
||||
prevPluginId: string,
|
||||
prevOptions: any
|
||||
) => {
|
||||
|
26
public/app/plugins/panel/table/models.cue
Normal file
26
public/app/plugins/panel/table/models.cue
Normal file
@ -0,0 +1,26 @@
|
||||
package grafanaschema
|
||||
|
||||
import (
|
||||
ui "github.com/grafana/grafana/cue/ui:grafanaschema"
|
||||
)
|
||||
|
||||
Family: {
|
||||
lineages: [
|
||||
[
|
||||
{
|
||||
PanelOptions: {
|
||||
frameIndex: number | *0
|
||||
showHeader: bool | *true
|
||||
sortBy?: [...ui.TableSortByFieldState]
|
||||
}
|
||||
PanelFieldConfig: {
|
||||
width?: int
|
||||
align?: *null | string
|
||||
displayMode?: string | *"auto" // TODO? TableCellDisplayMode
|
||||
filterable?: bool
|
||||
}
|
||||
},
|
||||
]
|
||||
]
|
||||
migrations: []
|
||||
}
|
34
public/app/plugins/panel/table/models.gen.ts
Normal file
34
public/app/plugins/panel/table/models.gen.ts
Normal file
@ -0,0 +1,34 @@
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// NOTE: This file will be auto generated from models.cue
|
||||
// It is currenty hand written but will serve as the target for cuetsy
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
import { TableCellDisplayMode, TableSortByFieldState } from '@grafana/ui';
|
||||
|
||||
// Only the latest schema version is translated to TypeScript, on the premise
|
||||
// that either the dashboard loading process, or (eventually) CUE-defined
|
||||
// migrations ensure that bulk of the frontend application only ever
|
||||
// need directly consider the most recent version of the schema.
|
||||
export const modelVersion = Object.freeze([1, 0]);
|
||||
|
||||
export interface PanelOptions {
|
||||
frameIndex: number;
|
||||
showHeader: boolean;
|
||||
sortBy?: TableSortByFieldState[];
|
||||
}
|
||||
|
||||
export const defaultPanelOptions: PanelOptions = {
|
||||
frameIndex: 0,
|
||||
showHeader: true,
|
||||
};
|
||||
|
||||
export interface PanelFieldConfig {
|
||||
width?: number;
|
||||
align?: string;
|
||||
displayMode?: TableCellDisplayMode;
|
||||
filterable?: boolean;
|
||||
}
|
||||
|
||||
export const defaultPanelFieldConfig: PanelFieldConfig = {
|
||||
displayMode: TableCellDisplayMode.Auto,
|
||||
};
|
@ -1,10 +1,10 @@
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { TablePanel } from './TablePanel';
|
||||
import { CustomFieldConfig, Options } from './types';
|
||||
import { PanelOptions, PanelFieldConfig, defaultPanelOptions, defaultPanelFieldConfig } from './models.gen';
|
||||
import { tableMigrationHandler, tablePanelChangedHandler } from './migrations';
|
||||
import { TableCellDisplayMode } from '@grafana/ui';
|
||||
|
||||
export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
||||
export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(TablePanel)
|
||||
.setPanelChangeHandler(tablePanelChangedHandler)
|
||||
.setMigrationHandler(tableMigrationHandler)
|
||||
.setNoPadding()
|
||||
@ -20,6 +20,7 @@ export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
||||
max: 300,
|
||||
},
|
||||
shouldApply: () => true,
|
||||
defaultValue: defaultPanelFieldConfig.width,
|
||||
})
|
||||
.addRadio({
|
||||
path: 'align',
|
||||
@ -32,7 +33,7 @@ export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
||||
{ label: 'right', value: 'right' },
|
||||
],
|
||||
},
|
||||
defaultValue: null,
|
||||
defaultValue: defaultPanelFieldConfig.align,
|
||||
})
|
||||
.addSelect({
|
||||
path: 'displayMode',
|
||||
@ -51,12 +52,13 @@ export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
||||
{ value: TableCellDisplayMode.Image, label: 'Image' },
|
||||
],
|
||||
},
|
||||
defaultValue: defaultPanelFieldConfig.displayMode,
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
path: 'filterable',
|
||||
name: 'Column filter',
|
||||
description: 'Enables/disables field filters in table',
|
||||
defaultValue: false,
|
||||
defaultValue: defaultPanelFieldConfig.filterable,
|
||||
});
|
||||
},
|
||||
})
|
||||
@ -65,6 +67,6 @@ export const plugin = new PanelPlugin<Options, CustomFieldConfig>(TablePanel)
|
||||
path: 'showHeader',
|
||||
name: 'Show header',
|
||||
description: "To display table's header or not to display",
|
||||
defaultValue: true,
|
||||
defaultValue: defaultPanelOptions.showHeader,
|
||||
});
|
||||
});
|
||||
|
@ -3,6 +3,11 @@
|
||||
"name": "Table",
|
||||
"id": "table",
|
||||
|
||||
"models": {
|
||||
"version": [1, 0],
|
||||
"changed": "2021/03/30"
|
||||
},
|
||||
|
||||
"info": {
|
||||
"description": "Table Panel for Grafana",
|
||||
"author": {
|
||||
|
@ -1,17 +0,0 @@
|
||||
import { TableSortByFieldState } from '@grafana/ui';
|
||||
|
||||
export interface Options {
|
||||
frameIndex: number;
|
||||
showHeader: boolean;
|
||||
sortBy?: TableSortByFieldState[];
|
||||
}
|
||||
|
||||
export interface TableSortBy {
|
||||
displayName: string;
|
||||
desc: boolean;
|
||||
}
|
||||
|
||||
export interface CustomFieldConfig {
|
||||
width: number;
|
||||
displayMode: string;
|
||||
}
|
93
scripts/cuegen.sh
Executable file
93
scripts/cuegen.sh
Executable file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
# MUST BE RUN FROM GRAFANA ROOT DIR
|
||||
test -d cue
|
||||
|
||||
# Must have latest cue and cuetsy
|
||||
if ! command -v cue &> /dev/null; then
|
||||
echo "must install cue on PATH"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v cuetsy &> /dev/null; then
|
||||
echo "must install cuetsy on PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TODO Everything here needs to be moved into custom CUE logic in a Go program.
|
||||
# It _might_ be possible to do what we want with some CUE tools magic
|
||||
# (https://pkg.go.dev/cuelang.org/go@v0.3.0-beta.5/pkg/tool), but unless that
|
||||
# turns out to be pretty straightforward, it's probably better to encode our
|
||||
# filesystem semantics there.
|
||||
|
||||
# Enumerate and move all CUE files under packages/grafana-{data,ui} into
|
||||
# respective cue subdir. These subdirs are where we place assembled
|
||||
# definitions, where Go loads from, and where other CUE - including CUE defined
|
||||
# in plugins - import from.
|
||||
mkdir -p cue/ui cue/data
|
||||
rm -f {cue/ui/gen.cue,cue/data/gen.cue}
|
||||
|
||||
# TODO decide if multiple or single files seems like better ergonomics
|
||||
# shellcheck disable=SC2046
|
||||
cue def -s $(find packages/grafana-ui -type f -name "*.cue") > cue/ui/gen.cue
|
||||
# shellcheck disable=SC2046
|
||||
cue def -s $(find packages/grafana-data -type f -name "*.cue") > cue/data/gen.cue
|
||||
|
||||
# Horrible hack to remove import statements.
|
||||
#
|
||||
# HACK-IMPOSED CONSTRAINT: Only works for single-line imports, so we can ONLY use
|
||||
# single-line imports in CUE files until this is improved! Expressly only here
|
||||
# as a hack because we can't make this better with vanilla cue.
|
||||
#
|
||||
# HACK-IMPOSED CONSTRAINT: Can't import between @grafana/ui and @grafana/data,
|
||||
# because those imports will also be removed
|
||||
#
|
||||
# TODO move a more careful import-elimination check into a Go tool
|
||||
#
|
||||
# It's important to understand why this is necessary, though. We are expecting
|
||||
# that these core components may depend on each other - e.g., how
|
||||
# GraphTooltipOptions composes in TooltipMode. We have to preserve those
|
||||
# literal identifiers in our assembled CUE, so that when a panel plugin's
|
||||
# models.cue imports and references something like GraphTooltipOptions in CUE,
|
||||
# it's still the same identifier as appeared in the original core models.cue
|
||||
# files, AND therefore is exactly the identifier that appears in
|
||||
# cuetsy-generated @grafana/{ui,data} packages. That is, as long as we preserve
|
||||
# the relation between the identifier "GraphTooltipOptions" as a top-level
|
||||
# importable thing at all stages on the CUE side, then everything on the
|
||||
# TypeScript side will line up.
|
||||
sed -i -e 's/^import.*//g' {cue/ui/gen.cue,cue/data/gen.cue}
|
||||
|
||||
# Remove all qualified identifiers
|
||||
# (https://cuelang.org/docs/references/spec/#qualified-identifiers) from the
|
||||
# generated CUE files.
|
||||
#
|
||||
# Even worse hack than the above, but part and parcel with having imports. By
|
||||
# assembling the CUE inputs together into a single dir in a single package (and
|
||||
# even in a single file, though single dir is sufficient), we've obviated the
|
||||
# need for imports and qualified identifiers; CUE's loader logic concats
|
||||
# everything into a single instance.
|
||||
#
|
||||
# HACK-IMPOSED CONSTRAINT: No selectors (foo.bar,
|
||||
# https://cuelang.org/docs/references/spec/#qualified-identifiers), at all.
|
||||
# Thus, no nested identifiers. This is a horrible sledgehammer. It makes it
|
||||
# impossible to correctly consume a CUE file that references a nested
|
||||
# identifier (foo.bar), because this stupid logic can't disambiguate between
|
||||
# those and referencing a label from an import.
|
||||
#
|
||||
# HACK-IMPOSED CONSTRAINT: We can't experiment with the sort of complex
|
||||
# structures necessary for revisioning as long as we're doing this, as they're
|
||||
# necessarily going to involve some nesting.
|
||||
#
|
||||
# TODO move into grafana-cli and do a more careful check that we're only
|
||||
# eliminating qualified identifiers from imports we're also eliminating
|
||||
sed -i -e "s/[A-Za-z]*\.\([A-Za-z]*\)/\1/g" {cue/ui/gen.cue,cue/data/gen.cue}
|
||||
|
||||
# uuuugghhhh OSX sed
|
||||
rm -f {cue/ui/gen.cue-e,cue/data/gen.cue-e}
|
||||
|
||||
# Check that our output is still valid CUE.
|
||||
cue eval -E {cue/ui/gen.cue,cue/data/gen.cue} > /dev/null
|
||||
|
||||
# Run cuetsy over all core .cue files.
|
||||
find packages -type f -name '*.cue' -exec cuetsy {} \;
|
||||
find public/app/plugins -type f -name '*.cue' -exec cuetsy {} \;
|
@ -526,18 +526,6 @@ def shellcheck_step():
|
||||
],
|
||||
}
|
||||
|
||||
def dashboard_schemas_check():
|
||||
return {
|
||||
'name': 'check-dashboard-schemas',
|
||||
'image': build_image,
|
||||
'depends_on': [
|
||||
'initialize',
|
||||
],
|
||||
'commands': [
|
||||
'cue export --out openapi -o - ./dashboard-schemas/...',
|
||||
],
|
||||
}
|
||||
|
||||
def gen_version_step(ver_mode, include_enterprise2=False, is_downstream=False):
|
||||
deps = [
|
||||
'build-backend',
|
||||
@ -547,7 +535,6 @@ def gen_version_step(ver_mode, include_enterprise2=False, is_downstream=False):
|
||||
'test-frontend',
|
||||
'codespell',
|
||||
'shellcheck',
|
||||
'check-dashboard-schemas',
|
||||
]
|
||||
if include_enterprise2:
|
||||
sfx = '-enterprise2'
|
||||
|
@ -4,7 +4,6 @@ load(
|
||||
'lint_backend_step',
|
||||
'codespell_step',
|
||||
'shellcheck_step',
|
||||
'dashboard_schemas_check',
|
||||
'test_backend_step',
|
||||
'test_frontend_step',
|
||||
'build_backend_step',
|
||||
@ -47,7 +46,6 @@ def get_steps(edition, is_downstream=False):
|
||||
lint_backend_step(edition=edition),
|
||||
codespell_step(),
|
||||
shellcheck_step(),
|
||||
dashboard_schemas_check(),
|
||||
test_backend_step(edition=edition),
|
||||
test_frontend_step(),
|
||||
frontend_metrics_step(edition=edition),
|
||||
|
@ -4,7 +4,6 @@ load(
|
||||
'lint_backend_step',
|
||||
'codespell_step',
|
||||
'shellcheck_step',
|
||||
'dashboard_schemas_check',
|
||||
'test_backend_step',
|
||||
'test_frontend_step',
|
||||
'build_backend_step',
|
||||
@ -38,7 +37,6 @@ def pr_pipelines(edition):
|
||||
lint_backend_step(edition=edition),
|
||||
codespell_step(),
|
||||
shellcheck_step(),
|
||||
dashboard_schemas_check(),
|
||||
test_backend_step(edition=edition),
|
||||
test_frontend_step(),
|
||||
build_backend_step(edition=edition, ver_mode=ver_mode, variants=variants),
|
||||
|
@ -7,7 +7,6 @@ load(
|
||||
'lint_backend_step',
|
||||
'codespell_step',
|
||||
'shellcheck_step',
|
||||
'dashboard_schemas_check',
|
||||
'test_backend_step',
|
||||
'test_frontend_step',
|
||||
'build_backend_step',
|
||||
@ -72,7 +71,6 @@ def get_steps(edition, ver_mode):
|
||||
lint_backend_step(edition=edition),
|
||||
codespell_step(),
|
||||
shellcheck_step(),
|
||||
dashboard_schemas_check(),
|
||||
test_backend_step(edition=edition),
|
||||
test_frontend_step(),
|
||||
build_backend_step(edition=edition, ver_mode=ver_mode),
|
||||
|
Loading…
Reference in New Issue
Block a user