Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c67f767
feat!: initial commit for codegen CLI with golang support
anghelflorinm Sep 10, 2024
ec78985
docs!: add comments to few missing exported functions
anghelflorinm Sep 10, 2024
32f8069
refactor!: add better error messages for the CLI
anghelflorinm Sep 10, 2024
e4b02d0
refactor: move package to the top of the repository
anghelflorinm Sep 24, 2024
47889fc
refactor: move main file to top directory
anghelflorinm Sep 24, 2024
e5620ed
refactor: embed flag manifest schema into the script
anghelflorinm Sep 26, 2024
faf47d4
Merge branch 'main' into 2-cli-prototype
anghelflorinm Sep 26, 2024
9b8ddd8
fix: remove files after being moved
anghelflorinm Sep 26, 2024
5bdfc10
refactor: move main.go back into SRC and attach module to flagmanifes…
anghelflorinm Sep 30, 2024
896ac8d
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 1, 2024
a5059d0
refactor: change property names in manifest to camel case format
anghelflorinm Oct 1, 2024
01240cc
refactor: change property names to camel case
anghelflorinm Oct 1, 2024
301a51b
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 2, 2024
9c37b8f
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 3, 2024
96b2139
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 7, 2024
c766e83
refactor: change folder, package structure; integrate with cobra
anghelflorinm Oct 9, 2024
087b76b
chore: delete comment; this has been replaced with Git project issue
anghelflorinm Oct 10, 2024
36124c8
chore: update internal/generate/plugins/golang/golang.tmpl
anghelflorinm Oct 10, 2024
d858654
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Oct 23, 2024
97cdaff
test: add small tests for generate command for go and react with in m…
anghelflorinm Oct 29, 2024
4682490
Merge branch '2-cli-prototype' of https://github.com/open-feature/cod…
anghelflorinm Oct 29, 2024
09f0ac5
chore: update cmd/generate/generate_test.go
anghelflorinm Oct 30, 2024
6509bda
chore: update cmd/generate/generate_test.go
anghelflorinm Oct 30, 2024
efdcc4b
chore: update the permissions when making directory
anghelflorinm Oct 31, 2024
a883f7e
Merge remote-tracking branch 'origin' into 2-cli-prototype
anghelflorinm Nov 4, 2024
fc94016
chore: remove empty testutils package
anghelflorinm Nov 4, 2024
d96c3a0
/bin/bash: qm: command not found
anghelflorinm Nov 26, 2024
3cd02c6
Merge branch main into 2-cli-prototype
anghelflorinm Nov 26, 2024
1076fb8
Merge branch '2-cli-prototype' of https://github.com/open-feature/cod…
anghelflorinm Nov 26, 2024
356780b
Merge branch '2-cli-prototype' of https://github.com/open-feature/cod…
anghelflorinm Nov 26, 2024
9604583
chore: update back to previous mkdir permissions
anghelflorinm Nov 27, 2024
dd4fc72
Merge branch '2-cli-prototype' of https://github.com/open-feature/cod…
anghelflorinm Nov 27, 2024
2753953
feat: update golang output with new version
anghelflorinm Nov 27, 2024
929bfbe
Merge remote-tracking branch 'origin' into java-output
anghelflorinm Nov 27, 2024
50fb220
feat: add initial version for java flag accessors
anghelflorinm Nov 28, 2024
d23d93a
Merge remote-tracking branch 'origin' into java-output
anghelflorinm Nov 28, 2024
ebbc3e3
feat: add initial version for java flag accessors
anghelflorinm Nov 28, 2024
3fbf5b8
chore(main): release 0.1.9 (#58)
openfeaturebot Nov 27, 2024
6a49c87
Merge branch 'java-output' of https://github.com/open-feature/codegen…
anghelflorinm Nov 28, 2024
cdebd5d
feat: add initial version for java flag accessors
anghelflorinm Nov 28, 2024
89b6c71
chore(main): release 0.1.9 (#58)
openfeaturebot Nov 27, 2024
a2e1105
Merge branch 'java-output' of https://github.com/open-feature/codegen…
anghelflorinm Nov 28, 2024
7095703
Revert "feat: update golang output with new version" for the java branch
anghelflorinm Nov 28, 2024
3327dc2
Merge branch 'main' into java-output
beeme1mr Jan 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package generate

import (
"github.com/open-feature/cli/cmd/generate/golang"
"github.com/open-feature/cli/cmd/generate/java"
"github.com/open-feature/cli/cmd/generate/react"
"github.com/open-feature/cli/internal/flagkeys"

Expand All @@ -20,6 +21,7 @@ func init() {
// Add subcommands.
Root.AddCommand(golang.Cmd)
Root.AddCommand(react.Cmd)
Root.AddCommand(java.Cmd)

// Add flags.
Root.PersistentFlags().String(flagkeys.FlagManifestPath, "", "Path to the flag manifest.")
Expand Down
26 changes: 26 additions & 0 deletions cmd/generate/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ func TestGenerateReactSuccess(t *testing.T) {
compareOutput(t, testFileReact, memoryOutputPath, fs)
}

func TestGenerateJavaSuccess(t *testing.T) {
// Constant paths.
const memoryManifestPath = "manifest/path.json"
const memoryOutputPath = "example_java/ExperimentFlags.java"
const testFileManifest = "testdata/success_manifest.golden"
const testFileReact = "testdata/success_java.golden"

// Prepare in-memory files.
fs := afero.NewMemMapFs()
viper.Set(flagkeys.FileSystem, fs)
readOsFileAndWriteToMemMap(t, testFileManifest, memoryManifestPath, fs)

// Prepare command.
Root.SetArgs([]string{"java",
"--flag_manifest_path", memoryManifestPath,
"--output_path", memoryOutputPath,
"--package_name", "example_java",
})

// Run command.
Root.Execute()

// Compare result.
compareOutput(t, testFileReact, memoryOutputPath, fs)
}

func readOsFileAndWriteToMemMap(t *testing.T, inputPath string, memPath string, memFs afero.Fs) {
data, err := os.ReadFile(inputPath)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion cmd/generate/golang/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
"github.com/spf13/viper"
)

// Cmd for `generate“ command, handling code generation for flag accessors
// Cmd for `generate go` command, handling code generation for flag accessors
// for Golang.
var Cmd = &cobra.Command{
Use: "go",
Short: "Generate Golang flag accessors for OpenFeature.",
Expand Down
33 changes: 33 additions & 0 deletions cmd/generate/java/java.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package java

import (
"github.com/open-feature/cli/internal/flagkeys"
"github.com/open-feature/cli/internal/generate"
"github.com/open-feature/cli/internal/generate/plugins/java"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// Cmd for `generate java“ command, handling code generation for flag accessors
// for Java.
var Cmd = &cobra.Command{
Use: "java",
Short: "Generate Java flag accessors for OpenFeature.",
Long: `Generate Java flag accessors for OpenFeature.`,
RunE: func(cmd *cobra.Command, args []string) error {
params := java.Params{
JavaPackage: viper.GetString(flagkeys.JavaPackageName),
}
gen := java.NewGenerator(params)
err := generate.CreateFlagAccessors(gen)
return err
},
}

func init() {
Cmd.Flags().String(flagkeys.JavaPackageName, "", "Name of the Java package to be generated.")
Cmd.MarkFlagRequired(flagkeys.JavaPackageName)
viper.BindPFlag(flagkeys.JavaPackageName, Cmd.Flags().Lookup(flagkeys.JavaPackageName))

}
56 changes: 56 additions & 0 deletions cmd/generate/testdata/success_java.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// AUTOMATICALLY GENERATED BY OPENFEATURE CODEGEN, DO NOT EDIT.
package example_java;
import dev.openfeature.sdk.*;


public class ExperimentFlags {
private static Client client = OpenFeatureAPI.getInstance().getClient();
/**
* Discount percentage applied to purchases.
*/
public static class DiscountPercentage {
public static Double getValue(EvaluationContext evaluationContext) {
return client.getDoubleValue("discountPercentage", 0.15, evaluationContext);
}

public static FlagEvaluationDetails<Double> getValueDetails(EvaluationContext evaluationContext) {
return client.getDoubleDetails("discountPercentage", 0.15, evaluationContext);
}
}
/**
* Controls whether Feature A is enabled.
*/
public static class EnableFeatureA {
public static Boolean getValue(EvaluationContext evaluationContext) {
return client.getBooleanValue("enableFeatureA", false, evaluationContext);
}

public static FlagEvaluationDetails<Boolean> getValueDetails(EvaluationContext evaluationContext) {
return client.getBooleanDetails("enableFeatureA", false, evaluationContext);
}
}
/**
* The message to use for greeting users.
*/
public static class GreetingMessage {
public static String getValue(EvaluationContext evaluationContext) {
return client.getStringValue("greetingMessage", "Hello there!", evaluationContext);
}

public static FlagEvaluationDetails<String> getValueDetails(EvaluationContext evaluationContext) {
return client.getStringDetails("greetingMessage", "Hello there!", evaluationContext);
}
}
/**
* Maximum allowed length for usernames.
*/
public static class UsernameMaxLength {
public static Integer getValue(EvaluationContext evaluationContext) {
return client.getIntegerValue("usernameMaxLength", 50, evaluationContext);
}

public static FlagEvaluationDetails<Integer> getValueDetails(EvaluationContext evaluationContext) {
return client.getIntegerDetails("usernameMaxLength", 50, evaluationContext);
}
}
}
4 changes: 4 additions & 0 deletions internal/flagkeys/flagkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const (
// GoPackageName is the key for the flag that stores the Golang package name.
GoPackageName = "package_name"

// `generate java` flags:
// JavaPackageName is the key for the flag that stores the Java package name.
JavaPackageName = "package_name"

//internal keys:
// FileSystem is the key for the flag that stores the filesystem interface.
FileSystem = "filesystem"
Expand Down
123 changes: 123 additions & 0 deletions internal/generate/plugins/java/java.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package java

import (
_ "embed"
"html/template"
"path/filepath"
"sort"
"strconv"
"strings"

"github.com/iancoleman/strcase"
"github.com/open-feature/cli/internal/generate"
"github.com/open-feature/cli/internal/generate/types"
)

// TmplData contains the Java-specific data and the base data for the codegen.
type TmplData struct {
*types.BaseTmplData
JavaPackage string
}

type genImpl struct {
javaPackage string
}

// BaseTmplDataInfo provides the base template data for the codegen.
func (td *TmplData) BaseTmplDataInfo() *types.BaseTmplData {
return td.BaseTmplData
}

// supportedFlagTypes is the flag types supported by the Java template.
var supportedFlagTypes = map[types.FlagType]bool{
types.FloatType: true,
types.StringType: true,
types.IntType: true,
types.BoolType: true,
types.ObjectType: false,
}

func (*genImpl) SupportedFlagTypes() map[types.FlagType]bool {
return supportedFlagTypes
}

//go:embed java.tmpl
var javaTmpl string

// Java Funcs BEGIN

func flagClass(flagName string) string {
return strcase.ToCamel(flagName)
}

func topLevelClass(outputPath string) string {
return strings.TrimSuffix(filepath.Base(outputPath), filepath.Ext(outputPath))
}

func flagInitParam(flagName string) string {
return strconv.Quote(flagName)
}

func javaType(t types.FlagType) string {
switch t {
case types.IntType:
return "Integer"
case types.FloatType:
return "Double"
case types.BoolType:
return "Boolean"
case types.StringType:
return "String"
default:
return ""

@kriscoleman kriscoleman Mar 4, 2025

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does types.ObjectType case belong here too?

EDIT: Michael let me know that this is OOS for now, so disregard 😄

}
}

func supportImports(flags []*types.FlagTmplData) []string {
var res []string
if len(flags) > 0 {
res = append(res, "dev.openfeature.sdk.*")
}
sort.Strings(res)
return res
}

func defaultValueLiteral(flag *types.FlagTmplData) string {
switch flag.Type {
case types.StringType:
return strconv.Quote(flag.DefaultValue)
default:
return flag.DefaultValue
}
}

// Java Funcs END

// Generate generates the Java flag accessors for OpenFeature.
func (g *genImpl) Generate(input types.Input) error {
funcs := template.FuncMap{
"FlagClass": flagClass,
"TopLevelClass": topLevelClass,
"FlagInitParam": flagInitParam,
"JavaType": javaType,
"SupportImports": supportImports,
"DefaultValueLiteral": defaultValueLiteral,
}
td := TmplData{
BaseTmplData: input.BaseData,
JavaPackage: g.javaPackage,
}
return generate.GenerateFile(funcs, javaTmpl, &td)
}

// Params are parameters for creating a Generator
type Params struct {
JavaPackage string
}

// NewGenerator creates a generator for Java.
func NewGenerator(params Params) types.Generator {
return &genImpl{
javaPackage: params.JavaPackage,
}
}
27 changes: 27 additions & 0 deletions internal/generate/plugins/java/java.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// AUTOMATICALLY GENERATED BY OPENFEATURE CODEGEN, DO NOT EDIT.
package {{.JavaPackage}};


{{- range $_, $p := SupportImports .Flags}}
import {{$p}};
{{- end}}


public class {{TopLevelClass .OutputPath}} {
private static Client client = OpenFeatureAPI.getInstance().getClient();

{{- range .Flags}}
/**
* {{.Docs}}
*/
public static class {{FlagClass .Name}} {
public static {{JavaType .Type}} getValue(EvaluationContext evaluationContext) {
return client.get{{JavaType .Type}}Value({{FlagInitParam .Name}}, {{DefaultValueLiteral .}}, evaluationContext);
}

public static FlagEvaluationDetails<{{JavaType .Type}}> getValueDetails(EvaluationContext evaluationContext) {
return client.get{{JavaType .Type}}Details({{FlagInitParam .Name}}, {{DefaultValueLiteral .}}, evaluationContext);
}
}
{{- end}}
}