grafana/pkg/kindsys/slot.go
Marcus Andersson 7f92f1df00
Schema: Refactor plugin code generation (#58901)
* wip

* wip

* almost there..

* wip - change so it can run.

* treelist is working.

* support CODEGEN_VERIFY env variable

* use log.fatal

* comment out old PluginTreeList code generation

* cleanup

* rename corelist package files

* fix makefile

* move pkg/codegen/pluggen.go to pkg/plugins/codegen

* copy and refactor files to pkg/plugins/codegen

* use pkg/plugins/codegen instead of pkg/codegen for core plugins code gen

* remove unneeded files

* remove unused code to resolve linting errors

* adapters first hack

* added flattener

* add back ignore build tags to go generate file

* cleaned up the code a bit.

* seems to work, needs to do some refactoring of the GoTypesJenns and TSTypesJenny.

* one more step, going to get upstream changes in this branch.

* working but need to run import tmpl in jenny_schemapath to have the proper imports.

* added header to generated files.

* added missing jenny.

* preventing plugins with multiple decls/schemas to insert multiple lines in corelist.

* fixed so we use Slot type from kindsys to detect if its group.

* adding a go jenny that only runs if the plugin has a backend.

* added version object to generated ts.

* generating the ts types with the same output as prior to this refactoring.

* removed code that is replaced by the jenny pattern.

* removed the go code that isn't used anymore.

* removed some more unused code and renamed pluggen to util_ts

* fixed linting issue.

* removed unused vars.

* use a jenny list postprocessor for header injection

* moved decl and decl_parser to pfs.

* removed the pre-pended header in the gotypes jenny since it is done in the postprocess.

* moved decl to pfs.

* removed unused template.

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2022-12-02 08:22:28 +01:00

122 lines
3.5 KiB
Go

package kindsys
import (
"fmt"
"cuelang.org/go/cue"
)
// Slot represents one of Grafana's named slot definitions.
// TODO link to framework docs
type Slot struct {
name string
raw cue.Value
plugins map[string]bool
}
// Name returns the name of the Slot.
//
// The name is also used as the path at which a Slot lineage is defined in a
// plugin models.cue file.
func (s Slot) Name() string {
return s.name
}
// MetaSchema returns the meta-schema that is the contract between core or
// custom kinds that compose the meta-schema, and the plugin-declared composable
// kinds that implement the meta-schema.
func (s Slot) MetaSchema() cue.Value {
return s.raw
}
// ForPluginType indicates whether for this Slot, plugins of the given type may
// provide a slot implementation (first return value), and for those types that
// may, whether they must produce one (second return value).
//
// Expected values here are those in the set of
// ["github.com/grafana/grafana/pkg/plugins/plugindef".Type], though passing
// a string not in that set will harmlessly return {false, false}. That type is
// not used here to avoid import cycles.
//
// Note that, at least for now, plugins are not required to provide any slot
// implementations, and do so by simply not containing any .cue files in the
// "grafanaplugin" package. Consequently, the "must" return value is best
// understood as, "IF a plugin provides a *.cue files, it MUST contain an
// implementation of this slot."
func (s Slot) ForPluginType(plugintype string) (may, must bool) {
must, may = s.plugins[plugintype]
return
}
// IsGroup indicates whether the slot specifies a group lineage - one in which
// each top-level key represents a distinct schema for objects that are expected
// to exist in the wild, but objects corresponding to the root of the schema are not
// expected to exist.
func (s Slot) IsGroup() bool {
// TODO rely on first-class Thema properties for this, one they exist - https://github.com/grafana/thema/issues/62
switch s.name {
case "Panel", "DSOptions":
return true
case "Query":
return false
default:
panic("unreachable - unknown slot name " + s.name)
}
}
func FindSlot(name string) (*Slot, error) {
sl, has := AllSlots(nil)[name]
if !has {
return nil, fmt.Errorf("unsupported slot: %s", name)
}
return sl, nil
}
// AllSlots returns a map of all [Slot]s defined in the Grafana kindsys
// framework.
//
// TODO cache this for core context
func AllSlots(ctx *cue.Context) map[string]*Slot {
fw := CUEFramework(ctx)
slots := make(map[string]*Slot)
// Ignore err, can only happen if we change structure of fw files, and all we'd
// do is panic and that's what the next line will do anyway. Same for similar ignored
// errors later in this func
iter, _ := fw.LookupPath(cue.ParsePath("pluginTypeMetaSchema")).Fields(cue.Optional(true))
type nameopt struct {
name string
req bool
}
plugslots := make(map[string][]nameopt)
for iter.Next() {
plugin := iter.Selector().String()
iiter, _ := iter.Value().Fields(cue.Optional(true))
for iiter.Next() {
slotname := iiter.Selector().String()
plugslots[slotname] = append(plugslots[slotname], nameopt{
name: plugin,
req: !iiter.IsOptional(),
})
}
}
iter, _ = fw.LookupPath(cue.ParsePath("slots")).Fields(cue.Optional(true))
for iter.Next() {
n := iter.Selector().String()
sl := Slot{
name: n,
raw: iter.Value(),
plugins: make(map[string]bool),
}
for _, no := range plugslots[n] {
sl.plugins[no.name] = no.req
}
slots[n] = &sl
}
return slots
}