mirror of
https://github.com/grafana/grafana.git
synced 2024-11-28 11:44:26 -06:00
61cd9eeb24
* Kinds: Generate JSON report * Kinds: Add Validate/Write support for JSON report * Kinds: Report format * Kinds: Report new line * Kinds: Use kindsys.SomeKindMeta to generate the report * Move kinds report generation to the end * Re-structure kinds report JSON output * Make prettier to ignore kinds json report * Minor on kinds report generation * Fix toggles_gen test
232 lines
7.7 KiB
Go
232 lines
7.7 KiB
Go
package kindsys
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"cuelang.org/go/cue"
|
|
"cuelang.org/go/cue/errors"
|
|
"github.com/grafana/grafana/pkg/cuectx"
|
|
"github.com/grafana/thema"
|
|
)
|
|
|
|
// DeclParentPath is the path, relative to the repository root, where
|
|
// each child directory is expected to contain directories with .cue files,
|
|
// declaring one kind.
|
|
var DeclParentPath = "kinds"
|
|
|
|
// CoreStructuredDeclParentPath is the path, relative to the repository root, where
|
|
// each child directory is expected to contain .cue files declaring one
|
|
// CoreStructured kind.
|
|
var CoreStructuredDeclParentPath = filepath.Join(DeclParentPath, "structured")
|
|
|
|
// RawDeclParentPath is the path, relative to the repository root, where each child
|
|
// directory is expected to contain .cue files declaring one Raw kind.
|
|
var RawDeclParentPath = filepath.Join(DeclParentPath, "raw")
|
|
|
|
// GoCoreKindParentPath is the path, relative to the repository root, to the directory
|
|
// containing one directory per kind, full of generated Go kind output: types and bindings.
|
|
var GoCoreKindParentPath = filepath.Join("pkg", "kinds")
|
|
|
|
// TSCoreKindParentPath is the path, relative to the repository root, to the directory that
|
|
// contains one directory per kind, full of generated TS kind output: types and default consts.
|
|
var TSCoreKindParentPath = filepath.Join("packages", "grafana-schema", "src", "raw")
|
|
|
|
var defaultFramework cue.Value
|
|
var fwOnce sync.Once
|
|
|
|
func init() {
|
|
loadpFrameworkOnce()
|
|
}
|
|
|
|
func loadpFrameworkOnce() {
|
|
fwOnce.Do(func() {
|
|
var err error
|
|
defaultFramework, err = doLoadFrameworkCUE(cuectx.GrafanaCUEContext())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func doLoadFrameworkCUE(ctx *cue.Context) (cue.Value, error) {
|
|
v, err := cuectx.BuildGrafanaInstance(ctx, filepath.Join("pkg", "kindsys"), "kindsys", nil)
|
|
if err != nil {
|
|
return v, err
|
|
}
|
|
|
|
if err = v.Validate(cue.Concrete(false), cue.All()); err != nil {
|
|
return cue.Value{}, fmt.Errorf("kindsys framework loaded cue.Value has err: %w", err)
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
// CUEFramework returns a cue.Value representing all the kind framework
|
|
// raw CUE files.
|
|
//
|
|
// For low-level use in constructing other types and APIs, while still letting
|
|
// us declare all the frameworky CUE bits in a single package. Other Go types
|
|
// make the constructs in this value easy to use.
|
|
//
|
|
// All calling code within grafana/grafana is expected to use Grafana's
|
|
// singleton [cue.Context], returned from [cuectx.GrafanaCUEContext]. If nil
|
|
// is passed, the singleton will be used.
|
|
func CUEFramework(ctx *cue.Context) cue.Value {
|
|
if ctx == nil || ctx == cuectx.GrafanaCUEContext() {
|
|
// Ensure framework is loaded, even if this func is called
|
|
// from an init() somewhere.
|
|
loadpFrameworkOnce()
|
|
return defaultFramework
|
|
}
|
|
// Error guaranteed to be nil here because erroring would have caused init() to panic
|
|
v, _ := doLoadFrameworkCUE(ctx) // nolint:errcheck
|
|
return v
|
|
}
|
|
|
|
// ToKindMeta takes a cue.Value expected to represent a kind of the category
|
|
// specified by the type parameter and populates the Go type from the cue.Value.
|
|
func ToKindMeta[T KindProperties](v cue.Value) (T, error) {
|
|
props := new(T)
|
|
if !v.Exists() {
|
|
return *props, ErrValueNotExist
|
|
}
|
|
|
|
fw := CUEFramework(v.Context())
|
|
var kdef cue.Value
|
|
|
|
anyprops := any(*props).(SomeKindProperties)
|
|
switch anyprops.(type) {
|
|
case RawProperties:
|
|
kdef = fw.LookupPath(cue.MakePath(cue.Def("Raw")))
|
|
case CoreStructuredProperties:
|
|
kdef = fw.LookupPath(cue.MakePath(cue.Def("CoreStructured")))
|
|
case CustomStructuredProperties:
|
|
kdef = fw.LookupPath(cue.MakePath(cue.Def("CustomStructured")))
|
|
case ComposableProperties:
|
|
kdef = fw.LookupPath(cue.MakePath(cue.Def("Composable")))
|
|
default:
|
|
// unreachable so long as all the possibilities in KindProperties have switch branches
|
|
panic("unreachable")
|
|
}
|
|
|
|
item := v.Unify(kdef)
|
|
if err := item.Validate(cue.Concrete(false), cue.All()); err != nil {
|
|
return *props, ewrap(item.Err(), ErrValueNotAKind)
|
|
}
|
|
if err := item.Decode(props); err != nil {
|
|
// Should only be reachable if CUE and Go framework types have diverged
|
|
panic(errors.Details(err, nil))
|
|
}
|
|
|
|
return *props, nil
|
|
}
|
|
|
|
// SomeDecl represents a single kind declaration, having been loaded
|
|
// and validated by a func such as [LoadCoreKind].
|
|
//
|
|
// The underlying type of the Properties field indicates the category of
|
|
// kind.
|
|
type SomeDecl struct {
|
|
// V is the cue.Value containing the entire Kind declaration.
|
|
V cue.Value
|
|
// Properties contains the kind's declared properties.
|
|
Properties SomeKindProperties
|
|
}
|
|
|
|
// BindKindLineage binds the lineage for the kind declaration. nil, nil is returned
|
|
// for raw kinds.
|
|
//
|
|
// For kinds with a corresponding Go type, it is left to the caller to associate
|
|
// that Go type with the lineage returned from this function by a call to [thema.BindType].
|
|
func (decl *SomeDecl) BindKindLineage(rt *thema.Runtime, opts ...thema.BindOption) (thema.Lineage, error) {
|
|
if rt == nil {
|
|
rt = cuectx.GrafanaThemaRuntime()
|
|
}
|
|
switch decl.Properties.(type) {
|
|
case RawProperties:
|
|
return nil, nil
|
|
case CoreStructuredProperties, CustomStructuredProperties, ComposableProperties:
|
|
return thema.BindLineage(decl.V.LookupPath(cue.MakePath(cue.Str("lineage"))), rt, opts...)
|
|
default:
|
|
panic("unreachable")
|
|
}
|
|
}
|
|
|
|
// IsRaw indicates whether the represented kind is a raw kind.
|
|
func (decl *SomeDecl) IsRaw() bool {
|
|
_, is := decl.Properties.(RawProperties)
|
|
return is
|
|
}
|
|
|
|
// IsCoreStructured indicates whether the represented kind is a core structured kind.
|
|
func (decl *SomeDecl) IsCoreStructured() bool {
|
|
_, is := decl.Properties.(CoreStructuredProperties)
|
|
return is
|
|
}
|
|
|
|
// IsCustomStructured indicates whether the represented kind is a custom structured kind.
|
|
func (decl *SomeDecl) IsCustomStructured() bool {
|
|
_, is := decl.Properties.(CustomStructuredProperties)
|
|
return is
|
|
}
|
|
|
|
// IsComposable indicates whether the represented kind is a composable kind.
|
|
func (decl *SomeDecl) IsComposable() bool {
|
|
_, is := decl.Properties.(ComposableProperties)
|
|
return is
|
|
}
|
|
|
|
// Decl represents a single kind declaration, having been loaded
|
|
// and validated by a func such as [LoadCoreKind].
|
|
//
|
|
// Its type parameter indicates the category of kind.
|
|
type Decl[T KindProperties] struct {
|
|
// V is the cue.Value containing the entire Kind declaration.
|
|
V cue.Value
|
|
// Properties contains the kind's declared properties.
|
|
Properties T
|
|
}
|
|
|
|
// Some converts the typed Decl to the equivalent typeless SomeDecl.
|
|
func (decl *Decl[T]) Some() *SomeDecl {
|
|
return &SomeDecl{
|
|
V: decl.V,
|
|
Properties: any(decl.Properties).(SomeKindProperties),
|
|
}
|
|
}
|
|
|
|
// LoadCoreKind loads and validates a core kind declaration of the kind category
|
|
// indicated by the type parameter. On success, it returns a [Decl] which
|
|
// contains the entire contents of the kind declaration.
|
|
//
|
|
// declpath is the path to the directory containing the core kind declaration,
|
|
// relative to the grafana/grafana root. For example, dashboards are in
|
|
// "kinds/structured/dashboard".
|
|
//
|
|
// The .cue file bytes containing the core kind declaration will be retrieved
|
|
// from the central embedded FS, [grafana.CueSchemaFS]. If desired (e.g. for
|
|
// testing), an optional fs.FS may be provided via the overlay parameter, which
|
|
// will be merged over [grafana.CueSchemaFS]. But in typical circumstances,
|
|
// overlay can and should be nil.
|
|
//
|
|
// This is a low-level function, primarily intended for use in code generation.
|
|
// For representations of core kinds that are useful in Go programs at runtime,
|
|
// see ["github.com/grafana/grafana/pkg/registry/corekind"].
|
|
func LoadCoreKind[T RawProperties | CoreStructuredProperties](declpath string, ctx *cue.Context, overlay fs.FS) (*Decl[T], error) {
|
|
vk, err := cuectx.BuildGrafanaInstance(ctx, declpath, "kind", overlay)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
decl := &Decl[T]{
|
|
V: vk,
|
|
}
|
|
decl.Properties, err = ToKindMeta[T](vk)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return decl, nil
|
|
}
|