Introduce coremodels framework (extracted from intent-api) (#47653)

* Copy over most of coremodel from intent-api branch

* Fix import paths

* Fix incorrect provider name

* Add root compgen file, fixup componentroot.yaml

* go mod tidy

* Remove compgen for now

* Add dashboard coremodel

* Remove datasource coremodel, for now

* Tweak comments on dashboard struct model

* devenv: add dashboard testing directly

* Fixup dashboard schema for openness, heatmap

* Update Thema to tip

* Fix wire/registry references

* Fix hclog version
This commit is contained in:
sam boyer
2022-04-14 14:54:35 -04:00
committed by GitHub
parent 758364e78b
commit 2a178bd73c
13 changed files with 890 additions and 44 deletions

View File

@@ -0,0 +1 @@
package coremodel

View File

@@ -0,0 +1,25 @@
package coremodel
import (
"github.com/grafana/thema"
)
// Interface is the primary coremodel interface that must be implemented by all
// Grafana coremodels. A coremodel is the foundational, canonical schema for
// some known-at-compile-time Grafana object.
//
// Currently, all Coremodels are expressed as Thema lineages.
type Interface interface {
// Lineage should return the canonical Thema lineage for the coremodel.
Lineage() thema.Lineage
// CurrentSchema should return the schema of the version that the Grafana backend
// is currently written against. (While Grafana can accept data from all
// older versions of the Thema schema, backend Go code is written against a
// single version for simplicity)
CurrentSchema() thema.Schema
// GoType should return a pointer to the Go struct type that corresponds to
// the Current() schema.
GoType() interface{}
}

View File

@@ -0,0 +1,78 @@
package coremodel
import (
"errors"
"fmt"
"sync"
"github.com/grafana/thema"
)
var (
// ErrModelAlreadyRegistered is returned when trying to register duplicate model to Registry.
ErrModelAlreadyRegistered = errors.New("error registering duplicate model")
)
// Registry is a registry of coremodel instances.
type Registry struct {
lock sync.RWMutex
models []Interface
modelIdx map[string]Interface
}
// NewRegistry returns a new Registry with the provided coremodel instances.
func NewRegistry(models ...Interface) (*Registry, error) {
r := &Registry{
models: make([]Interface, 0, len(models)),
modelIdx: make(map[string]Interface, len(models)),
}
if err := r.addModels(models); err != nil {
return nil, err
}
return r, nil
}
// Register adds coremodels to the Registry.
func (r *Registry) Register(models ...Interface) error {
return r.addModels(models)
}
// List returns all coremodels registered in this Registry.
func (r *Registry) List() []Interface {
r.lock.RLock()
defer r.lock.RUnlock()
return r.models
}
func (r *Registry) addModels(models []Interface) error {
r.lock.Lock()
defer r.lock.Unlock()
// Update model index and return an error if trying to register a duplicate.
for _, m := range models {
k := m.Lineage().Name()
// Ensure assignability first. TODO will this blow up for dashboards?
if err := thema.AssignableTo(m.CurrentSchema(), m.GoType()); err != nil {
return fmt.Errorf("%s schema version %v not assignable to provided Go type: %w", k, m.CurrentSchema().Version(), err)
}
if _, ok := r.modelIdx[k]; ok {
return ErrModelAlreadyRegistered
}
r.modelIdx[k] = m
}
// Remake model list.
// TODO: this can be more performant (proper resizing, maybe single loop with index building, etc.).
r.models = r.models[:0]
for _, m := range r.modelIdx {
r.models = append(r.models, m)
}
return nil
}

View File

@@ -0,0 +1,17 @@
package staticregistry
import (
"github.com/grafana/grafana/pkg/coremodel/dashboard"
"github.com/grafana/grafana/pkg/framework/coremodel"
)
// ProvideRegistry provides a simple static Registry for coremodels.
// Coremodels have to be manually added.
// TODO dynamism
func ProvideRegistry(
dashboard *dashboard.Coremodel,
) (*coremodel.Registry, error) {
return coremodel.NewRegistry(
dashboard,
)
}