Skip to content

Commit d113d0f

Browse files
committed
feat: added prompts for default values if not defined
Signed-off-by: Jason Salaber <jcsalaber@hotmail.com>
1 parent 1298a49 commit d113d0f

File tree

10 files changed

+236
-57
lines changed

10 files changed

+236
-57
lines changed

cmd/generate.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import (
44
"strings"
55

66
"github.com/open-feature/cli/internal/config"
7-
"github.com/open-feature/cli/internal/flagset"
87
"github.com/open-feature/cli/internal/generators"
98
"github.com/open-feature/cli/internal/generators/golang"
109
"github.com/open-feature/cli/internal/generators/react"
10+
"github.com/open-feature/cli/internal/manifest"
1111
"github.com/spf13/cobra"
1212
)
1313

@@ -53,7 +53,7 @@ func GetGenerateReactCmd() *cobra.Command {
5353
OutputPath: outputPath,
5454
Custom: react.Params{},
5555
}
56-
flagset, err := flagset.Load(manifestPath)
56+
flagset, err := manifest.LoadFlagSet(manifestPath)
5757
if err != nil {
5858
return err
5959
}
@@ -95,7 +95,7 @@ func GetGenerateGoCmd() *cobra.Command {
9595
},
9696
}
9797

98-
flagset, err := flagset.Load(manifestPath)
98+
flagset, err := manifest.LoadFlagSet(manifestPath)
9999
if err != nil {
100100
return err
101101
}

