From 7aa3cf83983b257156e6e4257dbdbc65fecc937e Mon Sep 17 00:00:00 2001 From: Bill Zdon Date: Fri, 5 Apr 2024 09:19:09 -0700 Subject: [PATCH 1/5] add optional params when there is a presence check --- cmd/lekko/gen/ts.go | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/cmd/lekko/gen/ts.go b/cmd/lekko/gen/ts.go index 70f2f6bb..e8ea14bd 100644 --- a/cmd/lekko/gen/ts.go +++ b/cmd/lekko/gen/ts.go @@ -288,13 +288,23 @@ export function {{$.FuncName}}({{$.Parameters}}): {{$.RetType}} { } usedVariables := make(map[string]string) - code := translateFeatureTS(f, usedVariables) + optionalVariables := make(map[string]string) + code := translateFeatureTS(f, usedVariables, optionalVariables) if len(parameters) == 0 && len(usedVariables) > 0 { var keys []string var keyAndTypes []string for k, t := range usedVariables { + if _, exists := optionalVariables[k]; !exists { + keys = append(keys, k) + keyAndTypes = append(keyAndTypes, fmt.Sprintf("%s: %s", k, t)) + } + } + for k, t := range optionalVariables { keys = append(keys, k) - keyAndTypes = append(keyAndTypes, fmt.Sprintf("%s: %s", k, t)) + if paramType, exists := usedVariables[k]; exists { + t = paramType + } + keyAndTypes = append(keyAndTypes, fmt.Sprintf("%s?: %s", k, t)) } parameters = fmt.Sprintf("{%s}: {%s}", strings.Join(keys, ","), strings.Join(keyAndTypes, ",")) } @@ -327,14 +337,14 @@ export function {{$.FuncName}}({{$.Parameters}}): {{$.RetType}} { return ret.String(), nil } -func translateFeatureTS(f *featurev1beta1.Feature, usedVariables map[string]string) []string { +func translateFeatureTS(f *featurev1beta1.Feature, usedVariables map[string]string, optionalVariables map[string]string) []string { var buffer []string for i, constraint := range f.Tree.Constraints { ifToken := "} else if" if i == 0 { ifToken = "if" } - rule := translateRuleTS(constraint.GetRuleAstNew(), usedVariables) + rule := translateRuleTS(constraint.GetRuleAstNew(), usedVariables, optionalVariables) buffer = append(buffer, fmt.Sprintf("\t%s %s {", ifToken, rule)) // TODO this doesn't work for proto, but let's try @@ -360,7 +370,7 @@ func structpbValueToKindString(v *structpb.Value) string { return "unknown" } -func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string) string { +func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string, optionalVariables map[string]string) string { marshalOptions := protojson.MarshalOptions{ UseProtoNames: true, } @@ -405,6 +415,10 @@ func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string) s case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_ENDS_WITH: usedVariables[v.Atom.ContextKey] = structpbValueToKindString(v.Atom.ComparisonValue) return fmt.Sprintf("(%s.endsWith(%s))", v.Atom.ContextKey, try.To1(marshalOptions.Marshal(v.Atom.ComparisonValue))) + case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_PRESENT: + usedVariables[v.Atom.ContextKey] = "unknown" + optionalVariables[v.Atom.ContextKey] = "unknown" + return fmt.Sprintf("(%s !== undefined)", v.Atom.ContextKey) } case *rulesv1beta3.Rule_LogicalExpression: operator := " && " From 9c681ab49e1a8ae879e4beb8cdd024b9aec7edd5 Mon Sep 17 00:00:00 2001 From: Bill Zdon Date: Fri, 5 Apr 2024 09:26:40 -0700 Subject: [PATCH 2/5] don't overwrite type --- cmd/lekko/gen/ts.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/lekko/gen/ts.go b/cmd/lekko/gen/ts.go index e8ea14bd..e4ab75dd 100644 --- a/cmd/lekko/gen/ts.go +++ b/cmd/lekko/gen/ts.go @@ -416,7 +416,9 @@ func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string, o usedVariables[v.Atom.ContextKey] = structpbValueToKindString(v.Atom.ComparisonValue) return fmt.Sprintf("(%s.endsWith(%s))", v.Atom.ContextKey, try.To1(marshalOptions.Marshal(v.Atom.ComparisonValue))) case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_PRESENT: - usedVariables[v.Atom.ContextKey] = "unknown" + if _, ok := usedVariables[v.Atom.ContextKey]; !ok { + usedVariables[v.Atom.ContextKey] = "unknown" + } optionalVariables[v.Atom.ContextKey] = "unknown" return fmt.Sprintf("(%s !== undefined)", v.Atom.ContextKey) } From 34aff56becafb6f3340ff834f398ecd397c5d7e5 Mon Sep 17 00:00:00 2001 From: Bill Zdon Date: Fri, 5 Apr 2024 09:33:48 -0700 Subject: [PATCH 3/5] fix func call --- cmd/lekko/gen/ts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lekko/gen/ts.go b/cmd/lekko/gen/ts.go index e4ab75dd..8404d41d 100644 --- a/cmd/lekko/gen/ts.go +++ b/cmd/lekko/gen/ts.go @@ -431,7 +431,7 @@ func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string, o var result []string for _, rule := range v.LogicalExpression.Rules { // worry about inner parens later - result = append(result, translateRuleTS(rule, usedVariables)) + result = append(result, translateRuleTS(rule, usedVariables, optionalVariables)) } return "(" + strings.Join(result, operator) + ")" } From 67c5320c43ac7f73b166fe5909e5cbd1f7539e85 Mon Sep 17 00:00:00 2001 From: Bill Zdon Date: Mon, 15 Apr 2024 15:47:27 +0200 Subject: [PATCH 4/5] fix optional params --- cmd/lekko/gen/ts.go | 57 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/cmd/lekko/gen/ts.go b/cmd/lekko/gen/ts.go index 19c6e36e..fe9fdb6c 100644 --- a/cmd/lekko/gen/ts.go +++ b/cmd/lekko/gen/ts.go @@ -305,8 +305,10 @@ export function {{$.FuncName}}({{$.Parameters}}): {{$.RetType}} { } } - usedVariables := make(map[string]string) optionalVariables := make(map[string]string) + translateFeatureToOptionalVariables(f, optionalVariables) + + usedVariables := make(map[string]string) code := translateFeatureTS(f, usedVariables, optionalVariables) if len(parameters) == 0 && len(usedVariables) > 0 { var keys []string @@ -355,6 +357,12 @@ export function {{$.FuncName}}({{$.Parameters}}): {{$.RetType}} { return ret.String(), nil } +func translateFeatureToOptionalVariables(f *featurev1beta1.Feature, optionalVariables map[string]string) { + for _, constraint := range f.Tree.Constraints { + getOptionalVariables(constraint.GetRuleAstNew(), optionalVariables) + } +} + func translateFeatureTS(f *featurev1beta1.Feature, usedVariables map[string]string, optionalVariables map[string]string) []string { var buffer []string for i, constraint := range f.Tree.Constraints { @@ -388,6 +396,41 @@ func structpbValueToKindString(v *structpb.Value) string { return "unknown" } +func getOptionalVariables(rule *rulesv1beta3.Rule, optionalVariables map[string]string) { + if rule == nil { + return + } + switch v := rule.GetRule().(type) { + case *rulesv1beta3.Rule_Atom: + switch v.Atom.GetComparisonOperator() { + case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_PRESENT: + optionalVariables[v.Atom.ContextKey] = "string | boolean | number" + } + case *rulesv1beta3.Rule_LogicalExpression: + for _, rule := range v.LogicalExpression.Rules { + getOptionalVariables(rule, optionalVariables) + } + } +} + +func createUndefinedSafeJSExpression(contextKey string, comparisonValue *structpb.Value, operator string, optionalVariables map[string]string, marshalOptions protojson.MarshalOptions, usedVariables map[string]string) string { + usedVariables[contextKey] = structpbValueToKindString(comparisonValue) + + marshaledValue, err := marshalOptions.Marshal(comparisonValue) + if err != nil { + return fmt.Sprintf("Error marshaling comparison value: %v", err) + } + + marshaledValueStr := string(marshaledValue) + + methodCall := fmt.Sprintf("%s.%s(%s)", contextKey, operator, marshaledValueStr) + if _, ok := optionalVariables[contextKey]; ok { + methodCall = fmt.Sprintf("%s?.%s(%s)", contextKey, operator, marshaledValueStr) + } + + return fmt.Sprintf("(%s)", methodCall) +} + func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string, optionalVariables map[string]string) string { marshalOptions := protojson.MarshalOptions{ UseProtoNames: true, @@ -425,19 +468,15 @@ func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string, o usedVariables[v.Atom.ContextKey] = structpbValueToKindString(v.Atom.ComparisonValue) return fmt.Sprintf("(%s >= %s)", v.Atom.ContextKey, try.To1(marshalOptions.Marshal(v.Atom.ComparisonValue))) case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_CONTAINS: - usedVariables[v.Atom.ContextKey] = structpbValueToKindString(v.Atom.ComparisonValue) - return fmt.Sprintf("(%s.includes(%s))", v.Atom.ContextKey, try.To1(marshalOptions.Marshal(v.Atom.ComparisonValue))) + return createUndefinedSafeJSExpression(v.Atom.ContextKey, v.Atom.ComparisonValue, "includes", optionalVariables, marshalOptions, usedVariables) case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_STARTS_WITH: - usedVariables[v.Atom.ContextKey] = structpbValueToKindString(v.Atom.ComparisonValue) - return fmt.Sprintf("(%s.startsWith(%s))", v.Atom.ContextKey, try.To1(marshalOptions.Marshal(v.Atom.ComparisonValue))) + return createUndefinedSafeJSExpression(v.Atom.ContextKey, v.Atom.ComparisonValue, "startsWith", optionalVariables, marshalOptions, usedVariables) case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_ENDS_WITH: - usedVariables[v.Atom.ContextKey] = structpbValueToKindString(v.Atom.ComparisonValue) - return fmt.Sprintf("(%s.endsWith(%s))", v.Atom.ContextKey, try.To1(marshalOptions.Marshal(v.Atom.ComparisonValue))) + return createUndefinedSafeJSExpression(v.Atom.ContextKey, v.Atom.ComparisonValue, "endsWith", optionalVariables, marshalOptions, usedVariables) case rulesv1beta3.ComparisonOperator_COMPARISON_OPERATOR_PRESENT: if _, ok := usedVariables[v.Atom.ContextKey]; !ok { - usedVariables[v.Atom.ContextKey] = "unknown" + usedVariables[v.Atom.ContextKey] = "string | boolean | number" } - optionalVariables[v.Atom.ContextKey] = "unknown" return fmt.Sprintf("(%s !== undefined)", v.Atom.ContextKey) } case *rulesv1beta3.Rule_LogicalExpression: From e5540b6452bf22b95605947b5289e059eae54268 Mon Sep 17 00:00:00 2001 From: Bill Zdon Date: Mon, 15 Apr 2024 18:41:40 +0200 Subject: [PATCH 5/5] checking in specific file --- cmd/lekko/gen/ts.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/lekko/gen/ts.go b/cmd/lekko/gen/ts.go index fe9fdb6c..761877e8 100644 --- a/cmd/lekko/gen/ts.go +++ b/cmd/lekko/gen/ts.go @@ -414,21 +414,21 @@ func getOptionalVariables(rule *rulesv1beta3.Rule, optionalVariables map[string] } func createUndefinedSafeJSExpression(contextKey string, comparisonValue *structpb.Value, operator string, optionalVariables map[string]string, marshalOptions protojson.MarshalOptions, usedVariables map[string]string) string { - usedVariables[contextKey] = structpbValueToKindString(comparisonValue) + usedVariables[contextKey] = structpbValueToKindString(comparisonValue) - marshaledValue, err := marshalOptions.Marshal(comparisonValue) + marshaledValue, err := marshalOptions.Marshal(comparisonValue) if err != nil { - return fmt.Sprintf("Error marshaling comparison value: %v", err) - } - + return fmt.Sprintf("Error marshaling comparison value: %v", err) + } + marshaledValueStr := string(marshaledValue) - methodCall := fmt.Sprintf("%s.%s(%s)", contextKey, operator, marshaledValueStr) - if _, ok := optionalVariables[contextKey]; ok { - methodCall = fmt.Sprintf("%s?.%s(%s)", contextKey, operator, marshaledValueStr) - } + methodCall := fmt.Sprintf("%s.%s(%s)", contextKey, operator, marshaledValueStr) + if _, ok := optionalVariables[contextKey]; ok { + methodCall = fmt.Sprintf("%s?.%s(%s)", contextKey, operator, marshaledValueStr) + } - return fmt.Sprintf("(%s)", methodCall) + return fmt.Sprintf("(%s)", methodCall) } func translateRuleTS(rule *rulesv1beta3.Rule, usedVariables map[string]string, optionalVariables map[string]string) string {