mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Core: Remove thema and kindsys dependencies (#84499)
* Move some thema code inside grafana * Use new codegen instead of thema for core kinds * Replace TS generator * Use new generator for go types * Remove thema from oapi generator * Remove thema from generators * Don't use kindsys/thema for core kinds * Remove kindsys/thema from plugins * Remove last thema related * Remove most of cuectx and move utils_ts into codegen. It also deletes wire dependency * Merge plugins generators * Delete thema dependency 🎉 * Fix CODEOWNERS * Fix package name * Fix TS output names * More path fixes * Fix mod codeowners * Use original plugin's name * Remove kindsys dependency 🎉 * Modify oapi schema and create an apply function to fix elasticsearch errors * cue.mod was deleted by mistake * Fix TS panels * sort imports * Fixing elasticsearch output * Downgrade oapi-codegen library * Update output ts files * More fixes * Restore old elasticsearch generated file and skip its generation. Remove core imports into plugins * More lint fixes * Add codeowners * restore embed.go file * Fix embed.go
This commit is contained in:
199
pkg/codegen/generators/openapi_generator.go
Normal file
199
pkg/codegen/generators/openapi_generator.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/ast"
|
||||
"cuelang.org/go/encoding/openapi"
|
||||
)
|
||||
|
||||
type OpenApiConfig struct {
|
||||
Config *openapi.Config
|
||||
IsGroup bool
|
||||
RootName string
|
||||
SubPath cue.Path
|
||||
}
|
||||
|
||||
func generateOpenAPI(v cue.Value, cfg *OpenApiConfig) (*ast.File, error) {
|
||||
if cfg == nil {
|
||||
return nil, fmt.Errorf("missing openapi configuration")
|
||||
}
|
||||
|
||||
if cfg.Config == nil {
|
||||
cfg.Config = &openapi.Config{}
|
||||
}
|
||||
|
||||
name, err := getSchemaName(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gen := &oapiGen{
|
||||
cfg: cfg,
|
||||
name: name,
|
||||
val: v.LookupPath(cue.ParsePath("lineage.schemas[0].schema")),
|
||||
subpath: cfg.SubPath,
|
||||
bpath: v.LookupPath(cue.ParsePath("lineage.schemas[0]")).Path(),
|
||||
}
|
||||
|
||||
declFunc := genSchema
|
||||
if cfg.IsGroup {
|
||||
declFunc = genGroup
|
||||
}
|
||||
|
||||
decls, err := declFunc(gen)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO recursively sort output to improve stability of output
|
||||
return &ast.File{
|
||||
Decls: []ast.Decl{
|
||||
ast.NewStruct(
|
||||
"openapi", ast.NewString("3.0.0"),
|
||||
"paths", ast.NewStruct(),
|
||||
"components", ast.NewStruct(
|
||||
"schemas", &ast.StructLit{Elts: decls},
|
||||
),
|
||||
),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type oapiGen struct {
|
||||
cfg *OpenApiConfig
|
||||
val cue.Value
|
||||
subpath cue.Path
|
||||
|
||||
// overall name for the generated oapi doc
|
||||
name string
|
||||
|
||||
// original NameFunc
|
||||
onf func(cue.Value, cue.Path) string
|
||||
|
||||
// full prefix path that leads up to the #SchemaDef, e.g. lin._sortedSchemas[0]
|
||||
bpath cue.Path
|
||||
}
|
||||
|
||||
func genGroup(gen *oapiGen) ([]ast.Decl, error) {
|
||||
ctx := gen.val.Context()
|
||||
iter, err := gen.val.Fields(cue.Definitions(true), cue.Optional(true))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("unreachable - should always be able to get iter for struct kinds: %w", err))
|
||||
}
|
||||
|
||||
var decls []ast.Decl
|
||||
for iter.Next() {
|
||||
val, sel := iter.Value(), iter.Selector()
|
||||
name := strings.Trim(sel.String(), "?#")
|
||||
|
||||
v := ctx.CompileString(fmt.Sprintf("#%s: _", name))
|
||||
defpath := cue.MakePath(cue.Def(name))
|
||||
defsch := v.FillPath(defpath, val)
|
||||
|
||||
cfgi := *gen.cfg.Config
|
||||
cfgi.NameFunc = func(val cue.Value, path cue.Path) string {
|
||||
return gen.nfSingle(val, path, defpath, name)
|
||||
}
|
||||
|
||||
part, err := openapi.Generate(defsch, &cfgi)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed generation for grouped field %s: %w", sel, err)
|
||||
}
|
||||
|
||||
decls = append(decls, getSchemas(part)...)
|
||||
}
|
||||
|
||||
return decls, nil
|
||||
}
|
||||
|
||||
func genSchema(gen *oapiGen) ([]ast.Decl, error) {
|
||||
hasSubpath := len(gen.cfg.SubPath.Selectors()) > 0
|
||||
name := sanitizeLabelString(gen.name)
|
||||
if gen.cfg.RootName != "" {
|
||||
name = gen.cfg.RootName
|
||||
} else if hasSubpath {
|
||||
sel := gen.cfg.SubPath.Selectors()
|
||||
name = sel[len(sel)-1].String()
|
||||
}
|
||||
|
||||
val := gen.val
|
||||
if hasSubpath {
|
||||
for i, sel := range gen.cfg.SubPath.Selectors() {
|
||||
if !gen.val.Allows(sel) {
|
||||
return nil, fmt.Errorf("subpath %q not present in schema", cue.MakePath(gen.cfg.SubPath.Selectors()[:i+1]...))
|
||||
}
|
||||
}
|
||||
val = val.LookupPath(gen.cfg.SubPath)
|
||||
}
|
||||
|
||||
v := gen.val.Context().CompileString(fmt.Sprintf("#%s: _", name))
|
||||
defpath := cue.MakePath(cue.Def(name))
|
||||
defsch := v.FillPath(defpath, val)
|
||||
|
||||
gen.cfg.Config.NameFunc = func(val cue.Value, path cue.Path) string {
|
||||
return gen.nfSingle(val, path, defpath, name)
|
||||
}
|
||||
|
||||
f, err := openapi.Generate(defsch.Eval(), gen.cfg.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getSchemas(f), nil
|
||||
}
|
||||
|
||||
// For generating a single, our NameFunc must:
|
||||
// - Eliminate any path prefixes on the element, both internal lineage and wrapping
|
||||
// - Replace the name "_#schema" with the desired name
|
||||
// - Call the user-provided NameFunc, if any
|
||||
// - Remove CUE markers like #, !, ?
|
||||
func (gen *oapiGen) nfSingle(val cue.Value, path, defpath cue.Path, name string) string {
|
||||
tpath := trimPathPrefix(trimThemaPathPrefix(path, gen.bpath), defpath)
|
||||
|
||||
if path.String() == "" || tpath.String() == defpath.String() {
|
||||
return name
|
||||
}
|
||||
|
||||
if val == gen.val {
|
||||
return ""
|
||||
}
|
||||
|
||||
if gen.onf != nil {
|
||||
return gen.onf(val, tpath)
|
||||
}
|
||||
return strings.Trim(tpath.String(), "?#")
|
||||
}
|
||||
|
||||
func getSchemas(f *ast.File) []ast.Decl {
|
||||
compos := orp(getFieldByLabel(f, "components"))
|
||||
schemas := orp(getFieldByLabel(compos.Value, "schemas"))
|
||||
return schemas.Value.(*ast.StructLit).Elts
|
||||
}
|
||||
|
||||
func orp[T any](t T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func trimThemaPathPrefix(p, base cue.Path) cue.Path {
|
||||
if !pathHasPrefix(p, base) {
|
||||
return p
|
||||
}
|
||||
|
||||
rest := p.Selectors()[len(base.Selectors()):]
|
||||
if len(rest) == 0 {
|
||||
return cue.Path{}
|
||||
}
|
||||
switch rest[0].String() {
|
||||
case "schema", "_#schema", "_join", "joinSchema":
|
||||
return cue.MakePath(rest[1:]...)
|
||||
default:
|
||||
return cue.MakePath(rest...)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user