Skip to content

Commit 6538b6b

Browse files
Config Arguments from proto in TS (#414)
* not even sure.. * gen the sig from the cli thing * improve comment * improvements --------- Co-authored-by: lekko-app[bot] <108442683+lekko-app[bot]@users.noreply.github.com>
1 parent f2fac7e commit 6538b6b

File tree

3 files changed

+116
-13
lines changed

3 files changed

+116
-13
lines changed

cmd/lekko/sync.go

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"golang.org/x/mod/modfile"
2828

29+
"github.com/iancoleman/strcase"
2930
"github.com/lainio/err2"
3031
"github.com/lainio/err2/try"
3132
"github.com/pkg/errors"
@@ -36,6 +37,7 @@ import (
3637
"google.golang.org/protobuf/reflect/protoreflect"
3738
"google.golang.org/protobuf/reflect/protoregistry"
3839
"google.golang.org/protobuf/types/descriptorpb"
40+
"google.golang.org/protobuf/types/dynamicpb"
3941
"google.golang.org/protobuf/types/known/anypb"
4042

4143
bffv1beta1 "buf.build/gen/go/lekkodev/cli/protocolbuffers/go/lekko/bff/v1beta1"
@@ -313,7 +315,7 @@ func isSameTS(ctx context.Context, existing map[string]map[string]*featurev1beta
313315
dot := try.To1(dotlekko.ReadDotLekko(""))
314316
cmd := exec.Command("npx", "ts-to-proto", "--lekko-dir", dot.LekkoPath) // #nosec G204
315317
cmd.Dir = root
316-
nsString, err := cmd.CombinedOutput()
318+
nsString, err := cmd.CombinedOutput() // The output format of ts-to-proto changed, but no one is using this right now..
317319
if err != nil {
318320
fmt.Println("Error running ts-to-proto")
319321
return false, err
@@ -477,8 +479,18 @@ func convertLangCmd() *cobra.Command {
477479
if err != nil {
478480
panic(err)
479481
}
480-
privateFile := goToGo(ctx, f)
481-
fmt.Println(privateFile)
482+
483+
if inputLang == "proto-json" && outputLang == "ts" {
484+
lines := strings.Split(string(f), "\n")
485+
out, err := ProtoJSONToTS([]byte(lines[0]), []byte(lines[1]))
486+
if err != nil {
487+
panic(err)
488+
}
489+
fmt.Println(out)
490+
} else {
491+
privateFile := goToGo(ctx, f)
492+
fmt.Println(privateFile)
493+
}
482494
return nil
483495
},
484496
}
@@ -731,3 +743,81 @@ func WriteToRepo(ctx context.Context, r repo.ConfigurationRepository, types *pro
731743
}
732744
return nil
733745
}
746+
747+
func ProtoJSONToTS(nsString []byte, fdString []byte) (string, error) {
748+
registry, err := prototypes.RegisterDynamicTypes(nil)
749+
if err != nil {
750+
panic(err)
751+
}
752+
var fileDescriptorProto descriptorpb.FileDescriptorProto
753+
err = protojson.UnmarshalOptions{Resolver: registry.Types}.Unmarshal(fdString, &fileDescriptorProto)
754+
if err != nil {
755+
return "", err
756+
}
757+
// This is partly duplicated from pkg/sync/golang:RegisterDescriptor
758+
fileDescriptor, err := protodesc.NewFile(&fileDescriptorProto, nil)
759+
if err != nil {
760+
return "", err
761+
}
762+
var interfaceStrings []string
763+
764+
for i := 0; i < fileDescriptor.Messages().Len(); i++ {
765+
messageDescriptor := fileDescriptor.Messages().Get(i)
766+
dynamicMessage := dynamicpb.NewMessage(messageDescriptor)
767+
768+
err := registry.Types.RegisterMessage(dynamicMessage.Type())
769+
if err != nil {
770+
return "", err
771+
}
772+
if !strings.HasSuffix(string(messageDescriptor.Name()), "Args") {
773+
face, err := gen.GetTSInterface(messageDescriptor)
774+
if err != nil {
775+
panic(err)
776+
}
777+
interfaceStrings = append(interfaceStrings, face+"\n")
778+
}
779+
}
780+
var namespaces bffv1beta1.NamespaceContents
781+
err = protojson.UnmarshalOptions{Resolver: registry.Types}.Unmarshal(nsString, &namespaces)
782+
if err != nil {
783+
return "", err
784+
}
785+
gen.TypeRegistry = registry.Types
786+
var featureStrings []string
787+
for _, namespace := range namespaces.Namespaces {
788+
for _, c := range namespace.Configs {
789+
f := c.StaticFeature
790+
if f.GetTree().GetDefault() != nil {
791+
f.Tree.DefaultNew = anyToLekkoAny(f.Tree.Default)
792+
}
793+
for _, c := range f.GetTree().GetConstraints() {
794+
if c.GetValue() != nil {
795+
c.ValueNew = anyToLekkoAny(c.Value)
796+
}
797+
}
798+
799+
var ourParameters string
800+
sigType, err := gen.TypeRegistry.FindMessageByName(protoreflect.FullName(namespace.Name + ".config.v1beta1." + strcase.ToCamel(f.Key) + "Args"))
801+
if err == nil {
802+
d := sigType.Descriptor()
803+
var varNames []string
804+
var fields []string
805+
for i := 0; i < d.Fields().Len(); i++ {
806+
f := d.Fields().Get(i)
807+
t := gen.FieldDescriptorToTS(f)
808+
fields = append(fields, fmt.Sprintf("%s?: %s;", strcase.ToLowerCamel(f.TextName()), t))
809+
varNames = append(varNames, strcase.ToLowerCamel(f.TextName()))
810+
}
811+
812+
ourParameters = fmt.Sprintf("{%s}: {%s}", strings.Join(varNames, ", "), strings.Join(fields, " "))
813+
}
814+
815+
fs, err := gen.GenTSForFeature(f, namespace.Name, ourParameters)
816+
featureStrings = append(featureStrings, fs)
817+
if err != nil {
818+
return "", err
819+
}
820+
}
821+
}
822+
return strings.Join(append(interfaceStrings, featureStrings...), "\n"), nil
823+
}

cmd/lekko/sync_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,16 @@ func Test_goToGo(t *testing.T) {
127127
}
128128
})
129129
}
130+
131+
/*
132+
Here to just run the code, but I really don't want to have an unrelated node thing as a dep for the test
133+
func Test_ProtoJsonToTs(t *testing.T) {
134+
t.Run("simple", func(t *testing.T) {
135+
nsStr := `{"namespaces":[{"name":"default","configs":[{"static_feature":{"key":"banner-config","description":"","tree":{"default":{"@type":"type.googleapis.com/default.config.v1beta1.BannerConfig"},"constraints":[{"value":{"cta":{"external":true,"text":"Learn more","url":"https://www.lekko.com/"},"text":"This is a development only example of a banner on the login page","@type":"type.googleapis.com/default.config.v1beta1.BannerConfig"},"ruleAstNew":{"logicalExpression":{"rules":[{"atom":{"contextKey":"pathname","comparisonValue":"/login","comparisonOperator":"COMPARISON_OPERATOR_EQUALS"}},{"atom":{"contextKey":"env","comparisonValue":"development","comparisonOperator":"COMPARISON_OPERATOR_EQUALS"}}],"logicalOperator":"LOGICAL_OPERATOR_AND"}}},{"value":{"cta":{"external":true,"text":"Learn more","url":"https://www.lekko.com/"},"text":"A test banner for a particular repo main page","@type":"type.googleapis.com/default.config.v1beta1.BannerConfig"},"ruleAstNew":{"atom":{"contextKey":"pathname","comparisonValue":"/teams/lekko-staging/repositories/lekkodev/plugins/branches/main","comparisonOperator":"COMPARISON_OPERATOR_EQUALS"}}}]},"type":"FEATURE_TYPE_PROTO"}}]}]}`
136+
fdStr := `{"name":"lekko.proto","package":"default.config.v1beta1","dependency":[],"messageType":[{"name":"BannerConfig","field":[{"name":"text","number":1,"type":"TYPE_STRING","typeName":""},{"name":"cta","number":2,"type":"TYPE_MESSAGE","typeName":"default.config.v1beta1.BannerConfig.Cta"},{"name":"permanent","number":3,"type":"TYPE_BOOL","typeName":""}],"nestedType":[{"name":"Cta","field":[{"name":"text","number":1,"type":"TYPE_STRING","typeName":""},{"name":"url","number":2,"type":"TYPE_STRING","typeName":""},{"name":"external","number":3,"type":"TYPE_BOOL","typeName":""}],"nestedType":[],"enumType":[],"extensionRange":[],"extension":[],"oneofDecl":[],"reservedRange":[],"reservedName":[]}],"enumType":[],"extensionRange":[],"extension":[],"oneofDecl":[],"reservedRange":[],"reservedName":[]},{"name":"RemoteTemplates","field":[{"name":"templates","number":1,"label":"LABEL_REPEATED","type":"TYPE_MESSAGE","typeName":"default.config.v1beta1.RemoteTemplates.Templates"}],"nestedType":[{"name":"Templates","field":[{"name":"code","number":1,"type":"TYPE_STRING","typeName":""},{"name":"feature_type","number":2,"type":"TYPE_STRING","typeName":""},{"name":"name","number":3,"type":"TYPE_STRING","typeName":""}],"nestedType":[],"enumType":[],"extensionRange":[],"extension":[],"oneofDecl":[],"reservedRange":[],"reservedName":[]}],"enumType":[],"extensionRange":[],"extension":[],"oneofDecl":[],"reservedRange":[],"reservedName":[]},{"name":"BannerConfigArgs","field":[{"name":"env","number":1,"type":"TYPE_STRING","typeName":""},{"name":"pathname","number":2,"type":"TYPE_STRING","typeName":""}],"nestedType":[],"enumType":[],"extensionRange":[],"extension":[],"oneofDecl":[],"reservedRange":[],"reservedName":[]}],"enumType":[],"service":[],"extension":[],"publicDependency":[],"weakDependency":[],"syntax":"proto3"}`
137+
out, err := ProtoJSONToTS([]byte(nsStr), []byte(fdStr))
138+
fmt.Println(err)
139+
fmt.Println(out)
140+
})
141+
}
142+
*/

