Reconcile coremodels, entities, objects under new kind framework (#56492)
* Update thema to latest * Deal with s/Library/*Runtime/ * Commit new, working results of codegen * We like pointers now * Always take runtime arg for NewBase() * Sketchy handwavy pass at entity meta framework * Little nibbles * Update pkg/framework/coremodel/entityframework.cue Co-authored-by: Artur Wierzbicki <wierzbicki.artur.94@gmail.com> * Move file into new framework location * Introduce loaders, Go code * Complete rename to kind * Flesh out framework, add svg/dashboard examples * Cruft removal * Remove generated kind go files from gitignore * Refine maturity concept, add SlotKind * Update embed and go deps * Export PrefixWithGrafanaCUE * Make the loader actually work, holy crap * Many small tweaks to type.cue * Add Apache 2 licensing exceptions for kinds * Add new kinds dir, start of generator * Roll back to earlier oapi-codegen * Introduce new grafana-specific CUE loaders * Introduce new tidy code generators framework * Catch up kind framework with tinkering * Add slices for the generators * Add write/verify step to main generator * Many renames * Split up kind framework cue files * Use kind.Decl within generated kinds * Create kind.SomeDecl wrapper type to cache lineages * Better names again * Get one generated implemented, hopefully * Copy dashboard schema into new kind.cue * Small fixes to make the initial gen work * Put svg kind in its new home * Add generated Go dashboard type * More renames and cleanups * Add base kind registry and generator * Stop blacklisting *_gen.go files This is not the Go best practice, anyway. All we actually want to ignore for enterprise is generated wire files. * Change codegen output directories pkg/kind -> pkg/kinds pkg/registry/kindreg -> pkg/registry/corekind * Rename pkg/framework/kind to pkg/kindsys * Add core structured kind generator * Add plural and machine names to kind spec * Copy playlist over to kind system * Consolidate kindsys files * Add raw kind generator * Update CODEOWNERS for kind framework * Touch up comments a bit * More docs tweaks * Remove generated types to reduce noise for review * Split each generator into its own file * Rename Slot kind to Composable kind * Add handwavy types for customkind loading * Guard against init calls to framework loader * First pass at doc on extending the kind system * Improve attribute example in docs * Fix wire imports * Add basic TS types generator * Fix composable kind category def * No need for a separate file with generate directive * Catch dashboard schema up * Rename generator types to something saner and generic * Make version configurable in ts/go generators * Add CommonMeta to ease property access * Add kindsys prop indicating whether lineage is group * Put all kind categories back in a single file * Finish with kindsys group props * Refactor maturity progression per discussion - Replace "committed" with "merged" - All kindcats can use all maturity levels, at least for now * Convert ts veneer index generator to modular system * Move over to new jennywrites framework * Strip down old coremodel generator * Use public version of jennywrites * Pull latest thema * Commit generated Go types * Add header injection postprocessor * Move sdboyer/jennywrites to grafana/codejen * Tweak header output * Remove dashboard and playlist coremodels * Fix up backend dashboards devenv test * Fix TS import patterns to new gen filename * Update internal imports, remove coremodel registry * Fix compilation errors, wire generation * Export and replace the prefix dropper * More Go struct and field name changes * Last name fixes, hopefully * Fix lint errors * Last lint error Co-authored-by: Artur Wierzbicki <wierzbicki.artur.94@gmail.com>
This commit is contained in:
co-authored by
Artur Wierzbicki
parent
f92d978386
commit
07e5f8117f
@@ -1,10 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
// HandoffSchemaVersion is the minimum schemaVersion for dashboards at which the
|
||||
// Thema-based dashboard schema is possibly valid
|
||||
//
|
||||
// schemaVersion is the original version numbering system for dashboards. If a
|
||||
// dashboard is below this schemaVersion, it is necessary for the frontend
|
||||
// typescript dashboard migration logic to first run and get it past this
|
||||
// number, after which Thema can take over.
|
||||
const HandoffSchemaVersion = 36
|
||||
@@ -1,455 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/thema"
|
||||
)
|
||||
|
||||
thema.#Lineage
|
||||
name: "dashboard"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{// 0.0
|
||||
@grafana(TSVeneer="type")
|
||||
|
||||
// Unique numeric identifier for the dashboard.
|
||||
// TODO must isolate or remove identifiers local to a Grafana instance...?
|
||||
id?: int64
|
||||
// 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 @grafanamaturity(NeedsExpertReview)
|
||||
// Tags associated with dashboard.
|
||||
tags?: [...string] @grafanamaturity(NeedsExpertReview)
|
||||
// Theme of dashboard.
|
||||
style: "light" | *"dark" @grafanamaturity(NeedsExpertReview)
|
||||
// Timezone of dashboard,
|
||||
timezone?: *"browser" | "utc" | "" @grafanamaturity(NeedsExpertReview)
|
||||
// Whether a dashboard is editable or not.
|
||||
editable: bool | *true
|
||||
graphTooltip: #DashboardCursorSync @grafanamaturity(NeedsExpertReview)
|
||||
// Time range for dashboard, e.g. last 6 hours, last 7 days, etc
|
||||
time?: {
|
||||
from: string | *"now-6h"
|
||||
to: string | *"now"
|
||||
} @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
// TODO this appears to be spread all over in the frontend. Concepts will likely need tidying in tandem with schema changes
|
||||
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"]
|
||||
// TODO docs
|
||||
time_options: [...string] | *["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
|
||||
} @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
fiscalYearStartMonth?: uint8 & <13 @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
liveNow?: bool @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
weekStart?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
refresh?: string | false @grafanamaturity(NeedsExpertReview)
|
||||
// Version of the JSON schema, incremented each time a Grafana update brings
|
||||
// changes to said schema.
|
||||
// TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion
|
||||
schemaVersion: uint16 | *36
|
||||
// Version of the dashboard, incremented each time the dashboard is updated.
|
||||
version?: uint32 @grafanamaturity(NeedsExpertReview)
|
||||
panels?: [...(#Panel | #RowPanel | #GraphPanel | #HeatmapPanel)] @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
templating?: {
|
||||
list: [...#VariableModel] @grafanamaturity(NeedsExpertReview)
|
||||
}
|
||||
// TODO docs
|
||||
annotations?: {
|
||||
list: [...#AnnotationQuery] @grafanamaturity(NeedsExpertReview)
|
||||
}
|
||||
// TODO docs
|
||||
links?: [...#DashboardLink] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
///////////////////////////////////////
|
||||
// Definitions (referenced above) are declared below
|
||||
|
||||
#AnnotationTarget: {
|
||||
limit: int64
|
||||
matchAny: bool
|
||||
tags: [...string]
|
||||
type: string
|
||||
}
|
||||
|
||||
// TODO docs
|
||||
// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
||||
#AnnotationQuery: {
|
||||
// Datasource to use for annotation.
|
||||
datasource: {
|
||||
type?: string
|
||||
uid?: string
|
||||
} @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Whether annotation is enabled.
|
||||
enable: bool | *true @grafanamaturity(NeedsExpertReview)
|
||||
// Name of annotation.
|
||||
name?: string @grafanamaturity(NeedsExpertReview)
|
||||
builtIn: uint8 | *0 @grafanamaturity(NeedsExpertReview) // TODO should this be persisted at all?
|
||||
// Whether to hide annotation.
|
||||
hide?: bool | *false @grafanamaturity(NeedsExpertReview)
|
||||
// Annotation icon color.
|
||||
iconColor?: string @grafanamaturity(NeedsExpertReview)
|
||||
type: string | *"dashboard" @grafanamaturity(NeedsExpertReview)
|
||||
// Query for annotation data.
|
||||
rawQuery?: string @grafanamaturity(NeedsExpertReview)
|
||||
showIn: uint8 | *0 @grafanamaturity(NeedsExpertReview)
|
||||
target?: #AnnotationTarget @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// FROM: packages/grafana-data/src/types/templateVars.ts
|
||||
// TODO docs
|
||||
// TODO what about what's in public/app/features/types.ts?
|
||||
// TODO there appear to be a lot of different kinds of [template] vars here? if so need a disjunction
|
||||
#VariableModel: {
|
||||
type: #VariableType
|
||||
name: string
|
||||
label?: string
|
||||
...
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// FROM public/app/features/dashboard/state/DashboardModels.ts - ish
|
||||
// TODO docs
|
||||
#DashboardLink: {
|
||||
title: string @grafanamaturity(NeedsExpertReview)
|
||||
type: #DashboardLinkType @grafanamaturity(NeedsExpertReview)
|
||||
icon?: string @grafanamaturity(NeedsExpertReview)
|
||||
tooltip?: string @grafanamaturity(NeedsExpertReview)
|
||||
url?: string @grafanamaturity(NeedsExpertReview)
|
||||
tags: [...string] @grafanamaturity(NeedsExpertReview)
|
||||
asDropdown: bool | *false @grafanamaturity(NeedsExpertReview)
|
||||
targetBlank: bool | *false @grafanamaturity(NeedsExpertReview)
|
||||
includeVars: bool | *false @grafanamaturity(NeedsExpertReview)
|
||||
keepTime: bool | *false @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// TODO docs
|
||||
#DashboardLinkType: "link" | "dashboards" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// FROM: packages/grafana-data/src/types/templateVars.ts
|
||||
// TODO docs
|
||||
// TODO this implies some wider pattern/discriminated union, probably?
|
||||
#VariableType: "query" | "adhoc" | "constant" | "datasource" | "interval" | "textbox" | "custom" | "system" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#FieldColorModeId: "thresholds" | "palette-classic" | "palette-saturated" | "continuous-GrYlRd" | "fixed" @cuetsy(kind="enum",memberNames="Thresholds|PaletteClassic|PaletteSaturated|ContinuousGrYlRd|Fixed") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#FieldColorSeriesByMode: "min" | "max" | "last" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#FieldColor: {
|
||||
// The main color scheme mode
|
||||
mode: #FieldColorModeId | string
|
||||
// Stores the fixed color value if mode is fixed
|
||||
fixedColor?: string
|
||||
// Some visualizations need to know how to assign a series color from by value color schemes
|
||||
seriesBy?: #FieldColorSeriesByMode
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
#GridPos: {
|
||||
// Panel
|
||||
h: uint32 & >0 | *9 @grafanamaturity(NeedsExpertReview)
|
||||
// Panel
|
||||
w: uint32 & >0 & <=24 | *12 @grafanamaturity(NeedsExpertReview)
|
||||
// Panel x
|
||||
x: uint32 & >=0 & <24 | *0 @grafanamaturity(NeedsExpertReview)
|
||||
// Panel y
|
||||
y: uint32 & >=0 | *0 @grafanamaturity(NeedsExpertReview)
|
||||
// true if fixed
|
||||
static?: bool @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// TODO docs
|
||||
#Threshold: {
|
||||
// TODO docs
|
||||
// FIXME the corresponding typescript field is required/non-optional, but nulls currently appear here when serializing -Infinity to JSON
|
||||
value?: number @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
color: string @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
// TODO are the values here enumerable into a disjunction?
|
||||
// Some seem to be listed in typescript comment
|
||||
state?: string @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
#ThresholdsMode: "absolute" | "percentage" @cuetsy(kind="enum") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
#ThresholdsConfig: {
|
||||
mode: #ThresholdsMode @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Must be sorted by 'value', first value is always -Infinity
|
||||
steps: [...#Threshold] @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#ValueMapping: #ValueMap | #RangeMap | #RegexMap | #SpecialValueMap @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#MappingType: "value" | "range" | "regex" | "special" @cuetsy(kind="enum",memberNames="ValueToText|RangeToText|RegexToText|SpecialValue") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#ValueMap: {
|
||||
type: #MappingType & "value"
|
||||
options: [string]: #ValueMappingResult
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// TODO docs
|
||||
#RangeMap: {
|
||||
type: #MappingType & "range"
|
||||
options: {
|
||||
// to and from are `number | null` in current ts, really not sure what to do
|
||||
from: float64 @grafanamaturity(NeedsExpertReview)
|
||||
to: float64 @grafanamaturity(NeedsExpertReview)
|
||||
result: #ValueMappingResult
|
||||
}
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#RegexMap: {
|
||||
type: #MappingType & "regex"
|
||||
options: {
|
||||
pattern: string
|
||||
result: #ValueMappingResult
|
||||
}
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#SpecialValueMap: {
|
||||
type: #MappingType & "special"
|
||||
options: {
|
||||
match: "true" | "false"
|
||||
pattern: string
|
||||
result: #ValueMappingResult
|
||||
}
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
#SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cuetsy(kind="enum",memberNames="True|False|Null|NaN|NullAndNan|Empty")
|
||||
|
||||
// TODO docs
|
||||
#ValueMappingResult: {
|
||||
text?: string
|
||||
color?: string
|
||||
icon?: string
|
||||
index?: int32
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// TODO docs
|
||||
// FIXME this is extremely underspecfied; wasn't obvious which typescript types corresponded to it
|
||||
#Transformation: {
|
||||
id: string
|
||||
options: {...}
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// 0 for no shared crosshair or tooltip (default).
|
||||
// 1 for shared crosshair.
|
||||
// 2 for shared crosshair AND shared tooltip.
|
||||
#DashboardCursorSync: *0 | 1 | 2 @cuetsy(kind="enum",memberNames="Off|Crosshair|Tooltip") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Schema for panel targets is specified by datasource
|
||||
// plugins. We use a placeholder definition, which the Go
|
||||
// schema loader either left open/as-is with the Base
|
||||
// variant of the Dashboard and Panel families, or filled
|
||||
// with types derived from plugins in the Instance variant.
|
||||
// When working directly from CUE, importers can extend this
|
||||
// type directly to achieve the same effect.
|
||||
#Target: {...} @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Dashboard panels. Panels are canonically defined inline
|
||||
// because they share a version timeline with the dashboard
|
||||
// schema; they do not evolve independently.
|
||||
#Panel: {
|
||||
// The panel plugin type id. May not be empty.
|
||||
type: string & strings.MinRunes(1) @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
id?: uint32 @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// FIXME this almost certainly has to be changed in favor of scuemata versions
|
||||
pluginVersion?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
tags?: [...string] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
targets?: [...#Target] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Panel title.
|
||||
title?: string @grafanamaturity(NeedsExpertReview)
|
||||
// Description.
|
||||
description?: string @grafanamaturity(NeedsExpertReview)
|
||||
// Whether to display the panel without a background.
|
||||
transparent: bool | *false @grafanamaturity(NeedsExpertReview)
|
||||
// The datasource used in all targets.
|
||||
datasource?: {
|
||||
type?: string
|
||||
uid?: string
|
||||
} @grafanamaturity(NeedsExpertReview)
|
||||
// Grid position.
|
||||
gridPos?: #GridPos
|
||||
// Panel links.
|
||||
// TODO fill this out - seems there are a couple variants?
|
||||
links?: [...#DashboardLink] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Name of template variable to repeat for.
|
||||
repeat?: string @grafanamaturity(NeedsExpertReview)
|
||||
// Direction to repeat in if 'repeat' is set.
|
||||
// "h" for horizontal, "v" for vertical.
|
||||
repeatDirection: *"h" | "v" @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
maxDataPoints?: number @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs - seems to be an old field from old dashboard alerts?
|
||||
thresholds?: [...] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
timeRegions?: [...] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
transformations: [...#Transformation] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
// TODO tighter constraint
|
||||
interval?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
// TODO tighter constraint
|
||||
timeFrom?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// TODO docs
|
||||
// TODO tighter constraint
|
||||
timeShift?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// options is specified by the PanelOptions field in panel
|
||||
// plugin schemas.
|
||||
options: {...} @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
fieldConfig: #FieldConfigSource
|
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
#FieldConfigSource: {
|
||||
defaults: #FieldConfig
|
||||
overrides: [...{
|
||||
matcher: #MatcherConfig
|
||||
properties: [...#DynamicConfigValue]
|
||||
}] @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
#MatcherConfig: {
|
||||
id: string | *"" @grafanamaturity(NeedsExpertReview)
|
||||
options?: _ @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
#DynamicConfigValue: {
|
||||
id: string | *"" @grafanamaturity(NeedsExpertReview)
|
||||
value?: _ @grafanamaturity(NeedsExpertReview)
|
||||
}
|
||||
|
||||
#FieldConfig: {
|
||||
// The display value for this field. This supports template variables blank is auto
|
||||
displayName?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// 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 @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Human readable field metadata
|
||||
description?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// 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 @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// True if data source can write a value to the path. Auth/authz are supported separately
|
||||
writeable?: bool @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// True if data source field supports ad-hoc filters
|
||||
filterable?: bool @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Numeric Options
|
||||
unit?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Significant digits (for display)
|
||||
decimals?: number @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
min?: number @grafanamaturity(NeedsExpertReview)
|
||||
max?: number @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Convert input values into a display string
|
||||
mappings?: [...#ValueMapping] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Map numeric values to states
|
||||
thresholds?: #ThresholdsConfig @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Map values to a display color
|
||||
color?: #FieldColor @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Used when reducing field values
|
||||
// nullValueMode?: NullValueMode
|
||||
|
||||
// The behavior when clicking on a result
|
||||
links?: [...] @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// custom is specified by the PanelFieldConfig field
|
||||
// in panel plugin schemas.
|
||||
custom?: {...} @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Row panel
|
||||
#RowPanel: {
|
||||
type: "row" @grafanamaturity(NeedsExpertReview)
|
||||
collapsed: bool | *false @grafanamaturity(NeedsExpertReview)
|
||||
title?: string @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Name of default datasource.
|
||||
datasource?: {
|
||||
type?: string @grafanamaturity(NeedsExpertReview)
|
||||
uid?: string @grafanamaturity(NeedsExpertReview)
|
||||
} @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
gridPos?: #GridPos
|
||||
id: uint32 @grafanamaturity(NeedsExpertReview)
|
||||
panels: [...(#Panel | #GraphPanel | #HeatmapPanel)] @grafanamaturity(NeedsExpertReview)
|
||||
// Name of template variable to repeat for.
|
||||
repeat?: string @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
// Support for legacy graph and heatmap panels.
|
||||
#GraphPanel: {
|
||||
type: "graph" @grafanamaturity(NeedsExpertReview)
|
||||
...
|
||||
} @grafanamaturity(NeedsExpertReview)
|
||||
#HeatmapPanel: {
|
||||
type: "heatmap" @grafanamaturity(NeedsExpertReview)
|
||||
...
|
||||
} @grafanamaturity(NeedsExpertReview)
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
package dashboard_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"cuelang.org/go/cue/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/coremodel/dashboard"
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
)
|
||||
|
||||
func TestDevenvDashboardValidity(t *testing.T) {
|
||||
path, err := filepath.Abs("../../../devenv/dev-dashboards")
|
||||
require.NoError(t, err)
|
||||
|
||||
m, err := themaTestableDashboards(os.DirFS(path))
|
||||
require.NoError(t, err)
|
||||
cm, err := dashboard.New(cuectx.GrafanaThemaRuntime())
|
||||
require.NoError(t, err)
|
||||
|
||||
for path, b := range m {
|
||||
t.Run(path, func(t *testing.T) {
|
||||
// The path arg here only matters for error output
|
||||
cv, err := cuectx.JSONtoCUE(path, b)
|
||||
require.NoError(t, err, "error while decoding dashboard JSON into a CUE value")
|
||||
|
||||
_, err = cm.CurrentSchema().Validate(cv)
|
||||
if err != nil {
|
||||
// Testify trims errors to short length. We want the full text
|
||||
errstr := errors.Details(err, nil)
|
||||
t.Log(errstr)
|
||||
if strings.Contains(errstr, "null") {
|
||||
t.Log("validation failure appears to involve nulls - see if scripts/stripnulls.sh has any effect?")
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func themaTestableDashboards(in fs.FS) (map[string][]byte, error) {
|
||||
m := make(map[string][]byte)
|
||||
|
||||
err := fs.WalkDir(in, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() || filepath.Ext(d.Name()) != ".json" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// nolint:gosec
|
||||
f, err := in.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close() //nolint:errcheck
|
||||
|
||||
b, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jtree := make(map[string]interface{})
|
||||
err = json.Unmarshal(b, &jtree)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oldschemav, has := jtree["schemaVersion"]; !has || !(oldschemav.(float64) > dashboard.HandoffSchemaVersion-1) {
|
||||
return nil
|
||||
}
|
||||
|
||||
m[path] = b
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"github.com/grafana/thema"
|
||||
)
|
||||
|
||||
thema.#Lineage
|
||||
name: "playlist"
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{//0.0
|
||||
// Unique playlist identifier. Generated on creation, either by the
|
||||
// creator of the playlist of by the application.
|
||||
uid: string
|
||||
|
||||
// Name of the playlist.
|
||||
name: string
|
||||
|
||||
// Interval sets the time between switching views in a playlist.
|
||||
// FIXME: Is this based on a standardized format or what options are available? Can datemath be used?
|
||||
interval: string | *"5m"
|
||||
|
||||
// The ordered list of items that the playlist will iterate over.
|
||||
// FIXME! This should not be optional, but changing it makes the godegen awkward
|
||||
items?: [...#PlaylistItem]
|
||||
|
||||
///////////////////////////////////////
|
||||
// Definitions (referenced above) are declared below
|
||||
|
||||
#PlaylistItem: {
|
||||
// Type of the item.
|
||||
type: "dashboard_by_uid" | "dashboard_by_id" | "dashboard_by_tag"
|
||||
// Value depends on type and describes the playlist item.
|
||||
//
|
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
// is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
// dashboards behind the tag will be added to the playlist.
|
||||
// - dashboard_by_uid: The value is the dashboard UID
|
||||
value: string
|
||||
|
||||
// Title is an unused property -- it will be removed in the future
|
||||
title?: string
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,136 +0,0 @@
|
||||
// This file is autogenerated. DO NOT EDIT.
|
||||
//
|
||||
// Generated by pkg/framework/coremodel/gen.go
|
||||
//
|
||||
// Derived from the Thema lineage declared in pkg/coremodel/playlist/coremodel.cue
|
||||
//
|
||||
// Run `make gen-cue` from repository root to regenerate.
|
||||
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
"github.com/grafana/grafana/pkg/framework/coremodel"
|
||||
"github.com/grafana/thema"
|
||||
)
|
||||
|
||||
// Defines values for PlaylistItemType.
|
||||
const (
|
||||
PlaylistItemTypeDashboardById PlaylistItemType = "dashboard_by_id"
|
||||
|
||||
PlaylistItemTypeDashboardByTag PlaylistItemType = "dashboard_by_tag"
|
||||
|
||||
PlaylistItemTypeDashboardByUid PlaylistItemType = "dashboard_by_uid"
|
||||
)
|
||||
|
||||
// Model is the Go representation of a playlist.
|
||||
//
|
||||
// THIS TYPE IS INTENDED FOR INTERNAL USE BY THE GRAFANA BACKEND, AND IS SUBJECT TO BREAKING CHANGES.
|
||||
// Equivalent Go types at stable import paths are provided in https://github.com/grafana/grok.
|
||||
type Model struct {
|
||||
// Interval sets the time between switching views in a playlist.
|
||||
// FIXME: Is this based on a standardized format or what options are available? Can datemath be used?
|
||||
Interval string `json:"interval"`
|
||||
|
||||
// The ordered list of items that the playlist will iterate over.
|
||||
// FIXME! This should not be optional, but changing it makes the godegen awkward
|
||||
Items *[]PlaylistItem `json:"items,omitempty"`
|
||||
|
||||
// Name of the playlist.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Unique playlist identifier. Generated on creation, either by the
|
||||
// creator of the playlist of by the application.
|
||||
Uid string `json:"uid"`
|
||||
}
|
||||
|
||||
// PlaylistItem is the Go representation of a playlist.Item.
|
||||
//
|
||||
// THIS TYPE IS INTENDED FOR INTERNAL USE BY THE GRAFANA BACKEND, AND IS SUBJECT TO BREAKING CHANGES.
|
||||
// Equivalent Go types at stable import paths are provided in https://github.com/grafana/grok.
|
||||
type PlaylistItem struct {
|
||||
// Title is an unused property -- it will be removed in the future
|
||||
Title *string `json:"title,omitempty"`
|
||||
|
||||
// Type of the item.
|
||||
Type PlaylistItemType `json:"type"`
|
||||
|
||||
// Value depends on type and describes the playlist item.
|
||||
//
|
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
// is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
// dashboards behind the tag will be added to the playlist.
|
||||
// - dashboard_by_uid: The value is the dashboard UID
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Type of the item.
|
||||
//
|
||||
// THIS TYPE IS INTENDED FOR INTERNAL USE BY THE GRAFANA BACKEND, AND IS SUBJECT TO BREAKING CHANGES.
|
||||
// Equivalent Go types at stable import paths are provided in https://github.com/grafana/grok.
|
||||
type PlaylistItemType string
|
||||
|
||||
//go:embed coremodel.cue
|
||||
var cueFS embed.FS
|
||||
|
||||
// The current version of the coremodel schema, as declared in coremodel.cue.
|
||||
// This version determines what schema version is returned from [Coremodel.CurrentSchema],
|
||||
// and which schema version is used for code generation within the grafana/grafana repository.
|
||||
//
|
||||
// The code generator ensures that this is always the latest Thema schema version.
|
||||
var currentVersion = thema.SV(0, 0)
|
||||
|
||||
// Lineage returns the Thema lineage representing a Grafana playlist.
|
||||
//
|
||||
// The lineage is the canonical specification of the current playlist schema,
|
||||
// all prior schema versions, and the mappings that allow migration between
|
||||
// schema versions.
|
||||
func Lineage(rt *thema.Runtime, opts ...thema.BindOption) (thema.Lineage, error) {
|
||||
return cuectx.LoadGrafanaInstancesWithThema(filepath.Join("pkg", "coremodel", "playlist"), cueFS, rt, opts...)
|
||||
}
|
||||
|
||||
var _ thema.LineageFactory = Lineage
|
||||
var _ coremodel.Interface = &Coremodel{}
|
||||
|
||||
// Coremodel contains the foundational schema declaration for playlists.
|
||||
// It implements coremodel.Interface.
|
||||
type Coremodel struct {
|
||||
lin thema.Lineage
|
||||
}
|
||||
|
||||
// Lineage returns the canonical playlist Lineage.
|
||||
func (c *Coremodel) Lineage() thema.Lineage {
|
||||
return c.lin
|
||||
}
|
||||
|
||||
// CurrentSchema returns the current (latest) playlist Thema schema.
|
||||
func (c *Coremodel) CurrentSchema() thema.Schema {
|
||||
return thema.SchemaP(c.lin, currentVersion)
|
||||
}
|
||||
|
||||
// GoType returns a pointer to an empty Go struct that corresponds to
|
||||
// the current Thema schema.
|
||||
func (c *Coremodel) GoType() interface{} {
|
||||
return &Model{}
|
||||
}
|
||||
|
||||
// New returns a new instance of the playlist coremodel.
|
||||
//
|
||||
// Note that this function does not cache, and initially loading a Thema lineage
|
||||
// can be expensive. As such, the Grafana backend should prefer to access this
|
||||
// coremodel through a registry (pkg/framework/coremodel/registry), which does cache.
|
||||
func New(rt *thema.Runtime) (*Coremodel, error) {
|
||||
lin, err := Lineage(rt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Coremodel{
|
||||
lin: lin,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user