From bba4d9bd7f85ea44e8bc1188f77a8472e1c274b0 Mon Sep 17 00:00:00 2001 From: sam boyer Date: Thu, 8 Apr 2021 04:11:11 -0400 Subject: [PATCH] Introduce "scuemata" system for CUE-based specification of Grafana objects (#32527) --- .drone.yml | 64 ---- cue.mod/module.cue | 1 + cue/data/gen.cue | 213 ++++++++++++++ cue/scuemata/panel-plugin.cue | 21 ++ cue/scuemata/scuemata.cue | 60 ++++ cue/ui/gen.cue | 93 ++++++ dashboard-schemas/Dashboard.cue | 68 ----- dashboard-schemas/README.md | 86 ------ dashboard-schemas/cue.mod/module.cue | 1 - dashboard-schemas/go.mod | 5 - dashboard-schemas/go.sum | 185 ------------ dashboard-schemas/main.go | 58 ---- dashboard-schemas/main_test.go | 47 --- dashboard-schemas/panels/Gauge.cue | 74 ----- dashboard-schemas/panels/Graph.cue | 192 ------------ dashboard-schemas/panels/Row.cue | 24 -- dashboard-schemas/panels/gridPos.cue | 12 - dashboard-schemas/panels/link.cue | 14 - dashboard-schemas/panels/mapping.cue | 11 - dashboard-schemas/panels/override.cue | 12 - dashboard-schemas/panels/panel.cue | 24 -- dashboard-schemas/panels/thresholds.cue | 11 - dashboard-schemas/targets/Prometheus.cue | 19 -- .../transformations/CalculateField.cue | 39 --- .../transformations/Organize.cue | 16 - dashboard-schemas/variables/Custom.cue | 9 - dashboard-schemas/variables/Datasource.cue | 18 -- dashboard-schemas/variables/Query.cue | 30 -- dashboard-schemas/variables/variable.cue | 33 --- embed.go | 21 ++ go.mod | 4 +- go.sum | 25 +- packages/grafana-data/src/types/dashboard.cue | 213 ++++++++++++++ .../components/BarChart/BarChart.story.tsx | 2 +- .../src/components/BarChart/BarChart.tsx | 2 +- .../src/components/BarChart/types.ts | 2 +- .../src/components/BarChart/utils.test.ts | 2 +- .../src/components/Chart/Tooltip.tsx | 3 +- .../src/components/Chart/models.cue | 3 + .../src/components/Chart/models.gen.ts | 6 + .../src/components/Graph/Graph.story.tsx | 3 +- .../components/Graph/GraphTooltip/models.cue | 11 + .../Graph/GraphTooltip/models.gen.ts | 10 + .../components/Graph/GraphTooltip/types.ts | 3 +- .../Graph/GraphWithLegend.story.tsx | 2 +- .../src/components/Graph/GraphWithLegend.tsx | 3 +- .../src/components/GraphNG/GraphNG.story.tsx | 2 +- .../src/components/GraphNG/GraphNG.tsx | 2 +- .../src/components/PieChart/PieChart.tsx | 2 +- .../src/components/Table/models.cue | 28 ++ .../src/components/Timeline/TimelineChart.tsx | 2 +- .../src/components/Timeline/types.ts | 2 +- .../components/VizLegend/VizLegend.story.tsx | 3 +- .../src/components/VizLegend/VizLegend.tsx | 3 +- .../src/components/VizLegend/models.cue | 12 + .../src/components/VizLegend/models.gen.ts | 16 + .../src/components/VizLegend/types.ts | 15 +- packages/grafana-ui/src/components/index.ts | 3 +- .../src/components/uPlot/PlotLegend.tsx | 3 +- .../src/components/uPlot/models.cue | 60 ++++ .../src/components/uPlot/models.gen.ts | 83 ++++++ .../uPlot/plugins/TooltipPlugin.tsx | 2 +- pkg/schema/load/common.go | 87 ++++++ pkg/schema/load/dashboard.go | 200 +++++++++++++ pkg/schema/load/generic.go | 180 ++++++++++++ pkg/schema/load/load_test.go | 125 ++++++++ pkg/schema/load/panel.go | 161 ++++++++++ pkg/schema/load/testdata/artifacts/README.md | 3 + .../testdata/artifacts/dashboards/basic.json | 153 ++++++++++ .../load/testdata/artifacts/panels/.gitkeep | 0 .../artifacts/panels/basic-table.json | 85 ++++++ .../plugins/panel/with-lineage/models.cue | 90 ++++++ .../plugins/panel/with-lineage/plugin.json | 9 + pkg/schema/schema.go | 274 ++++++++++++++++++ pkg/schema/schema_test.go | 4 + public/app/plugins/panel/table/TablePanel.tsx | 4 +- public/app/plugins/panel/table/migrations.ts | 8 +- public/app/plugins/panel/table/models.cue | 26 ++ public/app/plugins/panel/table/models.gen.ts | 34 +++ public/app/plugins/panel/table/module.tsx | 12 +- public/app/plugins/panel/table/plugin.json | 5 + public/app/plugins/panel/table/types.ts | 17 -- scripts/cuegen.sh | 93 ++++++ scripts/lib.star | 13 - scripts/master.star | 2 - scripts/pr.star | 2 - scripts/release.star | 2 - 87 files changed, 2446 insertions(+), 1136 deletions(-) create mode 100644 cue.mod/module.cue create mode 100644 cue/data/gen.cue create mode 100644 cue/scuemata/panel-plugin.cue create mode 100644 cue/scuemata/scuemata.cue create mode 100644 cue/ui/gen.cue delete mode 100644 dashboard-schemas/Dashboard.cue delete mode 100644 dashboard-schemas/README.md delete mode 100644 dashboard-schemas/cue.mod/module.cue delete mode 100644 dashboard-schemas/go.mod delete mode 100644 dashboard-schemas/go.sum delete mode 100644 dashboard-schemas/main.go delete mode 100644 dashboard-schemas/main_test.go delete mode 100644 dashboard-schemas/panels/Gauge.cue delete mode 100644 dashboard-schemas/panels/Graph.cue delete mode 100644 dashboard-schemas/panels/Row.cue delete mode 100644 dashboard-schemas/panels/gridPos.cue delete mode 100644 dashboard-schemas/panels/link.cue delete mode 100644 dashboard-schemas/panels/mapping.cue delete mode 100644 dashboard-schemas/panels/override.cue delete mode 100644 dashboard-schemas/panels/panel.cue delete mode 100644 dashboard-schemas/panels/thresholds.cue delete mode 100644 dashboard-schemas/targets/Prometheus.cue delete mode 100644 dashboard-schemas/transformations/CalculateField.cue delete mode 100644 dashboard-schemas/transformations/Organize.cue delete mode 100644 dashboard-schemas/variables/Custom.cue delete mode 100644 dashboard-schemas/variables/Datasource.cue delete mode 100644 dashboard-schemas/variables/Query.cue delete mode 100644 dashboard-schemas/variables/variable.cue create mode 100644 embed.go create mode 100644 packages/grafana-data/src/types/dashboard.cue create mode 100644 packages/grafana-ui/src/components/Chart/models.cue create mode 100644 packages/grafana-ui/src/components/Chart/models.gen.ts create mode 100644 packages/grafana-ui/src/components/Graph/GraphTooltip/models.cue create mode 100644 packages/grafana-ui/src/components/Graph/GraphTooltip/models.gen.ts create mode 100644 packages/grafana-ui/src/components/Table/models.cue create mode 100644 packages/grafana-ui/src/components/VizLegend/models.cue create mode 100644 packages/grafana-ui/src/components/VizLegend/models.gen.ts create mode 100644 packages/grafana-ui/src/components/uPlot/models.cue create mode 100644 packages/grafana-ui/src/components/uPlot/models.gen.ts create mode 100644 pkg/schema/load/common.go create mode 100644 pkg/schema/load/dashboard.go create mode 100644 pkg/schema/load/generic.go create mode 100644 pkg/schema/load/load_test.go create mode 100644 pkg/schema/load/panel.go create mode 100644 pkg/schema/load/testdata/artifacts/README.md create mode 100644 pkg/schema/load/testdata/artifacts/dashboards/basic.json create mode 100644 pkg/schema/load/testdata/artifacts/panels/.gitkeep create mode 100644 pkg/schema/load/testdata/artifacts/panels/basic-table.json create mode 100644 pkg/schema/load/testdata/plugins/panel/with-lineage/models.cue create mode 100644 pkg/schema/load/testdata/plugins/panel/with-lineage/plugin.json create mode 100644 pkg/schema/schema.go create mode 100644 pkg/schema/schema_test.go create mode 100644 public/app/plugins/panel/table/models.cue create mode 100644 public/app/plugins/panel/table/models.gen.ts delete mode 100644 public/app/plugins/panel/table/types.ts create mode 100755 scripts/cuegen.sh diff --git a/.drone.yml b/.drone.yml index 040f8474991..7f89d438528 100644 --- a/.drone.yml +++ b/.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 diff --git a/cue.mod/module.cue b/cue.mod/module.cue new file mode 100644 index 00000000000..fea0567adbe --- /dev/null +++ b/cue.mod/module.cue @@ -0,0 +1 @@ +module: "github.com/grafana/grafana" diff --git a/cue/data/gen.cue b/cue/data/gen.cue new file mode 100644 index 00000000000..3976a05d08a --- /dev/null +++ b/cue/data/gen.cue @@ -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 +} diff --git a/cue/scuemata/panel-plugin.cue b/cue/scuemata/panel-plugin.cue new file mode 100644 index 00000000000..98d010b44dd --- /dev/null +++ b/cue/scuemata/panel-plugin.cue @@ -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] +} diff --git a/cue/scuemata/scuemata.cue b/cue/scuemata/scuemata.cue new file mode 100644 index 00000000000..6e76548f638 --- /dev/null +++ b/cue/scuemata/scuemata.cue @@ -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 +} diff --git a/cue/ui/gen.cue b/cue/ui/gen.cue new file mode 100644 index 00000000000..13525012ee7 --- /dev/null +++ b/cue/ui/gen.cue @@ -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") diff --git a/dashboard-schemas/Dashboard.cue b/dashboard-schemas/Dashboard.cue deleted file mode 100644 index 76448a2b729..00000000000 --- a/dashboard-schemas/Dashboard.cue +++ /dev/null @@ -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?: [...{}] -} diff --git a/dashboard-schemas/README.md b/dashboard-schemas/README.md deleted file mode 100644 index 721cb184ad4..00000000000 --- a/dashboard-schemas/README.md +++ /dev/null @@ -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 . ... -``` diff --git a/dashboard-schemas/cue.mod/module.cue b/dashboard-schemas/cue.mod/module.cue deleted file mode 100644 index 14eaf4ef3d8..00000000000 --- a/dashboard-schemas/cue.mod/module.cue +++ /dev/null @@ -1 +0,0 @@ -module: "github.com/grafana/grafana/dashboard-schemas" diff --git a/dashboard-schemas/go.mod b/dashboard-schemas/go.mod deleted file mode 100644 index 20df7dc42fc..00000000000 --- a/dashboard-schemas/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/grafana/grafana/dashboard-schemas - -go 1.15 - -require cuelang.org/go v0.2.2 diff --git a/dashboard-schemas/go.sum b/dashboard-schemas/go.sum deleted file mode 100644 index 1201e093db2..00000000000 --- a/dashboard-schemas/go.sum +++ /dev/null @@ -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= diff --git a/dashboard-schemas/main.go b/dashboard-schemas/main.go deleted file mode 100644 index 5450c274607..00000000000 --- a/dashboard-schemas/main.go +++ /dev/null @@ -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 -} diff --git a/dashboard-schemas/main_test.go b/dashboard-schemas/main_test.go deleted file mode 100644 index 5b0c29f0bca..00000000000 --- a/dashboard-schemas/main_test.go +++ /dev/null @@ -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) - } - } -} diff --git a/dashboard-schemas/panels/Gauge.cue b/dashboard-schemas/panels/Gauge.cue deleted file mode 100644 index a978b5fe836..00000000000 --- a/dashboard-schemas/panels/Gauge.cue +++ /dev/null @@ -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" -} diff --git a/dashboard-schemas/panels/Graph.cue b/dashboard-schemas/panels/Graph.cue deleted file mode 100644 index ebee4a88eb2..00000000000 --- a/dashboard-schemas/panels/Graph.cue +++ /dev/null @@ -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 - } -} diff --git a/dashboard-schemas/panels/Row.cue b/dashboard-schemas/panels/Row.cue deleted file mode 100644 index 67cca4d2723..00000000000 --- a/dashboard-schemas/panels/Row.cue +++ /dev/null @@ -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" -} diff --git a/dashboard-schemas/panels/gridPos.cue b/dashboard-schemas/panels/gridPos.cue deleted file mode 100644 index d4935043d00..00000000000 --- a/dashboard-schemas/panels/gridPos.cue +++ /dev/null @@ -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 -} diff --git a/dashboard-schemas/panels/link.cue b/dashboard-schemas/panels/link.cue deleted file mode 100644 index 50e702be9a6..00000000000 --- a/dashboard-schemas/panels/link.cue +++ /dev/null @@ -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 diff --git a/dashboard-schemas/panels/mapping.cue b/dashboard-schemas/panels/mapping.cue deleted file mode 100644 index 72a992ead7a..00000000000 --- a/dashboard-schemas/panels/mapping.cue +++ /dev/null @@ -1,11 +0,0 @@ -package panels - -_mapping: { - id: int - from: string - operator: string - to: string - text: string - type: int - value: string -} diff --git a/dashboard-schemas/panels/override.cue b/dashboard-schemas/panels/override.cue deleted file mode 100644 index 9957a4edb4b..00000000000 --- a/dashboard-schemas/panels/override.cue +++ /dev/null @@ -1,12 +0,0 @@ -package panels - -_override: { - matcher: { - id: string - options: string - } - properties: [...{ - id: string - value: int - }] -} diff --git a/dashboard-schemas/panels/panel.cue b/dashboard-schemas/panels/panel.cue deleted file mode 100644 index bdfa74af5c1..00000000000 --- a/dashboard-schemas/panels/panel.cue +++ /dev/null @@ -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?: [...{}] -} diff --git a/dashboard-schemas/panels/thresholds.cue b/dashboard-schemas/panels/thresholds.cue deleted file mode 100644 index 901729aa9d3..00000000000 --- a/dashboard-schemas/panels/thresholds.cue +++ /dev/null @@ -1,11 +0,0 @@ -package panels - -_thresholds: { - // Threshold mode. - mode: string | *"absolute" - // Threshold steps. - steps: [...{ - color: string - value: number - }] -} diff --git a/dashboard-schemas/targets/Prometheus.cue b/dashboard-schemas/targets/Prometheus.cue deleted file mode 100644 index 93052139d5c..00000000000 --- a/dashboard-schemas/targets/Prometheus.cue +++ /dev/null @@ -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" -} diff --git a/dashboard-schemas/transformations/CalculateField.cue b/dashboard-schemas/transformations/CalculateField.cue deleted file mode 100644 index 79fcd457acd..00000000000 --- a/dashboard-schemas/transformations/CalculateField.cue +++ /dev/null @@ -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 - } -} diff --git a/dashboard-schemas/transformations/Organize.cue b/dashboard-schemas/transformations/Organize.cue deleted file mode 100644 index fb2387508fa..00000000000 --- a/dashboard-schemas/transformations/Organize.cue +++ /dev/null @@ -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: {} - } -} diff --git a/dashboard-schemas/variables/Custom.cue b/dashboard-schemas/variables/Custom.cue deleted file mode 100644 index f375a50577d..00000000000 --- a/dashboard-schemas/variables/Custom.cue +++ /dev/null @@ -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" -} diff --git a/dashboard-schemas/variables/Datasource.cue b/dashboard-schemas/variables/Datasource.cue deleted file mode 100644 index 870baef3e7d..00000000000 --- a/dashboard-schemas/variables/Datasource.cue +++ /dev/null @@ -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" -} diff --git a/dashboard-schemas/variables/Query.cue b/dashboard-schemas/variables/Query.cue deleted file mode 100644 index 1757ed13987..00000000000 --- a/dashboard-schemas/variables/Query.cue +++ /dev/null @@ -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 -} diff --git a/dashboard-schemas/variables/variable.cue b/dashboard-schemas/variables/variable.cue deleted file mode 100644 index 54755c6a724..00000000000 --- a/dashboard-schemas/variables/variable.cue +++ /dev/null @@ -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 -} diff --git a/embed.go b/embed.go new file mode 100644 index 00000000000..0f4ba0970a8 --- /dev/null +++ b/embed.go @@ -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") diff --git a/go.mod b/go.mod index d8a19a699b5..733f1495ba5 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 78ff30940e8..aeb509dbd5e 100644 --- a/go.sum +++ b/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= diff --git a/packages/grafana-data/src/types/dashboard.cue b/packages/grafana-data/src/types/dashboard.cue new file mode 100644 index 00000000000..a5b07f97aad --- /dev/null +++ b/packages/grafana-data/src/types/dashboard.cue @@ -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 +} diff --git a/packages/grafana-ui/src/components/BarChart/BarChart.story.tsx b/packages/grafana-ui/src/components/BarChart/BarChart.story.tsx index 63aeaeeb36d..b50f46b8206 100644 --- a/packages/grafana-ui/src/components/BarChart/BarChart.story.tsx +++ b/packages/grafana-ui/src/components/BarChart/BarChart.story.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/BarChart/BarChart.tsx b/packages/grafana-ui/src/components/BarChart/BarChart.tsx index 15e820be7f4..50a8ba0d4a4 100644 --- a/packages/grafana-ui/src/components/BarChart/BarChart.tsx +++ b/packages/grafana-ui/src/components/BarChart/BarChart.tsx @@ -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'; /** diff --git a/packages/grafana-ui/src/components/BarChart/types.ts b/packages/grafana-ui/src/components/BarChart/types.ts index 208a9d22b67..2e1b3c33fab 100644 --- a/packages/grafana-ui/src/components/BarChart/types.ts +++ b/packages/grafana-ui/src/components/BarChart/types.ts @@ -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 diff --git a/packages/grafana-ui/src/components/BarChart/utils.test.ts b/packages/grafana-ui/src/components/BarChart/utils.test.ts index 2a3389d4016..1b905290955 100644 --- a/packages/grafana-ui/src/components/BarChart/utils.test.ts +++ b/packages/grafana-ui/src/components/BarChart/utils.test.ts @@ -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({ diff --git a/packages/grafana-ui/src/components/Chart/Tooltip.tsx b/packages/grafana-ui/src/components/Chart/Tooltip.tsx index 3bce9f4809b..7a7849ac809 100644 --- a/packages/grafana-ui/src/components/Chart/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Chart/Tooltip.tsx @@ -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: diff --git a/packages/grafana-ui/src/components/Chart/models.cue b/packages/grafana-ui/src/components/Chart/models.cue new file mode 100644 index 00000000000..fc69fa9240c --- /dev/null +++ b/packages/grafana-ui/src/components/Chart/models.cue @@ -0,0 +1,3 @@ +package grafanaschema + +TooltipMode: "single" | "multi" | "none" @cuetsy(targetType="type") \ No newline at end of file diff --git a/packages/grafana-ui/src/components/Chart/models.gen.ts b/packages/grafana-ui/src/components/Chart/models.gen.ts new file mode 100644 index 00000000000..dd3ad2757b3 --- /dev/null +++ b/packages/grafana-ui/src/components/Chart/models.gen.ts @@ -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'; diff --git a/packages/grafana-ui/src/components/Graph/Graph.story.tsx b/packages/grafana-ui/src/components/Graph/Graph.story.tsx index 01de29a8176..1ab2ace3b29 100644 --- a/packages/grafana-ui/src/components/Graph/Graph.story.tsx +++ b/packages/grafana-ui/src/components/Graph/Graph.story.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/Graph/GraphTooltip/models.cue b/packages/grafana-ui/src/components/Graph/GraphTooltip/models.cue new file mode 100644 index 00000000000..041ce50aaad --- /dev/null +++ b/packages/grafana-ui/src/components/Graph/GraphTooltip/models.cue @@ -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") \ No newline at end of file diff --git a/packages/grafana-ui/src/components/Graph/GraphTooltip/models.gen.ts b/packages/grafana-ui/src/components/Graph/GraphTooltip/models.gen.ts new file mode 100644 index 00000000000..9ffd6bea3f4 --- /dev/null +++ b/packages/grafana-ui/src/components/Graph/GraphTooltip/models.gen.ts @@ -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; +} diff --git a/packages/grafana-ui/src/components/Graph/GraphTooltip/types.ts b/packages/grafana-ui/src/components/Graph/GraphTooltip/types.ts index 7fea3fa7293..304f75433a7 100644 --- a/packages/grafana-ui/src/components/Graph/GraphTooltip/types.ts +++ b/packages/grafana-ui/src/components/Graph/GraphTooltip/types.ts @@ -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; diff --git a/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx b/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx index a85d35d6964..792e929568c 100644 --- a/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx +++ b/packages/grafana-ui/src/components/Graph/GraphWithLegend.story.tsx @@ -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 { diff --git a/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx b/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx index a43ca71623f..9af00013906 100644 --- a/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx +++ b/packages/grafana-ui/src/components/Graph/GraphWithLegend.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx index 7c2efa3ab6a..e1eb1ae2d71 100644 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.story.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx index fd345b281ae..55476d5cbe1 100755 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx @@ -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'; /** diff --git a/packages/grafana-ui/src/components/PieChart/PieChart.tsx b/packages/grafana-ui/src/components/PieChart/PieChart.tsx index e1c63e5e3b4..71d34d88225 100644 --- a/packages/grafana-ui/src/components/PieChart/PieChart.tsx +++ b/packages/grafana-ui/src/components/PieChart/PieChart.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/Table/models.cue b/packages/grafana-ui/src/components/Table/models.cue new file mode 100644 index 00000000000..096c2873bf3 --- /dev/null +++ b/packages/grafana-ui/src/components/Table/models.cue @@ -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") \ No newline at end of file diff --git a/packages/grafana-ui/src/components/Timeline/TimelineChart.tsx b/packages/grafana-ui/src/components/Timeline/TimelineChart.tsx index 1995b1363f1..9c81aef132c 100755 --- a/packages/grafana-ui/src/components/Timeline/TimelineChart.tsx +++ b/packages/grafana-ui/src/components/Timeline/TimelineChart.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/Timeline/types.ts b/packages/grafana-ui/src/components/Timeline/types.ts index da0193d27fb..bd3cb1fcf82 100644 --- a/packages/grafana-ui/src/components/Timeline/types.ts +++ b/packages/grafana-ui/src/components/Timeline/types.ts @@ -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 diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegend.story.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegend.story.tsx index bf99f1fa0e3..c4428b10658 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegend.story.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegend.story.tsx @@ -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); diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx index ac05a624b9a..f93499d5605 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/VizLegend/models.cue b/packages/grafana-ui/src/components/VizLegend/models.cue new file mode 100644 index 00000000000..7221a830da2 --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/models.cue @@ -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 \ No newline at end of file diff --git a/packages/grafana-ui/src/components/VizLegend/models.gen.ts b/packages/grafana-ui/src/components/VizLegend/models.gen.ts new file mode 100644 index 00000000000..c5de02889aa --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/models.gen.ts @@ -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; +} diff --git a/packages/grafana-ui/src/components/VizLegend/types.ts b/packages/grafana-ui/src/components/VizLegend/types.ts index 3a966ead64d..73be4a4bd50 100644 --- a/packages/grafana-ui/src/components/VizLegend/types.ts +++ b/packages/grafana-ui/src/components/VizLegend/types.ts @@ -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 = (label: string, option: TOption) => void; export type SeriesColorChangeHandler = SeriesOptionChangeHandler; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 92b942fd469..026151acf64 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -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'; diff --git a/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx b/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx index efda6fe8726..e1d9bbd110a 100644 --- a/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx +++ b/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx @@ -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'; diff --git a/packages/grafana-ui/src/components/uPlot/models.cue b/packages/grafana-ui/src/components/uPlot/models.cue new file mode 100644 index 00000000000..b03c3a6969c --- /dev/null +++ b/packages/grafana-ui/src/components/uPlot/models.cue @@ -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") diff --git a/packages/grafana-ui/src/components/uPlot/models.gen.ts b/packages/grafana-ui/src/components/uPlot/models.gen.ts new file mode 100644 index 00000000000..681b403b0d2 --- /dev/null +++ b/packages/grafana-ui/src/components/uPlot/models.gen.ts @@ -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; +} diff --git a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx index 6559e6e274e..a8003bbff85 100644 --- a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx +++ b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx @@ -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 { diff --git a/pkg/schema/load/common.go b/pkg/schema/load/common.go new file mode 100644 index 00000000000..3ce07d0e109 --- /dev/null +++ b/pkg/schema/load/common.go @@ -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 +} diff --git a/pkg/schema/load/dashboard.go b/pkg/schema/load/dashboard.go new file mode 100644 index 00000000000..fd1bf837dd2 --- /dev/null +++ b/pkg/schema/load/dashboard.go @@ -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 ` == 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) +} diff --git a/pkg/schema/load/generic.go b/pkg/schema/load/generic.go new file mode 100644 index 00000000000..349e0497659 --- /dev/null +++ b/pkg/schema/load/generic.go @@ -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 ` == 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() + } +} diff --git a/pkg/schema/load/load_test.go b/pkg/schema/load/load_test.go new file mode 100644 index 00000000000..07d6dff602f --- /dev/null +++ b/pkg/schema/load/load_test.go @@ -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 + })) +} diff --git a/pkg/schema/load/panel.go b/pkg/schema/load/panel.go new file mode 100644 index 00000000000..5b3f17b462b --- /dev/null +++ b/pkg/schema/load/panel.go @@ -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 +} diff --git a/pkg/schema/load/testdata/artifacts/README.md b/pkg/schema/load/testdata/artifacts/README.md new file mode 100644 index 00000000000..0392ae23bda --- /dev/null +++ b/pkg/schema/load/testdata/artifacts/README.md @@ -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. diff --git a/pkg/schema/load/testdata/artifacts/dashboards/basic.json b/pkg/schema/load/testdata/artifacts/dashboards/basic.json new file mode 100644 index 00000000000..eadc68e5359 --- /dev/null +++ b/pkg/schema/load/testdata/artifacts/dashboards/basic.json @@ -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 +} \ No newline at end of file diff --git a/pkg/schema/load/testdata/artifacts/panels/.gitkeep b/pkg/schema/load/testdata/artifacts/panels/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/schema/load/testdata/artifacts/panels/basic-table.json b/pkg/schema/load/testdata/artifacts/panels/basic-table.json new file mode 100644 index 00000000000..43905a5e648 --- /dev/null +++ b/pkg/schema/load/testdata/artifacts/panels/basic-table.json @@ -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 + } +} \ No newline at end of file diff --git a/pkg/schema/load/testdata/plugins/panel/with-lineage/models.cue b/pkg/schema/load/testdata/plugins/panel/with-lineage/models.cue new file mode 100644 index 00000000000..03c31cf4926 --- /dev/null +++ b/pkg/schema/load/testdata/plugins/panel/with-lineage/models.cue @@ -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 + } + ] +} \ No newline at end of file diff --git a/pkg/schema/load/testdata/plugins/panel/with-lineage/plugin.json b/pkg/schema/load/testdata/plugins/panel/with-lineage/plugin.json new file mode 100644 index 00000000000..97fc3a21a46 --- /dev/null +++ b/pkg/schema/load/testdata/plugins/panel/with-lineage/plugin.json @@ -0,0 +1,9 @@ +{ + "type": "panel", + "name": "Sample plugin with lineage", + "id": "with-lineage", + + "info": { + "description": "Show how complex history may work" + } +} diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go new file mode 100644 index 00000000000..4035918b82c --- /dev/null +++ b/pkg/schema/schema.go @@ -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 diff --git a/pkg/schema/schema_test.go b/pkg/schema/schema_test.go new file mode 100644 index 00000000000..fee087844dd --- /dev/null +++ b/pkg/schema/schema_test.go @@ -0,0 +1,4 @@ +package schema + +// TODO tests for this stuff! Everything in this package is totally generic, +// nothing is specific to Grafana diff --git a/public/app/plugins/panel/table/TablePanel.tsx b/public/app/plugins/panel/table/TablePanel.tsx index 35fe0e88f91..9956d065aa1 100644 --- a/public/app/plugins/panel/table/TablePanel.tsx +++ b/public/app/plugins/panel/table/TablePanel.tsx @@ -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 {} +interface Props extends PanelProps {} export class TablePanel extends Component { constructor(props: Props) { diff --git a/public/app/plugins/panel/table/migrations.ts b/public/app/plugins/panel/table/migrations.ts index dc2fac31529..7487e312b16 100644 --- a/public/app/plugins/panel/table/migrations.ts +++ b/public/app/plugins/panel/table/migrations.ts @@ -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): Partial => { +export const tableMigrationHandler = (panel: PanelModel): Partial => { // 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> | any, + panel: PanelModel> | 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> | any, + panel: PanelModel> | any, prevPluginId: string, prevOptions: any ) => { diff --git a/public/app/plugins/panel/table/models.cue b/public/app/plugins/panel/table/models.cue new file mode 100644 index 00000000000..e272970ba12 --- /dev/null +++ b/public/app/plugins/panel/table/models.cue @@ -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: [] +} \ No newline at end of file diff --git a/public/app/plugins/panel/table/models.gen.ts b/public/app/plugins/panel/table/models.gen.ts new file mode 100644 index 00000000000..f3d7bfdd2ec --- /dev/null +++ b/public/app/plugins/panel/table/models.gen.ts @@ -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, +}; diff --git a/public/app/plugins/panel/table/module.tsx b/public/app/plugins/panel/table/module.tsx index f859dfb1f6b..01cbb330524 100644 --- a/public/app/plugins/panel/table/module.tsx +++ b/public/app/plugins/panel/table/module.tsx @@ -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(TablePanel) +export const plugin = new PanelPlugin(TablePanel) .setPanelChangeHandler(tablePanelChangedHandler) .setMigrationHandler(tableMigrationHandler) .setNoPadding() @@ -20,6 +20,7 @@ export const plugin = new PanelPlugin(TablePanel) max: 300, }, shouldApply: () => true, + defaultValue: defaultPanelFieldConfig.width, }) .addRadio({ path: 'align', @@ -32,7 +33,7 @@ export const plugin = new PanelPlugin(TablePanel) { label: 'right', value: 'right' }, ], }, - defaultValue: null, + defaultValue: defaultPanelFieldConfig.align, }) .addSelect({ path: 'displayMode', @@ -51,12 +52,13 @@ export const plugin = new PanelPlugin(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(TablePanel) path: 'showHeader', name: 'Show header', description: "To display table's header or not to display", - defaultValue: true, + defaultValue: defaultPanelOptions.showHeader, }); }); diff --git a/public/app/plugins/panel/table/plugin.json b/public/app/plugins/panel/table/plugin.json index 14d11500519..039726985ea 100644 --- a/public/app/plugins/panel/table/plugin.json +++ b/public/app/plugins/panel/table/plugin.json @@ -3,6 +3,11 @@ "name": "Table", "id": "table", + "models": { + "version": [1, 0], + "changed": "2021/03/30" + }, + "info": { "description": "Table Panel for Grafana", "author": { diff --git a/public/app/plugins/panel/table/types.ts b/public/app/plugins/panel/table/types.ts deleted file mode 100644 index 9126762388f..00000000000 --- a/public/app/plugins/panel/table/types.ts +++ /dev/null @@ -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; -} diff --git a/scripts/cuegen.sh b/scripts/cuegen.sh new file mode 100755 index 00000000000..3e003f69965 --- /dev/null +++ b/scripts/cuegen.sh @@ -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 {} \; \ No newline at end of file diff --git a/scripts/lib.star b/scripts/lib.star index cec3e1a6206..3ace8349a2d 100644 --- a/scripts/lib.star +++ b/scripts/lib.star @@ -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' diff --git a/scripts/master.star b/scripts/master.star index 9c4993560a3..eba5fd06e0e 100644 --- a/scripts/master.star +++ b/scripts/master.star @@ -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), diff --git a/scripts/pr.star b/scripts/pr.star index 5c7e9e4dcb8..de80102dcd7 100644 --- a/scripts/pr.star +++ b/scripts/pr.star @@ -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), diff --git a/scripts/release.star b/scripts/release.star index 44e331478b7..099e06b6668 100644 --- a/scripts/release.star +++ b/scripts/release.star @@ -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),