mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -06:00
473898e47c
* 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
200 lines
4.5 KiB
Go
200 lines
4.5 KiB
Go
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...)
|
|
}
|
|
}
|