cmd/init.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func GetInitCmd() *cobra.Command {
2121
RunE: func(cmd *cobra.Command, args []string) error {
2222
manifestPath := config.GetManifestPath(cmd)
2323
override := config.GetOverride(cmd)
24-
flagSourceUrl, _ := cmd.Flags().GetString("flagSourceUrl")
24+
flagSourceUrl := config.GetFlagSourceUrl(cmd)
2525

2626
manifestExists, _ := filesystem.Exists(manifestPath)
2727
if (manifestExists && !override) {
@@ -63,12 +63,7 @@ func GetInitCmd() *cobra.Command {
6363
},
6464
}
6565

66-
<<<<<<< HEAD
6766
config.AddInitFlags(initCmd)
68-
=======
69-
initCmd.Flags().Bool("override", false, "Override an existing configuration")
70-
initCmd.Flags().String("flagSourceUrl", "", "The URL of the flag source")
71-
>>>>>>> ae4ce45 (feat: added functionality to create yaml config file and preliminary pull command)
7267

7368
return initCmd
7469
}

cmd/pull.go

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,61 @@
11
package cmd
22

33
import (
4+
"errors"
45
"fmt"
6+
"strconv"
57

8+
"github.com/open-feature/cli/internal/config"
69
"github.com/open-feature/cli/internal/filesystem"
10+
"github.com/open-feature/cli/internal/flagset"
11+
"github.com/open-feature/cli/internal/manifest"
12+
"github.com/open-feature/cli/internal/requests"
13+
"github.com/pterm/pterm"
714
"github.com/spf13/cobra"
815
)
916

17+
func promptForDefaultValue(flag *flagset.Flag) (any) {
18+
var prompt string
19+
switch flag.Type {
20+
case flagset.BoolType:
21+
var options []string = []string{"false", "true"}
22+
prompt = fmt.Sprintf("Enter default value for flag '%s' (%s)", flag.Key, flag.Type)
23+
boolStr, _ := pterm.DefaultInteractiveSelect.WithOptions(options).WithFilter(false).Show(prompt)
24+
boolValue, _ := strconv.ParseBool(boolStr)
25+
return boolValue
26+
case flagset.IntType:
27+
var err error = errors.New("Input a valid integer")
28+
prompt = fmt.Sprintf("Enter default value for flag '%s' (%s)", flag.Key, flag.Type)
29+
var defaultValue int
30+
for err != nil {
31+
defaultValueString, _ := pterm.DefaultInteractiveTextInput.WithDefaultText("0").Show(prompt)
32+
defaultValue, err = strconv.Atoi(defaultValueString)
33+
}
34+
return defaultValue
35+
case flagset.FloatType:
36+
var err error = errors.New("Input a valid float")
37+
prompt = fmt.Sprintf("Enter default value for flag '%s' (%s)", flag.Key, flag.Type)
38+
var defaultValue float64
39+
for err != nil {
40+
defaultValueString, _ := pterm.DefaultInteractiveTextInput.WithDefaultText("0.0").Show(prompt)
41+
defaultValue, err = strconv.ParseFloat(defaultValueString, 64)
42+
if err != nil {
43+
pterm.Error.Println("Input a valid float")
44+
}
45+
}
46+
return defaultValue
47+
case flagset.StringType:
48+
prompt = fmt.Sprintf("Enter default value for flag '%s' (%s)", flag.Key, flag.Type)
49+
defaultValue, _ := pterm.DefaultInteractiveTextInput.WithDefaultText("").Show(prompt)
50+
return defaultValue
51+
// TODO: Add proper support for object type
52+
case flagset.ObjectType:
53+
return map[string]any{}
54+
default:
55+
return nil
56+
}
57+
}
58+
1059
func GetPullCmd() *cobra.Command {
1160
pullCmd := &cobra.Command{
1261
Use: "pull",
@@ -16,33 +65,43 @@ func GetPullCmd() *cobra.Command {
1665
return initializeConfig(cmd, "pull")
1766
},
1867
RunE: func(cmd *cobra.Command, args []string) error {
19-
flagSourceUrl, err := cmd.Flags().GetString("flagSourceUrl")
20-
if err != nil {
68+
flagSourceUrl := config.GetFlagSourceUrl(cmd)
69+
manifestPath := config.GetManifestPath(cmd)
70+
authToken := config.GetAuthToken(cmd)
71+
72+
var err error
73+
if flagSourceUrl == "" {
2174
flagSourceUrl, err = filesystem.GetFromYaml("flagSourceUrl")
2275
if err != nil {
2376
return fmt.Errorf("error getting flagSourceUrl from config: %w", err)
2477
}
2578
}
2679

27-
fmt.Println(flagSourceUrl)
80+
// fetch the flags from the remote source
81+
flags, err := requests.FetchFlags(flagSourceUrl, authToken)
82+
if err != nil {
83+
return fmt.Errorf("error fetching flags: %w", err)
84+
}
2885

29-
// // fetch the flags from the remote source
30-
// resp, err := http.Get(flagSourceUrl)
31-
// if err != nil {
32-
// return fmt.Errorf("error fetching flags: %w", err)
33-
// }
34-
// defer resp.Body.Close()
86+
// Check each flag for null defaultValue
87+
for index, flag := range flags.Flags {
88+
if flag.DefaultValue == nil {
89+
defaultValue := promptForDefaultValue(&flag)
90+
flags.Flags[index].DefaultValue = defaultValue
91+
}
92+
}
3593

36-
// flags, err := io.ReadAll(resp.Body)
37-
// if err != nil {
38-
// return fmt.Errorf("error reading response body: %w", err)
39-
// }
94+
pterm.Success.Printf("Successfully fetched flags from %s", flagSourceUrl)
95+
err = manifest.Write(manifestPath, flags)
96+
if err != nil {
97+
return fmt.Errorf("error writing manifest: %w", err)
98+
}
4099

41100
return nil
42101
},
43102
}
44103

45-
pullCmd.Flags().String("flagSourceUrl", "", "The URL of the flag source")
104+
config.AddPullFlags(pullCmd)
46105

47106
return pullCmd
48107
}

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func GetRootCmd() *cobra.Command {
6464

6565
// Add a custom error handler after the command is created
6666
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
67-
pterm.Error.Println("Invalid flag: %s", err)
67+
pterm.Error.Printf("Invalid flag: %s\n", err)
6868
pterm.Println("Run 'openfeature --help' for usage information")
6969
return err
7070
})

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
176176
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
177177
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
178178
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
179-
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
180-
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
181179
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
182180
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
183181
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

internal/config/flags.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const (
1111
NoInputFlagName = "no-input"
1212
GoPackageFlagName = "package-name"
1313
OverrideFlagName = "override"
14+
FlagSourceUrlFlagName = "flag-source-url"
15+
AuthTokenFlagName = "auth-token"
1416
)
1517

1618
// Default values for flags
@@ -39,6 +41,13 @@ func AddGoGenerateFlags(cmd *cobra.Command) {
3941
// AddInitFlags adds the init command specific flags
4042
func AddInitFlags(cmd *cobra.Command) {
4143
cmd.Flags().Bool(OverrideFlagName, false, "Override an existing configuration")
44+
cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag source")
45+
}
46+
47+
// AddPullFlags adds the pull command specific flags
48+
func AddPullFlags(cmd *cobra.Command) {
49+
cmd.Flags().String(FlagSourceUrlFlagName, "", "The URL of the flag source")
50+
cmd.Flags().String(AuthTokenFlagName, "", "The auth token for the flag source")
4251
}
4352

4453
// GetManifestPath gets the manifest path from the given command
@@ -70,3 +79,15 @@ func GetOverride(cmd *cobra.Command) bool {
7079
override, _ := cmd.Flags().GetBool(OverrideFlagName)
7180
return override
7281
}
82+
83+
// GetFlagSourceUrl gets the flag source URL from the given command
84+
func GetFlagSourceUrl(cmd *cobra.Command) string {
85+
flagSourceUrl, _ := cmd.Flags().GetString(FlagSourceUrlFlagName)
86+
return flagSourceUrl
87+
}
88+
89+
// GetAuthToken gets the auth token from the given command
90+
func GetAuthToken(cmd *cobra.Command) string {
91+
authToken, _ := cmd.Flags().GetString(AuthTokenFlagName)
92+
return authToken
93+
}

internal/flagset/flagset.go

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"sort"
8-
9-
"github.com/open-feature/cli/internal/filesystem"
10-
"github.com/open-feature/cli/internal/manifest"
11-
"github.com/spf13/afero"
128
)
139

1410
// FlagType are the primitive types of flags.
@@ -31,7 +27,7 @@ func (f FlagType) String() string {
3127
case FloatType:
3228
return "float"
3329
case BoolType:
34-
return "bool"
30+
return "boolean"
3531
case StringType:
3632
return "string"
3733
case ObjectType:
@@ -52,29 +48,6 @@ type Flagset struct {
5248
Flags []Flag
5349
}
5450

55-
// Loads, validates, and unmarshals the manifest file at the given path into a flagset
56-
func Load(manifestPath string) (*Flagset, error) {
57-
fs := filesystem.FileSystem()
58-
data, err := afero.ReadFile(fs, manifestPath)
59-
if err != nil {
60-
return nil, fmt.Errorf("error reading contents from file %q", manifestPath)
61-
}
62-
63-
validationErrors, err := manifest.Validate(data)
64-
if err != nil {
65-
return nil, err
66-
} else if len(validationErrors) > 0 {
67-
return nil, fmt.Errorf("validation failed: %v", validationErrors)
68-
}
69-
70-
var flagset Flagset
71-
if err := json.Unmarshal(data, &flagset); err != nil {
72-
return nil, fmt.Errorf("error unmarshaling JSON: %v", validationErrors)
73-
}
74-
75-
return &flagset, nil
76-
}
77-
7851
// Filter removes flags from the Flagset that are of unsupported types.
7952
func (fs *Flagset) Filter(unsupportedFlagTypes map[FlagType]bool) *Flagset {
8053
var filtered Flagset
@@ -131,4 +104,46 @@ func (fs *Flagset) UnmarshalJSON(data []byte) error {
131104
})
132105

133106
return nil
134-
}
107+
}
108+
109+
func LoadFromSourceFlags(data []byte) (*[]Flag, error) {
110+
type SourceFlag struct {
111+
Key string `json:"key"`
112+
Type string `json:"type"`
113+
Description string `json:"description"`
114+
DefaultValue any `json:"defaultValue"`
115+
}
116+
117+
var sourceFlags []SourceFlag
118+
if err := json.Unmarshal(data, &sourceFlags); err != nil {
119+
return nil, err
120+
}
121+
122+
var flags []Flag
123+
for _, sf := range sourceFlags {
124+
var flagType FlagType
125+
switch sf.Type {
126+
case "integer", "Integer":
127+
flagType = IntType
128+
case "float", "Float", "Number":
129+
flagType = FloatType
130+
case "boolean", "bool", "Boolean":
131+
flagType = BoolType
132+
case "string", "String":
133+
flagType = StringType
134+
case "object", "Object", "JSON":
135+
flagType = ObjectType
136+
default:
137+
return nil, fmt.Errorf("unknown flag type: %s", sf.Type)
138+
}
139+
140+
flags = append(flags, Flag{
141+
Key: sf.Key,
142+
Type: flagType,
143+
Description: sf.Description,
144+
DefaultValue: sf.DefaultValue,
145+
})
146+
}
147+
148+
return &flags, nil
149+
}

