diff --git a/config/foundation_sdk.yaml b/config/foundation_sdk.yaml index 13eeb5df..a273f187 100644 --- a/config/foundation_sdk.yaml +++ b/config/foundation_sdk.yaml @@ -95,7 +95,6 @@ output: languages: - go: - go_mod: true package_root: '%go_package_root%' builder_templates: - '%__config_dir%/templates/go' diff --git a/package_templates/go/.gitignore b/package_templates/go/.gitignore new file mode 100644 index 00000000..61ead866 --- /dev/null +++ b/package_templates/go/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/package_templates/go/cmd/cli/convert/commands.go b/package_templates/go/cmd/cli/convert/commands.go new file mode 100644 index 00000000..b7864b1e --- /dev/null +++ b/package_templates/go/cmd/cli/convert/commands.go @@ -0,0 +1,86 @@ +package convert + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/grafana/grafana-foundation-sdk/go/cmd/cli/tools" + "github.com/grafana/grafana-foundation-sdk/go/cog" + "github.com/grafana/grafana-foundation-sdk/go/dashboard" + "github.com/urfave/cli/v2" +) + +type converter func(input []byte, hint string) (string, error) + +func Command() *cli.Command { + converters := map[string]converter{ + tools.KindDashboard: convertDashboard, + tools.KindPanel: convertPanel, + tools.KindQuery: convertQuery, + } + + return &cli.Command{ + Name: "convert", + Usage: "Convert Grafana resources to Go", + Description: "Converts a Grafana resource JSON from INPUT (or standard input) to Go.", + ArgsUsage: "[INPUT]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "kind", + Aliases: []string{"k"}, + Required: true, + }, + &cli.StringFlag{ + Name: "hint", + }, + }, + Action: func(cCtx *cli.Context) error { + kind := cCtx.String("kind") + converterFunc, found := converters[kind] + if !found { + return fmt.Errorf("unknown kind '%s'. Valid kinds are: %s", kind, strings.Join(tools.KnownKinds(), ", ")) + } + + input, err := tools.ReadFileOrStdin(cCtx.Args().First()) + if err != nil { + return err + } + + result, err := converterFunc(input, cCtx.String("hint")) + if err != nil { + return err + } + + fmt.Println(result) + return nil + }, + } +} + +func convertDashboard(input []byte, _ string) (string, error) { + dash := dashboard.Dashboard{} + if err := json.Unmarshal(input, &dash); err != nil { + return "", err + } + + return dashboard.DashboardConverter(dash), nil +} + +func convertPanel(input []byte, _ string) (string, error) { + panel := dashboard.Panel{} + if err := json.Unmarshal(input, &panel); err != nil { + return "", err + } + + return cog.ConvertPanelToCode(panel, panel.Type), nil +} + +func convertQuery(input []byte, hint string) (string, error) { + query, err := cog.UnmarshalDataquery(input, hint) + if err != nil { + return "", err + } + + return cog.ConvertDataqueryToCode(query), nil +} diff --git a/package_templates/go/cmd/cli/main.go b/package_templates/go/cmd/cli/main.go new file mode 100644 index 00000000..01f6452f --- /dev/null +++ b/package_templates/go/cmd/cli/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "os" + + "github.com/grafana/grafana-foundation-sdk/go/cmd/cli/convert" + "github.com/grafana/grafana-foundation-sdk/go/cmd/cli/validate" + "github.com/grafana/grafana-foundation-sdk/go/cog/plugins" + "github.com/urfave/cli/v2" +) + +func main() { + plugins.RegisterDefaultPlugins() + + app := &cli.App{ + Name: "grafana-foundation-sdk", + Version: "{{ .Extra.ReleaseBranch }}", + Commands: cli.Commands{ + convert.Command(), + validate.Command(), + }, + } + + if err := app.Run(os.Args); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + } +} diff --git a/package_templates/go/cmd/cli/tools/input.go b/package_templates/go/cmd/cli/tools/input.go new file mode 100644 index 00000000..ced840b6 --- /dev/null +++ b/package_templates/go/cmd/cli/tools/input.go @@ -0,0 +1,20 @@ +package tools + +import ( + "io" + "os" +) + +func ReadFileOrStdin(inputFile string) ([]byte, error) { + if inputFile == "" { + return io.ReadAll(os.Stdin) + } + + file, err := os.Open(inputFile) + if err != nil { + return nil, err + } + defer func() { _ = file.Close() }() + + return io.ReadAll(file) +} diff --git a/package_templates/go/cmd/cli/tools/kinds.go b/package_templates/go/cmd/cli/tools/kinds.go new file mode 100644 index 00000000..631eddc0 --- /dev/null +++ b/package_templates/go/cmd/cli/tools/kinds.go @@ -0,0 +1,17 @@ +package tools + +type Kind = string + +const ( + KindDashboard = "dashboard" + KindPanel = "panel" + KindQuery = "query" +) + +func KnownKinds() []Kind { + return []Kind{ + KindDashboard, + KindPanel, + KindQuery, + } +} diff --git a/package_templates/go/cmd/cli/validate/commands.go b/package_templates/go/cmd/cli/validate/commands.go new file mode 100644 index 00000000..dbd25780 --- /dev/null +++ b/package_templates/go/cmd/cli/validate/commands.go @@ -0,0 +1,96 @@ +package validate + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/grafana/grafana-foundation-sdk/go/cmd/cli/tools" + "github.com/grafana/grafana-foundation-sdk/go/cog" + "github.com/grafana/grafana-foundation-sdk/go/dashboard" + "github.com/urfave/cli/v2" +) + +type unmarshaller func(decoder *json.Decoder, input []byte, hint string) (any, error) + +var unmarshallers = map[string]unmarshaller{ + tools.KindDashboard: unmarshalDashboard, + tools.KindPanel: unmarshalPanel, + tools.KindQuery: unmarshalQuery, +} + +func Command() *cli.Command { + return &cli.Command{ + Name: "validate", + Usage: "Validate a Grafana resource", + Description: "Validates a resource JSON from INPUT (or standard input).", + ArgsUsage: "[INPUT]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "kind", + Aliases: []string{"k"}, + Required: true, + }, + &cli.StringFlag{ + Name: "hint", + }, + &cli.BoolFlag{ + Name: "strict", + Aliases: []string{"s"}, + }, + }, + Action: func(cCtx *cli.Context) error { + input, err := tools.ReadFileOrStdin(cCtx.Args().First()) + if err != nil { + return err + } + + decoder := json.NewDecoder(bytes.NewBuffer(input)) + + if cCtx.Bool("strict") { + decoder.DisallowUnknownFields() + } + + _, err = unmarshalKind(decoder, input, cCtx.String("kind"), cCtx.String("hint")) + + return err + }, + } +} + +func unmarshalKind(decoder *json.Decoder, input []byte, kind string, hint string) (any, error) { + unmarshallerFunc, found := unmarshallers[kind] + if !found { + return nil, fmt.Errorf("unknown kind '%s'. Valid kinds are: %s", kind, strings.Join(tools.KnownKinds(), ", ")) + } + + return unmarshallerFunc(decoder, input, hint) +} + +func unmarshalDashboard(decoder *json.Decoder, input []byte, _ string) (any, error) { + dash := dashboard.Dashboard{} + if err := decoder.Decode(&dash); err != nil { + return dash, err + } + + return dash, nil +} + +func unmarshalPanel(decoder *json.Decoder, input []byte, _ string) (any, error) { + panel := dashboard.Panel{} + if err := decoder.Decode(&panel); err != nil { + return panel, err + } + + return panel, nil +} + +func unmarshalQuery(decoder *json.Decoder, input []byte, hint string) (any, error) { + query, err := cog.UnmarshalDataquery(input, hint) + if err != nil { + return nil, err + } + + return query, nil +} diff --git a/package_templates/go/go.mod b/package_templates/go/go.mod new file mode 100644 index 00000000..91bdeeef --- /dev/null +++ b/package_templates/go/go.mod @@ -0,0 +1,11 @@ +module github.com/grafana/grafana-foundation-sdk/go + +go 1.21 + +require github.com/urfave/cli/v2 v2.27.4 + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect +) diff --git a/package_templates/go/go.sum b/package_templates/go/go.sum new file mode 100644 index 00000000..46bd4d82 --- /dev/null +++ b/package_templates/go/go.sum @@ -0,0 +1,8 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= +github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=