pkg/gen/ts.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import (
4949

5050
var TypeRegistry *protoregistry.Types
5151

52-
func fieldDescriptorToTS(f protoreflect.FieldDescriptor) string {
52+
func FieldDescriptorToTS(f protoreflect.FieldDescriptor) string {
5353
var t string
5454
switch f.Kind() {
5555
case protoreflect.StringKind:
@@ -74,15 +74,15 @@ func fieldDescriptorToTS(f protoreflect.FieldDescriptor) string {
7474
t = "string"
7575
case protoreflect.MessageKind:
7676
if f.IsMap() {
77-
t = fmt.Sprintf("Record<%s, %s>", fieldDescriptorToTS(f.MapKey()), fieldDescriptorToTS(f.MapValue()))
77+
t = fmt.Sprintf("Record<%s, %s>", FieldDescriptorToTS(f.MapKey()), FieldDescriptorToTS(f.MapValue()))
7878
} else if strings.HasPrefix(string(f.Message().FullName()), "google") {
7979
t = fmt.Sprintf("protobuf.%s", f.Message().Name())
8080
} else {
8181
d := f.Message()
8282
t = "{\n"
8383
for i := 0; i < d.Fields().Len(); i++ {
8484
f := d.Fields().Get(i)
85-
t += fmt.Sprintf("\t%s?: %s;\n", strcase.ToLowerCamel(f.TextName()), fieldDescriptorToTS(f))
85+
t += fmt.Sprintf("\t%s?: %s;\n", strcase.ToLowerCamel(f.TextName()), FieldDescriptorToTS(f))
8686
}
8787
t += "}"
8888
}
@@ -96,15 +96,15 @@ func fieldDescriptorToTS(f protoreflect.FieldDescriptor) string {
9696
return t
9797
}
9898

99-
func getTSInterface(d protoreflect.MessageDescriptor) (string, error) {
99+
func GetTSInterface(d protoreflect.MessageDescriptor) (string, error) {
100100
const templateBody = `export interface {{$.Name}} {
101101
{{range $.Fields}} {{ . }}
102102
{{end}}}`
103103

104104
var fields []string
105105
for i := 0; i < d.Fields().Len(); i++ {
106106
f := d.Fields().Get(i)
107-
t := fieldDescriptorToTS(f)
107+
t := FieldDescriptorToTS(f)
108108
fields = append(fields, fmt.Sprintf("%s?: %s;", strcase.ToLowerCamel(f.TextName()), t))
109109
}
110110

@@ -184,7 +184,7 @@ func GenTS(ctx context.Context, repoPath, ns string, getWriter func() (io.Writer
184184
log.Fatal("error finding the message in the registry", err)
185185
}
186186
parameters = getTSParameters(ptype.Descriptor())
187-
face, err := getTSInterface(ptype.Descriptor())
187+
face, err := GetTSInterface(ptype.Descriptor())
188188
if err != nil {
189189
return err
190190
}
@@ -237,7 +237,7 @@ func GenTS(ctx context.Context, repoPath, ns string, getWriter func() (io.Writer
237237
if err != nil {
238238
return errors.Wrapf(err, "could not find message: %s", protoreflect.FullName(name))
239239
}
240-
face, err := getTSInterface(ptype.Descriptor())
240+
face, err := GetTSInterface(ptype.Descriptor())
241241
if err != nil {
242242
return err
243243
}
@@ -246,19 +246,19 @@ func GenTS(ctx context.Context, repoPath, ns string, getWriter func() (io.Writer
246246
}
247247
}
248248
// Check if there is a per-config signature proto
249-
sigType, err := TypeRegistry.FindMessageByName(protoreflect.FullName("lekko.default." + strcase.ToCamel(f.Key) + ".Signature"))
249+
sigType, err := TypeRegistry.FindMessageByName(protoreflect.FullName(ns + ".config.v1beta1." + strcase.ToCamel(f.Key) + "Args"))
250250
if err == nil {
251251
d := sigType.Descriptor()
252252
var varNames []string
253253
var fields []string
254254
for i := 0; i < d.Fields().Len(); i++ {
255255
f := d.Fields().Get(i)
256-
t := fieldDescriptorToTS(f)
256+
t := FieldDescriptorToTS(f)
257257
fields = append(fields, fmt.Sprintf("%s?: %s;", strcase.ToLowerCamel(f.TextName()), t))
258258
varNames = append(varNames, strcase.ToLowerCamel(f.TextName()))
259259
}
260260

261-
ourParameters = fmt.Sprintf("{%s}: {%s}", strings.Join(varNames, ", "), strings.Join(fields, ", "))
261+
ourParameters = fmt.Sprintf("{%s}: {%s}", strings.Join(varNames, ", "), strings.Join(fields, " "))
262262
}
263263

264264
codeString, err := GenTSForFeature(f, ns, ourParameters)

0 commit comments

Comments
 (0)