coremodels: Combine static and generic registries (#53246)
* Stop generating non-dynamic registry code * Remove generic, errors, s/static/base/ * Sort during codegen, not runtime * Not a method call * Precisiate a comment * Remove generic registry, fix assignability test
This commit is contained in:
@@ -353,7 +353,7 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hs.Features.IsEnabled(featuremgmt.FlagValidateDashboardsOnSave) {
|
if hs.Features.IsEnabled(featuremgmt.FlagValidateDashboardsOnSave) {
|
||||||
cm := hs.CoremodelStaticRegistry.Dashboard()
|
cm := hs.Coremodels.Dashboard()
|
||||||
|
|
||||||
// Ideally, coremodel validation calls would be integrated into the web
|
// Ideally, coremodel validation calls would be integrated into the web
|
||||||
// framework. But this does the job for now.
|
// framework. But this does the job for now.
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ func TestGetHomeDashboard(t *testing.T) {
|
|||||||
SQLStore: mockstore.NewSQLStoreMock(),
|
SQLStore: mockstore.NewSQLStoreMock(),
|
||||||
preferenceService: prefService,
|
preferenceService: prefService,
|
||||||
dashboardVersionService: dashboardVersionService,
|
dashboardVersionService: dashboardVersionService,
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -141,8 +141,8 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
DashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
|
|
||||||
setUp := func() {
|
setUp := func() {
|
||||||
viewerRole := models.ROLE_VIEWER
|
viewerRole := models.ROLE_VIEWER
|
||||||
@@ -262,8 +262,8 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
DashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
|
|
||||||
setUp := func() {
|
setUp := func() {
|
||||||
origCanEdit := setting.ViewersCanEdit
|
origCanEdit := setting.ViewersCanEdit
|
||||||
@@ -903,8 +903,8 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
AccessControl: accesscontrolmock.New(),
|
AccessControl: accesscontrolmock.New(),
|
||||||
DashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
hs.callGetDashboard(sc)
|
hs.callGetDashboard(sc)
|
||||||
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
@@ -958,8 +958,8 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
|||||||
),
|
),
|
||||||
DashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
|
|
||||||
hs.callGetDashboard(sc)
|
hs.callGetDashboard(sc)
|
||||||
|
|
||||||
@@ -1024,8 +1024,8 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
|||||||
DashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
folderService: folderService,
|
folderService: folderService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||||
@@ -1057,8 +1057,8 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
|
|||||||
SQLStore: sqlmock,
|
SQLStore: sqlmock,
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||||
@@ -1096,8 +1096,8 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
|||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
|
Coremodels: registry.NewBase(),
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
sc.sqlStore = sqlStore
|
sc.sqlStore = sqlStore
|
||||||
@@ -1121,16 +1121,6 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDashboardCoremodel(t *testing.T) (*registry.Static, *registry.Generic) {
|
|
||||||
// TODO abstract and generalize this further for wider reuse
|
|
||||||
t.Helper()
|
|
||||||
sreg, err := registry.ProvideStatic()
|
|
||||||
require.NoError(t, err)
|
|
||||||
greg, err := registry.ProvideGeneric()
|
|
||||||
require.NoError(t, err)
|
|
||||||
return sreg, greg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) ToJSON() *simplejson.Json {
|
func (sc *scenarioContext) ToJSON() *simplejson.Json {
|
||||||
result := simplejson.New()
|
result := simplejson.New()
|
||||||
err := json.NewDecoder(sc.resp.Body).Decode(result)
|
err := json.NewDecoder(sc.resp.Body).Decode(result)
|
||||||
|
|||||||
@@ -171,10 +171,9 @@ type HTTPServer struct {
|
|||||||
dashboardVersionService dashver.Service
|
dashboardVersionService dashver.Service
|
||||||
PublicDashboardsApi *publicdashboardsApi.Api
|
PublicDashboardsApi *publicdashboardsApi.Api
|
||||||
starService star.Service
|
starService star.Service
|
||||||
|
Coremodels *registry.Base
|
||||||
playlistService playlist.Service
|
playlistService playlist.Service
|
||||||
apiKeyService apikey.Service
|
apiKeyService apikey.Service
|
||||||
CoremodelRegistry *registry.Generic
|
|
||||||
CoremodelStaticRegistry *registry.Static
|
|
||||||
kvStore kvstore.KVStore
|
kvStore kvstore.KVStore
|
||||||
secretsMigrator secrets.Migrator
|
secretsMigrator secrets.Migrator
|
||||||
userService user.Service
|
userService user.Service
|
||||||
@@ -211,7 +210,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service,
|
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service,
|
||||||
teamsPermissionsService accesscontrol.TeamPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService,
|
teamsPermissionsService accesscontrol.TeamPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService,
|
||||||
dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service,
|
dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service,
|
||||||
starService star.Service, csrfService csrf.Service, coremodelRegistry *registry.Generic, coremodelStaticRegistry *registry.Static,
|
starService star.Service, csrfService csrf.Service, coremodels *registry.Base,
|
||||||
playlistService playlist.Service, apiKeyService apikey.Service, kvStore kvstore.KVStore, secretsMigrator secrets.Migrator, remoteSecretsCheck secretsKV.UseRemoteSecretsPluginCheck,
|
playlistService playlist.Service, apiKeyService apikey.Service, kvStore kvstore.KVStore, secretsMigrator secrets.Migrator, remoteSecretsCheck secretsKV.UseRemoteSecretsPluginCheck,
|
||||||
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service) (*HTTPServer, error) {
|
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service) (*HTTPServer, error) {
|
||||||
web.Env = cfg.Env
|
web.Env = cfg.Env
|
||||||
@@ -294,10 +293,9 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
dashboardPermissionsService: dashboardPermissionsService,
|
dashboardPermissionsService: dashboardPermissionsService,
|
||||||
dashboardVersionService: dashboardVersionService,
|
dashboardVersionService: dashboardVersionService,
|
||||||
starService: starService,
|
starService: starService,
|
||||||
|
Coremodels: coremodels,
|
||||||
playlistService: playlistService,
|
playlistService: playlistService,
|
||||||
apiKeyService: apiKeyService,
|
apiKeyService: apiKeyService,
|
||||||
CoremodelRegistry: coremodelRegistry,
|
|
||||||
CoremodelStaticRegistry: coremodelStaticRegistry,
|
|
||||||
kvStore: kvStore,
|
kvStore: kvStore,
|
||||||
PublicDashboardsApi: publicDashboardsApi,
|
PublicDashboardsApi: publicDashboardsApi,
|
||||||
secretsMigrator: secretsMigrator,
|
secretsMigrator: secretsMigrator,
|
||||||
|
|||||||
+20
-55
@@ -281,8 +281,8 @@ func (m modelReplacer) replacePrefix(str string) string {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateCoremodelRegistry produces Go files that define a static registry
|
// GenerateCoremodelRegistry produces Go files that define a registry with
|
||||||
// with references to all the Go code that is expected to be generated from the
|
// references to all the Go code that is expected to be generated from the
|
||||||
// provided lineages.
|
// provided lineages.
|
||||||
func GenerateCoremodelRegistry(path string, ecl []*ExtractedLineage) (WriteDiffer, error) {
|
func GenerateCoremodelRegistry(path string, ecl []*ExtractedLineage) (WriteDiffer, error) {
|
||||||
var cml []tplVars
|
var cml []tplVars
|
||||||
@@ -409,6 +409,7 @@ var tmplRegistry = template.Must(template.New("registry").Parse(`
|
|||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
@@ -419,25 +420,17 @@ import (
|
|||||||
"github.com/grafana/thema"
|
"github.com/grafana/thema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CoremodelSet contains all of the wire-style providers related to coremodels.
|
// Base is a registry of coremodel.Interface. It provides two modes for accessing
|
||||||
var CoremodelSet = wire.NewSet(
|
// coremodels: individually via literal named methods, or as a slice returned from All().
|
||||||
ProvideStatic,
|
//
|
||||||
ProvideGeneric,
|
// Prefer the individual named methods for use cases where the particular coremodel(s) that
|
||||||
)
|
// are needed are known to the caller. For example, a dashboard linter can know that it
|
||||||
|
// specifically wants the dashboard coremodel.
|
||||||
var (
|
//
|
||||||
staticOnce sync.Once
|
// Prefer All() when performing operations generically across all coremodels. For example,
|
||||||
defaultStatic *Static
|
// a validation HTTP middleware for any coremodel-schematized object type.
|
||||||
defaultStaticErr error
|
type Base struct {
|
||||||
|
all []coremodel.Interface
|
||||||
genericOnce sync.Once
|
|
||||||
defaultGeneric *Generic
|
|
||||||
defaultGenericErr error
|
|
||||||
)
|
|
||||||
|
|
||||||
// Static is a registry that provides access to individual coremodels via
|
|
||||||
// explicit method calls, to aid with static analysis.
|
|
||||||
type Static struct {
|
|
||||||
{{- range .Coremodels }}
|
{{- range .Coremodels }}
|
||||||
{{ .Name }} *{{ .Name }}.Coremodel{{end}}
|
{{ .Name }} *{{ .Name }}.Coremodel{{end}}
|
||||||
}
|
}
|
||||||
@@ -451,51 +444,23 @@ var (
|
|||||||
{{range .Coremodels }}
|
{{range .Coremodels }}
|
||||||
// {{ .TitleName }} returns the {{ .Name }} coremodel. The return value is guaranteed to
|
// {{ .TitleName }} returns the {{ .Name }} coremodel. The return value is guaranteed to
|
||||||
// implement coremodel.Interface.
|
// implement coremodel.Interface.
|
||||||
func (s *Static) {{ .TitleName }}() *{{ .Name }}.Coremodel {
|
func (s *Base) {{ .TitleName }}() *{{ .Name }}.Coremodel {
|
||||||
return s.{{ .Name }}
|
return s.{{ .Name }}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
func provideStatic(lib *thema.Library) (*Static, error) {
|
func doProvideBase(lib thema.Library) *Base {
|
||||||
if lib == nil {
|
|
||||||
staticOnce.Do(func() {
|
|
||||||
defaultStatic, defaultStaticErr = doProvideStatic(cuectx.ProvideThemaLibrary())
|
|
||||||
})
|
|
||||||
return defaultStatic, defaultStaticErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return doProvideStatic(*lib)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doProvideStatic(lib thema.Library) (*Static, error) {
|
|
||||||
var err error
|
var err error
|
||||||
reg := &Static{}
|
reg := &Base{}
|
||||||
|
|
||||||
{{range .Coremodels }}
|
{{range .Coremodels }}
|
||||||
reg.{{ .Name }}, err = {{ .Name }}.New(lib)
|
reg.{{ .Name }}, err = {{ .Name }}.New(lib)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
panic(fmt.Sprintf("error while initializing {{ .Name }} coremodel: %s", err))
|
||||||
}
|
}
|
||||||
|
reg.all = append(reg.all, reg.{{ .Name }})
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
return reg, nil
|
return reg
|
||||||
}
|
|
||||||
|
|
||||||
func provideGeneric() (*Generic, error) {
|
|
||||||
ereg, err := provideStatic(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
genericOnce.Do(func() {
|
|
||||||
defaultGeneric, defaultGenericErr = doProvideGeneric(ereg)
|
|
||||||
})
|
|
||||||
return defaultGeneric, defaultGenericErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func doProvideGeneric(ereg *Static) (*Generic, error) {
|
|
||||||
return NewRegistry({{ range .Coremodels }}
|
|
||||||
ereg.{{ .TitleName }}(),{{ end }}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
`))
|
`))
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"cuelang.org/go/cue/cuecontext"
|
"cuelang.org/go/cue/cuecontext"
|
||||||
@@ -59,6 +60,9 @@ func main() {
|
|||||||
lins = append(lins, lin)
|
lins = append(lins, lin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Slice(lins, func(i, j int) bool {
|
||||||
|
return lins[i].Lineage.Name() < lins[j].Lineage.Name()
|
||||||
|
})
|
||||||
|
|
||||||
wd := gcgen.NewWriteDiffer()
|
wd := gcgen.NewWriteDiffer()
|
||||||
for _, ls := range lins {
|
for _, ls := range lins {
|
||||||
|
|||||||
@@ -8,12 +8,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestSchemaAssignability(t *testing.T) {
|
func TestSchemaAssignability(t *testing.T) {
|
||||||
reg, err := registry.ProvideGeneric()
|
reg := registry.NewBase()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cm := range reg.List() {
|
for _, cm := range reg.All() {
|
||||||
tcm := cm
|
tcm := cm
|
||||||
t.Run(tcm.Lineage().Name(), func(t *testing.T) {
|
t.Run(tcm.Lineage().Name(), func(t *testing.T) {
|
||||||
err := thema.AssignableTo(tcm.CurrentSchema(), tcm.GoType())
|
err := thema.AssignableTo(tcm.CurrentSchema(), tcm.GoType())
|
||||||
|
|||||||
@@ -1,40 +1,59 @@
|
|||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
"github.com/grafana/grafana/pkg/cuectx"
|
||||||
|
"github.com/grafana/grafana/pkg/framework/coremodel"
|
||||||
"github.com/grafana/thema"
|
"github.com/grafana/thema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProvideStatic provides access to individual coremodels via explicit method calls.
|
// CoremodelSet contains all of the wire-style providers related to coremodels.
|
||||||
|
var CoremodelSet = wire.NewSet(
|
||||||
|
NewBase,
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewBase provides a registry of all coremodels, without any composition of
|
||||||
|
// plugin-defined schemas.
|
||||||
//
|
//
|
||||||
// Prefer this to the ProvideGeneric type when your code works with known,
|
// The returned registry will use the default Grafana thema.Library, defined in
|
||||||
// specific coremodels(s), rather than generically across all of them. This
|
// pkg/cuectx. If you need control over the thema.Library used by the coremodel
|
||||||
// allows standard Go static analysis tools to determine which code is depending
|
// lineages, use NewBaseWithLib instead.
|
||||||
// on particular coremodels.
|
func NewBase() *Base {
|
||||||
//
|
return provideBase(nil)
|
||||||
// This will use the default Grafana thema.Library, defined in pkg/cuectx, which
|
|
||||||
// will avoid duplicate parsing of Thema CUE schemas. If you need control over the
|
|
||||||
// thema.Library in use, use ProvideStaticWithLib instead.
|
|
||||||
func ProvideStatic() (*Static, error) {
|
|
||||||
return provideStatic(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvideStaticWithLib is the same as ProvideStatic, but
|
// NewBaseWithLib is the same as NewBase, but allows control over the
|
||||||
// allows control over the thema.Library used to initialize the underlying
|
// thema.Library used to initialize the underlying coremodels.
|
||||||
// coremodels.
|
|
||||||
//
|
//
|
||||||
// Prefer ProvideStatic unless you absolutely need this control.
|
// Prefer NewBase unless you absolutely need this control.
|
||||||
func ProvideStaticWithLib(lib thema.Library) (*Static, error) {
|
func NewBaseWithLib(lib thema.Library) *Base {
|
||||||
return provideStatic(&lib)
|
return provideBase(&lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvideGeneric provides a simple Generic registry of all coremodels.
|
var (
|
||||||
//
|
baseOnce sync.Once
|
||||||
// Prefer this to the static ProvideStatic when your code needs to
|
defaultBase *Base
|
||||||
// work with all coremodels generically, rather than specific coremodels.
|
)
|
||||||
func ProvideGeneric() (*Generic, error) {
|
|
||||||
return provideGeneric()
|
func provideBase(lib *thema.Library) *Base {
|
||||||
|
if lib == nil {
|
||||||
|
baseOnce.Do(func() {
|
||||||
|
defaultBase = doProvideBase(cuectx.ProvideThemaLibrary())
|
||||||
|
})
|
||||||
|
return defaultBase
|
||||||
|
}
|
||||||
|
|
||||||
|
return doProvideBase(*lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE - no ProvideRegistryWithLib is defined because there are no anticipated
|
// All returns a slice of all registered coremodels.
|
||||||
// cases where a caller would need to operate generically across all coremodels,
|
//
|
||||||
// and control the library they're initialized with. If that changes, add one.
|
// Prefer this method when operating generically across all coremodels.
|
||||||
|
//
|
||||||
|
// The returned slice is sorted lexicographically by coremodel name. It should
|
||||||
|
// not be modified.
|
||||||
|
func (s *Base) All() []coremodel.Interface {
|
||||||
|
return s.all
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
package registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/framework/coremodel"
|
|
||||||
"github.com/grafana/thema"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrModelAlreadyRegistered is returned when trying to register duplicate model to Generic.
|
|
||||||
ErrModelAlreadyRegistered = errors.New("error registering duplicate model")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generic is a registry of coremodel instances. It is intended for use in cases where
|
|
||||||
// generic operations limited to coremodel.Interface are being performed.
|
|
||||||
type Generic struct {
|
|
||||||
lock sync.RWMutex
|
|
||||||
models []coremodel.Interface
|
|
||||||
modelIdx map[string]coremodel.Interface
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRegistry returns a new Generic with the provided coremodel instances.
|
|
||||||
func NewRegistry(models ...coremodel.Interface) (*Generic, error) {
|
|
||||||
r := &Generic{
|
|
||||||
models: make([]coremodel.Interface, 0, len(models)),
|
|
||||||
modelIdx: make(map[string]coremodel.Interface, len(models)),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := r.addModels(models); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register adds coremodels to the Generic.
|
|
||||||
func (r *Generic) Register(models ...coremodel.Interface) error {
|
|
||||||
return r.addModels(models)
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns all coremodels registered in this Generic.
|
|
||||||
func (r *Generic) List() []coremodel.Interface {
|
|
||||||
r.lock.RLock()
|
|
||||||
defer r.lock.RUnlock()
|
|
||||||
|
|
||||||
return r.models
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Generic) addModels(models []coremodel.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
|
|
||||||
}
|
|
||||||
@@ -6,35 +6,24 @@
|
|||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/wire"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/coremodel/dashboard"
|
"github.com/grafana/grafana/pkg/coremodel/dashboard"
|
||||||
"github.com/grafana/grafana/pkg/cuectx"
|
|
||||||
"github.com/grafana/grafana/pkg/framework/coremodel"
|
"github.com/grafana/grafana/pkg/framework/coremodel"
|
||||||
"github.com/grafana/thema"
|
"github.com/grafana/thema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CoremodelSet contains all of the wire-style providers related to coremodels.
|
// Base is a registry of coremodel.Interface. It provides two modes for accessing
|
||||||
var CoremodelSet = wire.NewSet(
|
// coremodels: individually via literal named methods, or as a slice returned from All().
|
||||||
ProvideStatic,
|
//
|
||||||
ProvideGeneric,
|
// Prefer the individual named methods for use cases where the particular coremodel(s) that
|
||||||
)
|
// are needed are known to the caller. For example, a dashboard linter can know that it
|
||||||
|
// specifically wants the dashboard coremodel.
|
||||||
var (
|
//
|
||||||
staticOnce sync.Once
|
// Prefer All() when performing operations generically across all coremodels. For example,
|
||||||
defaultStatic *Static
|
// a validation HTTP middleware for any coremodel-schematized object type.
|
||||||
defaultStaticErr error
|
type Base struct {
|
||||||
|
all []coremodel.Interface
|
||||||
genericOnce sync.Once
|
|
||||||
defaultGeneric *Generic
|
|
||||||
defaultGenericErr error
|
|
||||||
)
|
|
||||||
|
|
||||||
// Static is a registry that provides access to individual coremodels via
|
|
||||||
// explicit method calls, to aid with static analysis.
|
|
||||||
type Static struct {
|
|
||||||
dashboard *dashboard.Coremodel
|
dashboard *dashboard.Coremodel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,47 +34,19 @@ var (
|
|||||||
|
|
||||||
// Dashboard returns the dashboard coremodel. The return value is guaranteed to
|
// Dashboard returns the dashboard coremodel. The return value is guaranteed to
|
||||||
// implement coremodel.Interface.
|
// implement coremodel.Interface.
|
||||||
func (s *Static) Dashboard() *dashboard.Coremodel {
|
func (s *Base) Dashboard() *dashboard.Coremodel {
|
||||||
return s.dashboard
|
return s.dashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
func provideStatic(lib *thema.Library) (*Static, error) {
|
func doProvideBase(lib thema.Library) *Base {
|
||||||
if lib == nil {
|
|
||||||
staticOnce.Do(func() {
|
|
||||||
defaultStatic, defaultStaticErr = doProvideStatic(cuectx.ProvideThemaLibrary())
|
|
||||||
})
|
|
||||||
return defaultStatic, defaultStaticErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return doProvideStatic(*lib)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doProvideStatic(lib thema.Library) (*Static, error) {
|
|
||||||
var err error
|
var err error
|
||||||
reg := &Static{}
|
reg := &Base{}
|
||||||
|
|
||||||
reg.dashboard, err = dashboard.New(lib)
|
reg.dashboard, err = dashboard.New(lib)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
panic(fmt.Sprintf("error while initializing dashboard coremodel: %s", err))
|
||||||
}
|
}
|
||||||
|
reg.all = append(reg.all, reg.dashboard)
|
||||||
|
|
||||||
return reg, nil
|
return reg
|
||||||
}
|
|
||||||
|
|
||||||
func provideGeneric() (*Generic, error) {
|
|
||||||
ereg, err := provideStatic(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
genericOnce.Do(func() {
|
|
||||||
defaultGeneric, defaultGenericErr = doProvideGeneric(ereg)
|
|
||||||
})
|
|
||||||
return defaultGeneric, defaultGenericErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func doProvideGeneric(ereg *Static) (*Generic, error) {
|
|
||||||
return NewRegistry(
|
|
||||||
ereg.Dashboard(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user