internal/generators/react/react.tmpl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import {
44
type ReactFlagEvaluationOptions,
55
type ReactFlagEvaluationNoSuspenseOptions,
6+
type FlagQuery,
67
useFlag,
78
useSuspenseFlag,
89
} from "@openfeature/react-sdk";
@@ -15,7 +16,7 @@ import {
1516
* - default value: `{{ .DefaultValue }}`
1617
* - type: `{{ .Type | OpenFeatureType }}`
1718
*/
18-
export const use{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationOptions) => {
19+
export const use{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationOptions): FlagQuery<{{ .Type | OpenFeatureType }}> => {
1920
return useFlag({{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, options);
2021
};
2122

@@ -30,7 +31,7 @@ export const use{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationOptions) =
3031
* Equivalent to useFlag with options: `{ suspend: true }`
3132
* @experimental — Suspense is an experimental feature subject to change in future versions.
3233
*/
33-
export const useSuspense{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationNoSuspenseOptions) => {
34+
export const useSuspense{{ .Key | ToPascal }} = (options?: ReactFlagEvaluationNoSuspenseOptions): FlagQuery<{{ .Type | OpenFeatureType }}> => {
3435
return useSuspenseFlag({{ .Key | Quote }}, {{ .DefaultValue | QuoteString }}, options);
3536
};
3637
{{ end}}

0 commit comments

Comments
 (0)