From 4e92e6084038992e14b420dc3e697961ecab9728 Mon Sep 17 00:00:00 2001 From: lekko-jonathan <150070021+lekko-jonathan@users.noreply.github.com> Date: Tue, 2 Jan 2024 11:28:56 -0800 Subject: [PATCH] Generate static file from config repo (#270) * Generate static file from config repo * Yell more at linter --- .golangci.yml | 4 ++ cmd/lekko/main.go | 175 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index 031a0b99..042b9e37 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,6 +7,10 @@ linters-settings: forbid: - "^print$" - "^println$" +issues: + exclude-rules: + - path: cmd/lekko/main.go + text: "File is not `goimports`-ed" linters: enable: - asciicheck diff --git a/cmd/lekko/main.go b/cmd/lekko/main.go index 9e974dc0..13c727b7 100644 --- a/cmd/lekko/main.go +++ b/cmd/lekko/main.go @@ -15,13 +15,19 @@ package main import ( + // "bytes" "context" "fmt" "os" "path/filepath" + // "regexp" + "sort" "strconv" + // "strings" + "text/template" bffv1beta1 "buf.build/gen/go/lekkodev/cli/protocolbuffers/go/lekko/bff/v1beta1" + featurev1beta1 "buf.build/gen/go/lekkodev/cli/protocolbuffers/go/lekko/feature/v1beta1" "github.com/AlecAivazis/survey/v2" "github.com/bufbuild/connect-go" "github.com/lekkodev/cli/pkg/feature" @@ -35,6 +41,7 @@ import ( "github.com/mitchellh/go-homedir" "github.com/pkg/errors" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" "github.com/spf13/cobra" ) @@ -65,6 +72,9 @@ func main() { experimentalCmd.AddCommand(parseCmd()) experimentalCmd.AddCommand(cleanupCmd) experimentalCmd.AddCommand(formatCmd()) + // code generation + genCmd.AddCommand(genGoCmd()) + experimentalCmd.AddCommand(genCmd) rootCmd.AddCommand(experimentalCmd) logging.InitColors() @@ -86,6 +96,171 @@ func rootCmd() *cobra.Command { return cmd } +var genCmd = &cobra.Command{ + Use: "gen", + Short: "generate library code from configs", +} + +/* +func genGoForFeature(f *featurev1beta1.Feature, ns string) (string, error) { + const defaultTemplateBody = ` +// {{$.Description}} +func (c *LekkoClient) {{$.FuncName}}(ctx context.Context) ({{$.RetType}}, error) { + return c.{{$.GetFunction}}(ctx, "{{$.Namespace}}", "{{$.Key}}") +} +` + // protoc --go_opt=Mdefault/config/v1beta1/example.proto=lekko.com/default/config/v1beta1 --proto_path=../teflon/config/proto --go_out=gen --go_opt=paths=source_relative default/config/v1beta1/example.proto + const protoTemplateBody = ` +// {{$.Description}} +func (c *LekkoClient) {{$.FuncName}}(ctx context.Context) (*{{$.RetType}}, error) { + result := &{{$.RetType}}{} + err := c.{{$.GetFunction}}(ctx, "{{$.Namespace}}", "{{$.Key}}", result) + if err != nil { + return result, err + } + return result, nil +} + ` + const jsonTemplateBody = ` +// {{$.Description}} +func (c *LekkoClient) {{$.FuncName}}(ctx context.Context, result interface{}) error { + return c.{{$.GetFunction}}(ctx, "{{$.Namespace}}", "{{$.Key}}", result) +} +` + var funcNameBuilder strings.Builder + funcNameBuilder.WriteString("Get") + for _, word := range regexp.MustCompile("[_-]+").Split(f.Key, -1) { + funcNameBuilder.WriteString(strings.ToUpper(word[:1]) + word[1:]) + } + funcName := funcNameBuilder.String() + var retType string + var getFunction string + templateBody := defaultTemplateBody + switch f.Type { + case 1: + retType = "bool" + getFunction = "GetBool" + case 2: + retType = "int64" + getFunction = "GetInt" + case 3: + retType = "float64" + getFunction = "GetFloat" + case 4: + retType = "string" + getFunction = "GetString" + case 5: + getFunction = "GetJSON" + templateBody = jsonTemplateBody + case 6: + getFunction = "GetProto" + templateBody = protoTemplateBody + typeParts := strings.Split(f.Tree.Default.TypeUrl, ".") + retType = strings.Join(typeParts[len(typeParts)-2:], ".") + } + + data := struct { + Description string + FuncName string + GetFunction string + RetType string + Namespace string + Key string + }{ + f.Description, + funcName, + getFunction, + retType, + ns, + f.Key, + } + templ, err := template.New("go func").Parse(templateBody) + if err != nil { + return "", err + } + var ret bytes.Buffer + err = templ.Execute(&ret, data) + return ret.String(), err +} +*/ + +func genGoCmd() *cobra.Command { + var ns string + var wd string + var of string + cmd := &cobra.Command{ + Use: "go", + Short: "generate Go library code from configs", + RunE: func(cmd *cobra.Command, args []string) error { + rs := secrets.NewSecretsOrFail() + r, err := repo.NewLocal(wd, rs) + if err != nil { + return errors.Wrap(err, "new repo") + } + ffs, err := r.GetFeatureFiles(cmd.Context(), ns) + if err != nil { + return err + } + sort.SliceStable(ffs, func(i, j int) bool { + return ffs[i].CompiledProtoBinFileName < ffs[j].CompiledProtoBinFileName + }) + var protoAsByteStrings []string + // var codeStrings []string + for _, ff := range ffs { + fff, err := os.ReadFile(wd + "/" + ns + "/" + ff.CompiledProtoBinFileName) + if err != nil { + return err + } + f := &featurev1beta1.Feature{} + err = proto.Unmarshal(fff, f) + if err != nil { + return err + } + // codeString, err := genGoForFeature(f, ns) + // if err != nil { + // return err + // } + protoAsBytes := fmt.Sprintf("\t\t\"%s\": []byte{", f.Key) + for idx, b := range fff { + if idx%16 == 0 { + protoAsBytes += "\n\t\t\t" + } else { + protoAsBytes += " " + } + protoAsBytes += fmt.Sprintf("0x%02x,", b) + } + protoAsBytes += "\n\t\t},\n" + protoAsByteStrings = append(protoAsByteStrings, protoAsBytes) + // codeStrings = append(codeStrings, codeString) + } + const templateBody = `package lekko + +var StaticConfig = map[string]map[string][]byte{ + "{{$.Namespace}}": { +{{range $.ProtoAsByteStrings}}{{ . }}{{end}} }, +} +` + f, err := os.Create(of) + if err != nil { + return err + } + data := struct { + Namespace string + ProtoAsByteStrings []string + }{ + ns, + protoAsByteStrings, + } + templ := template.Must(template.New("").Parse(templateBody)) + return templ.Execute(f, data) + }, + } + cmd.Flags().StringVarP(&ns, "namespace", "n", "default", "namespace to generate code from") + cmd.Flags().StringVarP(&wd, "config-path", "c", ".", "path to configuration repository") + cmd.Flags().StringVarP(&of, "output", "o", "./lekko.go", "output file") + return cmd +} + func formatCmd() *cobra.Command { var verbose bool cmd := &cobra.Command{