diff --git a/cmd/cli/evaluate/evaluate.go b/cmd/cli/evaluate/evaluate.go index 31ce6b369a9..44febc5c520 100644 --- a/cmd/cli/evaluate/evaluate.go +++ b/cmd/cli/evaluate/evaluate.go @@ -51,14 +51,20 @@ func (e evaluate) Evaluate() (map[string]model.RawVarResult, error) { if e.flag != "" { listFlags = append(listFlags, e.flag) } else { - flags, _ := goff.GetFlagsFromCache() + flags, err := goff.GetFlagsFromCache() + if err != nil { + return nil, err + } for key := range flags { listFlags = append(listFlags, key) } } for _, flag := range listFlags { - res, _ := goff.RawVariation(flag, convertedEvaluationCtx, nil) + res, err := goff.RawVariation(flag, convertedEvaluationCtx, nil) + if err != nil { + return nil, err + } result[flag] = res } return result, nil diff --git a/cmd/cli/evaluate/evaluate_cmd.go b/cmd/cli/evaluate/evaluate_cmd.go index 0b845a85396..11cc092ad0a 100644 --- a/cmd/cli/evaluate/evaluate_cmd.go +++ b/cmd/cli/evaluate/evaluate_cmd.go @@ -153,9 +153,8 @@ evaluate --kind postgres --table my-table --column my-column:my-column-type --fl if checkMode { return runCheck(cmd, retrieverConf) - } else { - return runEvaluate(cmd, args, evalFlagFormat, retrieverConf, evalFlag, evalCtx) } + return runEvaluate(cmd, args, evalFlagFormat, retrieverConf, evalFlag, evalCtx) }, SilenceUsage: true, SilenceErrors: true, diff --git a/cmd/cli/evaluate/evaluate_test.go b/cmd/cli/evaluate/evaluate_test.go index e99cbec8bc3..a74adbd2a6b 100644 --- a/cmd/cli/evaluate/evaluate_test.go +++ b/cmd/cli/evaluate/evaluate_test.go @@ -171,7 +171,8 @@ func Test_evaluate_Evaluate(t *testing.T) { return evaluate{}, err } - gitHubRetriever, _ := r.(*githubretriever.Retriever) + gitHubRetriever, ok := r.(*githubretriever.Retriever) + assert.True(t, ok, "failed to assert retriever to *githubretriever.Retriever") gitHubRetriever.SetHTTPClient(&mock.HTTP{}) return evaluate{ @@ -211,7 +212,8 @@ func Test_evaluate_Evaluate(t *testing.T) { return evaluate{}, err } - gitLabRetriever, _ := r.(*gitlabretriever.Retriever) + gitLabRetriever, ok := r.(*gitlabretriever.Retriever) + assert.True(t, ok, "failed to assert retriever to *gitlabretriever.Retriever") gitLabRetriever.SetHTTPClient(&mock.HTTP{}) return evaluate{ @@ -251,7 +253,8 @@ func Test_evaluate_Evaluate(t *testing.T) { return evaluate{}, err } - bitBucketRetriever, _ := r.(*bitbucketretriever.Retriever) + bitBucketRetriever, ok := r.(*bitbucketretriever.Retriever) + assert.True(t, ok, "failed to assert retriever to *bitbucketretriever.Retriever") bitBucketRetriever.SetHTTPClient(&mock.HTTP{}) return evaluate{ @@ -295,7 +298,8 @@ func Test_evaluate_Evaluate(t *testing.T) { return evaluate{}, err } - s3Retriever, _ := r.(*s3retrieverv2.Retriever) + s3Retriever, ok := r.(*s3retrieverv2.Retriever) + assert.True(t, ok, "failed to assert retriever to *s3retrieverv2.Retriever") s3Retriever.SetDownloader(downloader) _ = s3Retriever.Init(context.Background(), nil) @@ -340,7 +344,8 @@ func Test_evaluate_Evaluate(t *testing.T) { return evaluate{}, err } - httpRetriever, _ := r.(*httpretriever.Retriever) + httpRetriever, ok := r.(*httpretriever.Retriever) + assert.True(t, ok, "failed to assert retriever to *httpretriever.Retriever") httpRetriever.SetHTTPClient(&mock.HTTP{}) return evaluate{ @@ -383,7 +388,8 @@ func Test_evaluate_Evaluate(t *testing.T) { return evaluate{}, err } - gcsRetriever, _ := r.(*gcstorageretriever.Retriever) + gcsRetriever, ok := r.(*gcstorageretriever.Retriever) + assert.True(t, ok, "failed to assert retriever to *gcstorageretriever.Retriever") gcsRetriever.SetOptions([]option.ClientOption{ option.WithCredentials(&google.Credentials{}), option.WithHTTPClient(mockedStorage.Server.HTTPClient()), diff --git a/cmd/cli/generate/manifest/manifest.go b/cmd/cli/generate/manifest/manifest.go index d679db56569..896e0a042e2 100644 --- a/cmd/cli/generate/manifest/manifest.go +++ b/cmd/cli/generate/manifest/manifest.go @@ -11,11 +11,7 @@ import ( dtoCore "github.com/thomaspoignant/go-feature-flag/modules/core/dto" ) -func NewManifest( - configFile string, - configFormat string, - flagManifestDestination string, -) (Manifest, error) { +func NewManifest(configFile, configFormat, flagManifestDestination string) (Manifest, error) { if flagManifestDestination == "" { return Manifest{}, fmt.Errorf("--flag_manifest_destination is mandatory") } @@ -63,7 +59,7 @@ func (m *Manifest) generateDefinitions(flagDTOs map[string]dto.DTO) ( output := helper.Output{} for flagKey, flagDTO := range flagDTOs { flag := dtoCore.ConvertDtoToInternalFlag(flagDTO) - flagType, err := helper.GetFlagTypeFromVariations(flag.GetVariations()) + flagType, err := helper.FlagTypeFromVariations(flag.GetVariations()) if err != nil { return model.FlagManifest{}, output, fmt.Errorf("invalid configuration for flag %s: %s", flagKey, err.Error()) diff --git a/cmd/cli/generate/manifest/manifest_cmd.go b/cmd/cli/generate/manifest/manifest_cmd.go index 58cf05ec995..297c94f19a6 100644 --- a/cmd/cli/generate/manifest/manifest_cmd.go +++ b/cmd/cli/generate/manifest/manifest_cmd.go @@ -18,7 +18,10 @@ func NewManifestCmd() *cobra.Command { "⚠️ note that this is an experimental feature and we may change this command line without warning.", RunE: func(cmd *cobra.Command, _ []string) error { - m, _ := NewManifest(manifestConfigFile, manifestFlagFormat, flagManifestDestination) + m, err := NewManifest(manifestConfigFile, manifestFlagFormat, flagManifestDestination) + if err != nil { + return err + } output, err := m.Generate() if err != nil { cmd.SilenceUsage = true diff --git a/cmd/cli/helper/number_type.go b/cmd/cli/helper/number_type.go index a6226170035..e4fb27848be 100644 --- a/cmd/cli/helper/number_type.go +++ b/cmd/cli/helper/number_type.go @@ -7,7 +7,7 @@ import ( "github.com/thomaspoignant/go-feature-flag/cmd/cli/generate/manifest/model" ) -func GetFlagTypeFromVariations(variations map[string]*interface{}) (model.FlagType, error) { +func FlagTypeFromVariations(variations map[string]*interface{}) (model.FlagType, error) { if variations == nil { return "", fmt.Errorf("impossible to find type, no variations found") } diff --git a/cmd/cli/helper/number_type_test.go b/cmd/cli/helper/number_type_test.go index 05d8a2adc5a..3e8e6111b3d 100644 --- a/cmd/cli/helper/number_type_test.go +++ b/cmd/cli/helper/number_type_test.go @@ -9,7 +9,7 @@ import ( "github.com/thomaspoignant/go-feature-flag/modules/core/testutils/testconvert" ) -func TestGetFlagTypeFromVariations(t *testing.T) { +func TestFlagTypeFromVariations(t *testing.T) { tests := []struct { name string variations map[string]*interface{} @@ -101,7 +101,7 @@ func TestGetFlagTypeFromVariations(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := helper.GetFlagTypeFromVariations(tt.variations) + result, err := helper.FlagTypeFromVariations(tt.variations) if tt.expectErr { assert.Error(t, err) } else { diff --git a/cmd/cli/helper/print_output.go b/cmd/cli/helper/print_output.go index b1d27a0522b..cf01dea0298 100644 --- a/cmd/cli/helper/print_output.go +++ b/cmd/cli/helper/print_output.go @@ -48,7 +48,10 @@ func (o *Output) PrintLines(cmd *cobra.Command) { default: outputText = pterm.Sprint(line.Text) } - _, _ = fmt.Fprintln(writer, outputText) + _, err := fmt.Fprintln(writer, outputText) + if err != nil { + PrintFatalAndExit(err) + } } } diff --git a/cmd/cli/helper/read_config_file.go b/cmd/cli/helper/read_config_file.go index 4d2b8c67915..e97f82bf604 100644 --- a/cmd/cli/helper/read_config_file.go +++ b/cmd/cli/helper/read_config_file.go @@ -42,9 +42,10 @@ func LoadConfigFile( for _, location := range defaultLocations { for _, ext := range supportedExtensions { configFile := fmt.Sprintf("%s%s.%s", location, filename, ext) - if _, err := os.Stat(configFile); err == nil { - return readConfigFile(configFile, ext) + if _, err := os.Stat(configFile); err != nil { + continue } + return readConfigFile(configFile, ext) } } return nil, fmt.Errorf( @@ -53,7 +54,7 @@ func LoadConfigFile( ) } -func readConfigFile(configFile string, configFormat string) (map[string]dto.DTO, error) { +func readConfigFile(configFile, configFormat string) (map[string]dto.DTO, error) { dat, err := os.ReadFile(configFile) if err != nil { return nil, err diff --git a/cmd/cli/linter/lint_cmd.go b/cmd/cli/linter/lint_cmd.go index c7235666ccf..3edbe4e106e 100644 --- a/cmd/cli/linter/lint_cmd.go +++ b/cmd/cli/linter/lint_cmd.go @@ -28,7 +28,7 @@ func NewLintCmd() *cobra.Command { func runLint(cmd *cobra.Command, args []string, lintFlagFormat string) error { output := helper.Output{} l := Linter{ - InputFile: getFilePath(args), + InputFile: extractFilePathFromArgs(args), InputFormat: lintFlagFormat, } if errs := l.Lint(); len(errs) > 0 { @@ -44,7 +44,7 @@ func runLint(cmd *cobra.Command, args []string, lintFlagFormat string) error { return nil } -func getFilePath(args []string) string { +func extractFilePathFromArgs(args []string) string { if len(args) == 0 { return "" } diff --git a/cmd/relayproxy/api/lambda_handler.go b/cmd/relayproxy/api/lambda_handler.go index b409b6d8d9a..8f65487c4fc 100644 --- a/cmd/relayproxy/api/lambda_handler.go +++ b/cmd/relayproxy/api/lambda_handler.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-lambda-go/events" echoadapter "github.com/awslabs/aws-lambda-go-api-proxy/echo" "github.com/labstack/echo/v4" + "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/config" ) // newAwsLambdaHandlerManager is creating a new awsLambdaHandler struct with the echoadapter @@ -36,11 +37,12 @@ type awsLambdaHandler struct { adapterALB *echoadapter.EchoLambdaALB } -func (h *awsLambdaHandler) GetAdapter(mode string) interface{} { +// SelectAdapter returns the appropriate adapter based on the mode. +func (h *awsLambdaHandler) SelectAdapter(mode string) interface{} { switch strings.ToUpper(mode) { - case "APIGATEWAYV1": + case strings.ToUpper(config.LambdaAdapterAPIGatewayV1): return h.HandlerAPIGatewayV1 - case "ALB": + case strings.ToUpper(config.LambdaAdapterALB): return h.HandlerALB default: return h.HandlerAPIGatewayV2 diff --git a/cmd/relayproxy/api/lambda_handler_test.go b/cmd/relayproxy/api/lambda_handler_test.go index 51ea073c37e..b51562cc9d4 100644 --- a/cmd/relayproxy/api/lambda_handler_test.go +++ b/cmd/relayproxy/api/lambda_handler_test.go @@ -100,7 +100,7 @@ func TestAwsLambdaHandler_GetAdapter(t *testing.T) { require.NoError(t, err) // Create a Lambda handler - handler := lambda.NewHandler(apiServer.getLambdaHandler()) + handler := lambda.NewHandler(apiServer.lambdaHandler()) // Invoke the handler with the mock event response, err := handler.Invoke(context.Background(), reqJSON) @@ -216,7 +216,7 @@ func TestAwsLambdaHandler_BasePathSupport(t *testing.T) { require.NoError(t, err) // Create a Lambda handler - handler := lambda.NewHandler(apiServer.getLambdaHandler()) + handler := lambda.NewHandler(apiServer.lambdaHandler()) // Invoke the handler with the mock event response, err := handler.Invoke(context.Background(), reqJSON) diff --git a/cmd/relayproxy/api/routes_goff.go b/cmd/relayproxy/api/routes_goff.go index c1fc25c1be3..5c10f333f38 100644 --- a/cmd/relayproxy/api/routes_goff.go +++ b/cmd/relayproxy/api/routes_goff.go @@ -9,10 +9,10 @@ import ( ) func (s *Server) addGOFFRoutes( - cAllFlags controller.Controller, - cFlagEval controller.Controller, - cEvalDataCollector controller.Controller, - cFlagChange controller.Controller, + cAllFlags, + cFlagEval, + cEvalDataCollector, + cFlagChange, cFlagConfiguration controller.Controller) { // Grouping the routes v1 := s.apiEcho.Group("/v1") diff --git a/cmd/relayproxy/api/routes_monitoring.go b/cmd/relayproxy/api/routes_monitoring.go index 33dd5c92a7e..1dd68bef1af 100644 --- a/cmd/relayproxy/api/routes_monitoring.go +++ b/cmd/relayproxy/api/routes_monitoring.go @@ -11,7 +11,7 @@ import ( ) func (s *Server) addMonitoringRoutes() { - if s.config.GetMonitoringPort(s.zapLog) != 0 { + if s.config.EffectiveMonitoringPort(s.zapLog) != 0 { s.monitoringEcho = echo.New() s.monitoringEcho.HideBanner = true s.monitoringEcho.HidePort = true diff --git a/cmd/relayproxy/api/routes_monitoring_test.go b/cmd/relayproxy/api/routes_monitoring_test.go index c6f8ddfc703..ab3b131e930 100644 --- a/cmd/relayproxy/api/routes_monitoring_test.go +++ b/cmd/relayproxy/api/routes_monitoring_test.go @@ -68,7 +68,7 @@ func TestPprofEndpointsStarts(t *testing.T) { Metrics: metric.Metrics{}, }, z) - portToCheck := c.GetServerPort(z) + portToCheck := c.ServerPort(z) if tt.MonitoringPort != 0 { portToCheck = tt.MonitoringPort } @@ -77,6 +77,7 @@ func TestPprofEndpointsStarts(t *testing.T) { defer apiServer.Stop(context.Background()) time.Sleep(1 * time.Second) // waiting for the apiServer to start resp, err := http.Get(fmt.Sprintf("http://localhost:%d/debug/pprof/heap", portToCheck)) + defer func() { _ = resp.Body.Close() }() require.NoError(t, err) require.Equal(t, tt.expectedStatusCode, resp.StatusCode) }) diff --git a/cmd/relayproxy/api/server.go b/cmd/relayproxy/api/server.go index 31c3f9d4e3b..5beb74acd17 100644 --- a/cmd/relayproxy/api/server.go +++ b/cmd/relayproxy/api/server.go @@ -122,7 +122,7 @@ func (s *Server) StartWithContext(ctx context.Context) { // we can continue because otel is not mandatory to start the server } - switch s.config.GetServerMode(s.zapLog) { + switch s.config.ServerMode(s.zapLog) { case config.ServerModeLambda: s.startAwsLambda() case config.ServerModeUnixSocket: @@ -134,7 +134,7 @@ func (s *Server) StartWithContext(ctx context.Context) { // startUnixSocketServer launch the API server as a unix socket. func (s *Server) startUnixSocketServer(ctx context.Context) { - socketPath := s.config.GetUnixSocketPath() + socketPath := s.config.UnixSocketPath() // Clean up the old socket file if it exists (important for graceful restarts) if _, err := os.Stat(socketPath); err == nil { @@ -181,7 +181,7 @@ func (s *Server) startAsHTTPServer() { defer func() { _ = s.monitoringEcho.Close() }() } - address := fmt.Sprintf("%s:%d", s.config.GetServerHost(), s.config.GetServerPort(s.zapLog)) + address := fmt.Sprintf("%s:%d", s.config.ServerHost(), s.config.ServerPort(s.zapLog)) s.zapLog.Info( "Starting go-feature-flag relay proxy ...", zap.String("address", address), @@ -194,7 +194,7 @@ func (s *Server) startAsHTTPServer() { } func (s *Server) startMonitoringServer() { - addressMonitoring := fmt.Sprintf("%s:%d", s.config.GetServerHost(), s.config.GetMonitoringPort(s.zapLog)) + addressMonitoring := fmt.Sprintf("%s:%d", s.config.ServerHost(), s.config.EffectiveMonitoringPort(s.zapLog)) s.zapLog.Info( "Starting monitoring", zap.String("address", addressMonitoring)) @@ -206,15 +206,15 @@ func (s *Server) startMonitoringServer() { // startAwsLambda is starting the relay proxy as an AWS Lambda func (s *Server) startAwsLambda() { - lambda.Start(s.getLambdaHandler()) + lambda.Start(s.lambdaHandler()) } -// getLambdaHandler returns the appropriate lambda handler based on the configuration. +// lambdaHandler returns the appropriate lambda handler based on the configuration. // We need a dedicated function because it is called from tests as well, this is the // reason why we can't merged it in startAwsLambda. -func (s *Server) getLambdaHandler() interface{} { - handlerMngr := newAwsLambdaHandlerManager(s.apiEcho, s.config.GetAwsApiGatewayBasePath(s.zapLog)) - return handlerMngr.GetAdapter(s.config.GetLambdaAdapter(s.zapLog)) +func (s *Server) lambdaHandler() interface{} { + handlerMngr := newAwsLambdaHandlerManager(s.apiEcho, s.config.EffectiveAwsApiGatewayBasePath(s.zapLog)) + return handlerMngr.SelectAdapter(s.config.LambdaAdapter(s.zapLog)) } // Stop shutdown the API server @@ -241,5 +241,5 @@ func (s *Server) Stop(ctx context.Context) { // isMonitoringPortConfigured checks if the monitoring port is configured. func (s *Server) isMonitoringPortConfigured() bool { - return s.monitoringEcho != nil && s.config.GetMonitoringPort(s.zapLog) > 0 + return s.monitoringEcho != nil && s.config.EffectiveMonitoringPort(s.zapLog) > 0 } diff --git a/cmd/relayproxy/api/server_test.go b/cmd/relayproxy/api/server_test.go index eb19438f4f3..282d50049ac 100644 --- a/cmd/relayproxy/api/server_test.go +++ b/cmd/relayproxy/api/server_test.go @@ -53,9 +53,7 @@ func Test_Starting_RelayProxy_with_monitoring_on_same_port(t *testing.T) { prometheusNotifier, proxyNotifier, }) - if err != nil { - panic(err) - } + assert.NoError(t, err) services := service.Services{ MonitoringService: service.NewMonitoring(flagsetManager), @@ -72,14 +70,17 @@ func Test_Starting_RelayProxy_with_monitoring_on_same_port(t *testing.T) { response, err := http.Get("http://localhost:11024/health") assert.NoError(t, err) + defer func() { _ = response.Body.Close() }() assert.Equal(t, http.StatusOK, response.StatusCode) responseM, err := http.Get("http://localhost:11024/metrics") assert.NoError(t, err) + defer func() { _ = responseM.Body.Close() }() assert.Equal(t, http.StatusOK, responseM.StatusCode) responseI, err := http.Get("http://localhost:11024/info") assert.NoError(t, err) + defer func() { _ = responseI.Body.Close() }() assert.Equal(t, http.StatusOK, responseI.StatusCode) } @@ -114,9 +115,7 @@ func Test_Starting_RelayProxy_with_monitoring_on_different_port(t *testing.T) { prometheusNotifier, proxyNotifier, }) - if err != nil { - panic(err) - } + assert.NoError(t, err) services := service.Services{ MonitoringService: service.NewMonitoring(flagsetManager), @@ -133,6 +132,7 @@ func Test_Starting_RelayProxy_with_monitoring_on_different_port(t *testing.T) { response, err := http.Get("http://localhost:11024/health") assert.NoError(t, err) + defer func() { _ = response.Body.Close() }() assert.Equal(t, http.StatusNotFound, response.StatusCode) responseM, err := http.Get("http://localhost:11024/metrics") @@ -190,9 +190,7 @@ func Test_CheckOFREPAPIExists(t *testing.T) { prometheusNotifier, proxyNotifier, }) - if err != nil { - panic(err) - } + assert.NoError(t, err) services := service.Services{ MonitoringService: service.NewMonitoring(flagsetManager), @@ -225,6 +223,7 @@ func Test_CheckOFREPAPIExists(t *testing.T) { req.Header.Add("Content-Type", "application/json") response, err = http.DefaultClient.Do(req) assert.NoError(t, err) + defer func() { _ = response.Body.Close() }() assert.Equal(t, http.StatusNotFound, response.StatusCode) req, err = http.NewRequest("POST", @@ -235,6 +234,7 @@ func Test_CheckOFREPAPIExists(t *testing.T) { req.Header.Add("Content-Type", "application/json") response, err = http.DefaultClient.Do(req) assert.NoError(t, err) + defer func() { _ = response.Body.Close() }() assert.Equal(t, http.StatusOK, response.StatusCode) } @@ -256,7 +256,8 @@ func Test_Middleware_VersionHeader_Enabled_Default(t *testing.T) { log := log.InitLogger() defer func() { _ = log.ZapLogger.Sync() }() - metricsV2, _ := metric.NewMetrics() + metricsV2, err := metric.NewMetrics() + require.NoError(t, err) wsService := service.NewWebsocketService() defer wsService.Close() flagsetManager, _ := service.NewFlagsetManager(proxyConf, log.ZapLogger, nil) @@ -276,6 +277,7 @@ func Test_Middleware_VersionHeader_Enabled_Default(t *testing.T) { response, err := http.Get("http://localhost:11024/health") assert.NoError(t, err) + defer func() { _ = response.Body.Close() }() assert.Equal(t, http.StatusOK, response.StatusCode) assert.Equal(t, proxyConf.Version, response.Header.Get("X-GOFEATUREFLAG-VERSION")) } @@ -299,7 +301,8 @@ func Test_VersionHeader_Disabled(t *testing.T) { log := log.InitLogger() defer func() { _ = log.ZapLogger.Sync() }() - metricsV2, _ := metric.NewMetrics() + metricsV2, err := metric.NewMetrics() + require.NoError(t, err) wsService := service.NewWebsocketService() defer wsService.Close() flagsetManager, _ := service.NewFlagsetManager(proxyConf, log.ZapLogger, nil) @@ -319,6 +322,7 @@ func Test_VersionHeader_Disabled(t *testing.T) { response, err := http.Get("http://localhost:11024/health") assert.NoError(t, err) + defer func() { _ = response.Body.Close() }() assert.Equal(t, http.StatusOK, response.StatusCode) assert.Empty(t, response.Header.Get("X-GOFEATUREFLAG-VERSION")) } @@ -376,7 +380,8 @@ func Test_AuthenticationMiddleware(t *testing.T) { log := log.InitLogger() defer func() { _ = log.ZapLogger.Sync() }() - metricsV2, _ := metric.NewMetrics() + metricsV2, err := metric.NewMetrics() + require.NoError(t, err) wsService := service.NewWebsocketService() defer wsService.Close() flagsetManager, err := service.NewFlagsetManager(proxyConf, log.ZapLogger, nil) @@ -455,7 +460,8 @@ func Test_AuthenticationMiddleware(t *testing.T) { log := log.InitLogger() defer func() { _ = log.ZapLogger.Sync() }() - metricsV2, _ := metric.NewMetrics() + metricsV2, err := metric.NewMetrics() + require.NoError(t, err) wsService := service.NewWebsocketService() defer wsService.Close() flagsetManager, err := service.NewFlagsetManager(proxyConf, log.ZapLogger, nil) @@ -841,7 +847,8 @@ func Test_Starting_RelayProxy_UnixSocket_Authentication(t *testing.T) { log := log.InitLogger() defer func() { _ = log.ZapLogger.Sync() }() - metricsV2, _ := metric.NewMetrics() + metricsV2, err := metric.NewMetrics() + require.NoError(t, err) wsService := service.NewWebsocketService() defer wsService.Close() flagsetManager, err := service.NewFlagsetManager(proxyConf, log.ZapLogger, nil) diff --git a/cmd/relayproxy/config/api_keys.go b/cmd/relayproxy/config/api_keys.go index ec9ed052244..02052d02bce 100644 --- a/cmd/relayproxy/config/api_keys.go +++ b/cmd/relayproxy/config/api_keys.go @@ -29,14 +29,6 @@ func (c *Config) APIKeyExists(apiKey string) bool { return ok } -func (c *Config) GetAPIKeyType(apiKey string) ApiKeyType { - c.preloadAPIKeys() - if keyType, ok := c.apiKeysSet[apiKey]; ok { - return keyType - } - return ErrorKeyType -} - // IsAuthenticationEnabled returns true if we need to be authenticated. func (c *Config) IsAuthenticationEnabled() bool { c.preloadAPIKeys() diff --git a/cmd/relayproxy/config/config.go b/cmd/relayproxy/config/config.go index 5b0dede50aa..7bbb82ce5ef 100644 --- a/cmd/relayproxy/config/config.go +++ b/cmd/relayproxy/config/config.go @@ -226,14 +226,14 @@ func loadConfigFile(log *zap.Logger) { return } - parser := getParserForFile(configFileLocation) + parser := selectParserForFile(configFileLocation) if errBindFile := k.Load(file.Provider(configFileLocation), parser); errBindFile != nil { log.Error("error loading file", zap.Error(errBindFile)) } } -// getParserForFile returns the appropriate parser based on file extension -func getParserForFile(configFileLocation string) koanf.Parser { +// selectParserForFile returns the appropriate parser based on file extension +func selectParserForFile(configFileLocation string) koanf.Parser { ext := filepath.Ext(configFileLocation) switch strings.ToLower(ext) { case ".toml": diff --git a/cmd/relayproxy/config/config_envvar.go b/cmd/relayproxy/config/config_envvar.go index 249fa2b149c..3586d50cc47 100644 --- a/cmd/relayproxy/config/config_envvar.go +++ b/cmd/relayproxy/config/config_envvar.go @@ -11,7 +11,7 @@ import ( ) func mapEnvVariablesProvider(prefix string, log *zap.Logger) koanf.Provider { - return env.ProviderWithValue(prefix, ".", func(key string, v string) (string, interface{}) { + return env.ProviderWithValue(prefix, ".", func(key, v string) (string, interface{}) { key = strings.TrimPrefix(key, prefix) switch { case strings.HasPrefix(key, "RETRIEVERS"), @@ -65,7 +65,7 @@ func loadArrayEnv(s string, v string, configMap map[string]interface{}) (map[str return configMap, err } - configItem := getOrCreateConfigItem(configArray, index) + configItem := fetchOrInitConfigItemAtIndex(configArray, index) keys := paths[2:] if shouldHandleRecursively(prefixKey, keys) { @@ -84,8 +84,8 @@ func normalizePaths(s string) []string { return paths } -// getOrCreateConfigItem retrieves or creates a config item at the specified index -func getOrCreateConfigItem(configArray []interface{}, index int) map[string]interface{} { +// fetchOrInitConfigItemAtIndex retrieves or creates a config item at the specified index +func fetchOrInitConfigItemAtIndex(configArray []interface{}, index int) map[string]interface{} { outRange := index > len(configArray)-1 if outRange { return make(map[string]interface{}) diff --git a/cmd/relayproxy/config/config_otel.go b/cmd/relayproxy/config/config_otel.go index a1a377bf547..228a0c6d51d 100644 --- a/cmd/relayproxy/config/config_otel.go +++ b/cmd/relayproxy/config/config_otel.go @@ -33,17 +33,32 @@ type OtelResource struct { // JaegerSamplerConfiguration is the configuration object to configure the sampling. // Most of the time this configuration is set using environment variables. type JaegerSamplerConfiguration struct { - Sampler struct { - Manager struct { - Host struct { - Port string `mapstructure:"port" koanf:"port"` - } `mapstructure:"host" koanf:"host"` - } `mapstructure:"manager" koanf:"manager"` - Refresh struct { - Interval string `mapstructure:"interval" koanf:"interval"` - } `mapstructure:"refresh" koanf:"refresh"` - Max struct { - Operations int `mapstructure:"operations" koanf:"operations"` - } `mapstructure:"max" koanf:"max"` - } `mapstructure:"sampler" koanf:"sampler"` + Sampler JaegerSampler `mapstructure:"sampler" koanf:"sampler"` +} + +// JaegerSampler is the configuration object to configure the sampling. +type JaegerSampler struct { + Manager JaegerSamplerManager `mapstructure:"manager" koanf:"manager"` + Refresh JaegerSamplerRefresh `mapstructure:"refresh" koanf:"refresh"` + Max JaegerSamplerMax `mapstructure:"max" koanf:"max"` +} + +// JaegerSamplerManager is the configuration object to configure the manager of the sampling. +type JaegerSamplerManager struct { + Host JaegerSamplerManagerHost `mapstructure:"host" koanf:"host"` +} + +// JaegerSamplerManagerHost is the configuration object to configure the host of the manager of the sampling. +type JaegerSamplerManagerHost struct { + Port string `mapstructure:"port" koanf:"port"` +} + +// JaegerSamplerRefresh is the configuration object to configure the refresh of the sampling. +type JaegerSamplerRefresh struct { + Interval string `mapstructure:"interval" koanf:"interval"` +} + +// JaegerSamplerMax is the configuration object to configure the max of the sampling. +type JaegerSamplerMax struct { + Operations int `mapstructure:"operations" koanf:"operations"` } diff --git a/cmd/relayproxy/config/config_server.go b/cmd/relayproxy/config/config_server.go index 20d20bcbb2b..2d2c00ba99c 100644 --- a/cmd/relayproxy/config/config_server.go +++ b/cmd/relayproxy/config/config_server.go @@ -56,9 +56,9 @@ type Server struct { AwsApiGatewayBasePath string `mapstructure:"awsApiGatewayBasePath" koanf:"awsapigatewaybasepath"` } -// GetMonitoringPort returns the monitoring port, checking first the top-level config +// EffectiveMonitoringPort returns the monitoring port, checking first the top-level config // and then the server config. -func (c *Config) GetMonitoringPort(logger *zap.Logger) int { +func (c *Config) EffectiveMonitoringPort(logger *zap.Logger) int { if c.Server.MonitoringPort != 0 { return c.Server.MonitoringPort } @@ -72,17 +72,17 @@ func (c *Config) GetMonitoringPort(logger *zap.Logger) int { return 0 } -// GetServerHost returns the server host, defaulting to "0.0.0.0" if not set. -func (c *Config) GetServerHost() string { +// ServerHost returns the server host, defaulting to "0.0.0.0" if not set. +func (c *Config) ServerHost() string { if c.Server.Host != "" { return c.Server.Host } return "0.0.0.0" } -// GetServerPort returns the server port, checking first the server config +// ServerPort returns the server port, checking first the server config // and then the top-level config, defaulting to 1031 if not set. -func (c *Config) GetServerPort(logger *zap.Logger) int { +func (c *Config) ServerPort(logger *zap.Logger) int { if c.Server.Port != 0 { return c.Server.Port } @@ -96,9 +96,9 @@ func (c *Config) GetServerPort(logger *zap.Logger) int { return 1031 } -// GetServerMode returns the server mode, checking first the server config +// ServerMode returns the server mode, checking first the server config // and then the top-level config, defaulting to HTTP if not set. -func (c *Config) GetServerMode(logger *zap.Logger) ServerMode { +func (c *Config) ServerMode(logger *zap.Logger) ServerMode { if c.Server.Mode != "" { return c.Server.Mode } @@ -114,9 +114,9 @@ func (c *Config) GetServerMode(logger *zap.Logger) ServerMode { return ServerModeHTTP } -// GetLambdaAdapter returns the lambda adapter, checking first the server config +// LambdaAdapter returns the lambda adapter, checking first the server config // and then the top-level config, defaulting to APIGatewayV2 if not set. -func (c *Config) GetLambdaAdapter(logger *zap.Logger) LambdaAdapter { +func (c *Config) LambdaAdapter(logger *zap.Logger) LambdaAdapter { if c.Server.LambdaAdapter != "" { return c.Server.LambdaAdapter } @@ -132,9 +132,9 @@ func (c *Config) GetLambdaAdapter(logger *zap.Logger) LambdaAdapter { return LambdaAdapterAPIGatewayV2 } -// GetAwsApiGatewayBasePath returns the AWS API Gateway base path, checking first the server config +// EffectiveAwsApiGatewayBasePath returns the AWS API Gateway base path, checking first the server config // and then the top-level config, defaulting to empty string if not set. -func (c *Config) GetAwsApiGatewayBasePath(logger *zap.Logger) string { +func (c *Config) EffectiveAwsApiGatewayBasePath(logger *zap.Logger) string { if c.Server.AwsApiGatewayBasePath != "" { return c.Server.AwsApiGatewayBasePath } @@ -150,7 +150,7 @@ func (c *Config) GetAwsApiGatewayBasePath(logger *zap.Logger) string { return "" } -// GetUnixSocketPath returns the unix socket path. -func (c *Config) GetUnixSocketPath() string { +// UnixSocketPath returns the unix socket path. +func (c *Config) UnixSocketPath() string { return c.Server.UnixSocketPath } diff --git a/cmd/relayproxy/config/config_server_test.go b/cmd/relayproxy/config/config_server_test.go index 6a83b91d23c..fe96f2094f4 100644 --- a/cmd/relayproxy/config/config_server_test.go +++ b/cmd/relayproxy/config/config_server_test.go @@ -10,7 +10,7 @@ import ( "go.uber.org/zap/zaptest/observer" ) -func TestConfig_GetMonitoringPort(t *testing.T) { +func TestConfig_EffectiveMonitoringPort(t *testing.T) { tests := []struct { name string config *config.Config @@ -88,7 +88,7 @@ func TestConfig_GetMonitoringPort(t *testing.T) { if tt.setLoggerNil { logger = nil } - got := tt.config.GetMonitoringPort(logger) + got := tt.config.EffectiveMonitoringPort(logger) assert.Equal(t, tt.wantPort, got) if observedLogs != nil && tt.wantDeprecationWarned { @@ -101,7 +101,7 @@ func TestConfig_GetMonitoringPort(t *testing.T) { } } -func TestConfig_GetServerHost(t *testing.T) { +func TestConfig_ServerHost(t *testing.T) { tests := []struct { name string config *config.Config @@ -147,13 +147,13 @@ func TestConfig_GetServerHost(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := tt.config.GetServerHost() + got := tt.config.ServerHost() assert.Equal(t, tt.wantHost, got) }) } } -func TestConfig_GetServerPort(t *testing.T) { +func TestConfig_ServerPort(t *testing.T) { tests := []struct { name string config *config.Config @@ -232,7 +232,7 @@ func TestConfig_GetServerPort(t *testing.T) { logger = nil } - got := tt.config.GetServerPort(logger) + got := tt.config.ServerPort(logger) assert.Equal(t, tt.wantPort, got) if observedLogs != nil && tt.wantDeprecationWarned { @@ -245,7 +245,7 @@ func TestConfig_GetServerPort(t *testing.T) { } } -func TestConfig_GetServerMode(t *testing.T) { +func TestConfig_ServerMode(t *testing.T) { tests := []struct { name string config *config.Config @@ -332,7 +332,7 @@ func TestConfig_GetServerMode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Note: GetServerMode uses zap.L() instead of the passed logger, + // Note: ServerMode uses zap.L() instead of the passed logger, // so we need to replace the global logger to capture deprecation warnings var logger *zap.Logger var observedLogs *observer.ObservedLogs @@ -349,7 +349,7 @@ func TestConfig_GetServerMode(t *testing.T) { logger = zap.NewNop() } - got := tt.config.GetServerMode(logger) + got := tt.config.ServerMode(logger) assert.Equal(t, tt.wantMode, got) if observedLogs != nil && tt.wantDeprecationWarned { @@ -360,7 +360,7 @@ func TestConfig_GetServerMode(t *testing.T) { } } -func TestConfig_GetLambdaAdapter(t *testing.T) { +func TestConfig_LambdaAdapter(t *testing.T) { tests := []struct { name string config *config.Config @@ -448,7 +448,7 @@ func TestConfig_GetLambdaAdapter(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Note: GetLambdaAdapter uses zap.L() instead of the passed logger, + // Note: LambdaAdapter uses zap.L() instead of the passed logger, // so we need to replace the global logger to capture deprecation warnings var logger *zap.Logger var observedLogs *observer.ObservedLogs @@ -465,7 +465,7 @@ func TestConfig_GetLambdaAdapter(t *testing.T) { logger = zap.NewNop() } - got := tt.config.GetLambdaAdapter(logger) + got := tt.config.LambdaAdapter(logger) assert.Equal(t, tt.wantAdapter, got) if observedLogs != nil && tt.wantDeprecationWarned { @@ -476,7 +476,7 @@ func TestConfig_GetLambdaAdapter(t *testing.T) { } } -func TestConfig_GetAwsApiGatewayBasePath(t *testing.T) { +func TestConfig_EffectiveAwsApiGatewayBasePath(t *testing.T) { tests := []struct { name string config *config.Config @@ -553,7 +553,7 @@ func TestConfig_GetAwsApiGatewayBasePath(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Note: GetAwsApiGatewayBasePath uses zap.L() instead of the passed logger, + // Note: EffectiveAwsApiGatewayBasePath uses zap.L() instead of the passed logger, // so we need to replace the global logger to capture deprecation warnings var logger *zap.Logger var observedLogs *observer.ObservedLogs @@ -570,7 +570,7 @@ func TestConfig_GetAwsApiGatewayBasePath(t *testing.T) { logger = zap.NewNop() } - got := tt.config.GetAwsApiGatewayBasePath(logger) + got := tt.config.EffectiveAwsApiGatewayBasePath(logger) assert.Equal(t, tt.wantBasePath, got) if observedLogs != nil && tt.wantDeprecationWarned { @@ -581,7 +581,7 @@ func TestConfig_GetAwsApiGatewayBasePath(t *testing.T) { } } -func TestConfig_GetUnixSocketPath(t *testing.T) { +func TestConfig_UnixSocketPath(t *testing.T) { tests := []struct { name string config *config.Config @@ -627,7 +627,7 @@ func TestConfig_GetUnixSocketPath(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := tt.config.GetUnixSocketPath() + got := tt.config.UnixSocketPath() assert.Equal(t, tt.wantSocketPath, got) }) } diff --git a/cmd/relayproxy/config/config_validator.go b/cmd/relayproxy/config/config_validator.go index 5c1eecc8738..5710f2cb537 100644 --- a/cmd/relayproxy/config/config_validator.go +++ b/cmd/relayproxy/config/config_validator.go @@ -31,9 +31,9 @@ func (c *Config) IsValid() error { // validateServerConfig validates the server configuration func (c *Config) validateServerConfig() error { - mode := c.GetServerMode(nil) + mode := c.ServerMode(nil) if mode == ServerModeUnixSocket { - if c.GetUnixSocketPath() == "" { + if c.UnixSocketPath() == "" { return errors.New("unixSocketPath must be set when server mode is unixsocket") } } diff --git a/cmd/relayproxy/controller/all_flags.go b/cmd/relayproxy/controller/all_flags.go index 57e0dbf1074..70f83bfce4b 100644 --- a/cmd/relayproxy/controller/all_flags.go +++ b/cmd/relayproxy/controller/all_flags.go @@ -61,7 +61,7 @@ func (h *allFlags) Handler(c echo.Context) error { _, span := tracer.Start(c.Request().Context(), "AllFlagsState") defer span.End() - flagset, httpErr := helper.GetFlagSet(h.flagsetManager, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetManager, helper.APIKey(c)) if httpErr != nil { return httpErr } diff --git a/cmd/relayproxy/controller/collect_eval_data.go b/cmd/relayproxy/controller/collect_eval_data.go index f76dc301b65..47b0f031c8d 100644 --- a/cmd/relayproxy/controller/collect_eval_data.go +++ b/cmd/relayproxy/controller/collect_eval_data.go @@ -74,7 +74,7 @@ func (h *collectEvalData) Handler(c echo.Context) error { span.SetAttributes(attribute.Int("collectEventData.eventCollectionSize", len(reqBody.Events))) - flagset, httpErr := helper.GetFlagSet(h.flagsetManager, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetManager, helper.APIKey(c)) if httpErr != nil { return httpErr } diff --git a/cmd/relayproxy/controller/flag_change.go b/cmd/relayproxy/controller/flag_change.go index 20b958a974a..af0fdfcfa0f 100644 --- a/cmd/relayproxy/controller/flag_change.go +++ b/cmd/relayproxy/controller/flag_change.go @@ -43,7 +43,7 @@ type FlagChangeResponse struct { // @Failure 500 {object} modeldocs.HTTPErrorDoc "Internal server error" // @Router /v1/flag/change [get] func (h *FlagChangeAPICtrl) Handler(c echo.Context) error { - flagset, httpErr := helper.GetFlagSet(h.flagsetManager, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetManager, helper.APIKey(c)) if httpErr != nil { return httpErr } diff --git a/cmd/relayproxy/controller/flag_configuration.go b/cmd/relayproxy/controller/flag_configuration.go index c13b2017265..c3fdb633815 100644 --- a/cmd/relayproxy/controller/flag_configuration.go +++ b/cmd/relayproxy/controller/flag_configuration.go @@ -76,7 +76,7 @@ func (h *FlagConfigurationAPICtrl) Handler(c echo.Context) error { ) } - flagset, httpErr := helper.GetFlagSet(h.flagsetManager, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetManager, helper.APIKey(c)) if httpErr != nil { return httpErr } diff --git a/cmd/relayproxy/controller/flag_eval.go b/cmd/relayproxy/controller/flag_eval.go index 5a3581cbade..45ae8451104 100644 --- a/cmd/relayproxy/controller/flag_eval.go +++ b/cmd/relayproxy/controller/flag_eval.go @@ -74,7 +74,7 @@ func (h *flagEval) Handler(c echo.Context) error { _, span := tracer.Start(c.Request().Context(), "flagEvaluation") defer span.End() - flagset, httpErr := helper.GetFlagSet(h.flagsetMngr, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetMngr, helper.APIKey(c)) if httpErr != nil { return httpErr } @@ -94,7 +94,7 @@ func (h *flagEval) Handler(c echo.Context) error { attribute.String("flagEvaluation.value", fmt.Sprintf("%v", flagValue.Value)), ) - if flagsetName, err := h.flagsetMngr.GetFlagSetName(helper.GetAPIKey(c)); err == nil { + if flagsetName, err := h.flagsetMngr.FlagSetName(helper.APIKey(c)); err == nil { span.SetAttributes(attribute.String("flagEvaluation.flagSetName", flagsetName)) } diff --git a/cmd/relayproxy/controller/retriever_refresh.go b/cmd/relayproxy/controller/retriever_refresh.go index bcca7425f97..deafa6d584d 100644 --- a/cmd/relayproxy/controller/retriever_refresh.go +++ b/cmd/relayproxy/controller/retriever_refresh.go @@ -47,7 +47,7 @@ func NewForceFlagsRefresh(flagsetManager service.FlagsetManager, metrics metric. func (h *forceFlagsRefresh) Handler(c echo.Context) error { h.metrics.IncForceRefresh() - flagset, httpErr := helper.GetFlagSet(h.flagsetManager, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetManager, helper.APIKey(c)) if httpErr != nil { return httpErr } diff --git a/cmd/relayproxy/controller/retriever_refresh_test.go b/cmd/relayproxy/controller/retriever_refresh_test.go index 3a089c17eed..bc452350cfc 100644 --- a/cmd/relayproxy/controller/retriever_refresh_test.go +++ b/cmd/relayproxy/controller/retriever_refresh_test.go @@ -47,7 +47,7 @@ func Test_retriever_refresh_Handler_valid(t *testing.T) { assert.NoError(t, err, "impossible to create flagset manager") // Get the default flagset to check refresh date - defaultFlagset := flagsetManager.GetDefaultFlagSet() + defaultFlagset := flagsetManager.Default() previousRefresh := defaultFlagset.GetCacheRefreshDate() ctrl := controller.NewForceFlagsRefresh(flagsetManager, metric.Metrics{}) diff --git a/cmd/relayproxy/helper/echo_helper.go b/cmd/relayproxy/helper/echo_helper.go index 8ebd1865a69..051f9f8a5be 100644 --- a/cmd/relayproxy/helper/echo_helper.go +++ b/cmd/relayproxy/helper/echo_helper.go @@ -9,10 +9,10 @@ import ( "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/service" ) -// GetAPIKey extracts the API key from the Authorization header. +// APIKey extracts the API key from the Authorization header. // It removes the "Bearer " prefix if it exists. // For other schemes, it returns the raw header value or an empty string if the header is missing. -func GetAPIKey(c echo.Context) string { +func APIKey(c echo.Context) string { apiKey := c.Request().Header.Get("Authorization") const bearerPrefix = "Bearer " if len(apiKey) >= len(bearerPrefix) && strings.EqualFold(apiKey[:len(bearerPrefix)], bearerPrefix) { @@ -21,13 +21,13 @@ func GetAPIKey(c echo.Context) string { return apiKey } -// GetFlagSet retrieves the flagset for the given API key from the flagset manager +// FlagSet retrieves the flagset for the given API key from the flagset manager // This layer ensure that the flagset manager is initialized and that the API key is valid -func GetFlagSet(flagsetManager service.FlagsetManager, apiKey string) (*ffclient.GoFeatureFlag, *echo.HTTPError) { +func FlagSet(flagsetManager service.FlagsetManager, apiKey string) (*ffclient.GoFeatureFlag, *echo.HTTPError) { if flagsetManager == nil { return nil, echo.NewHTTPError(http.StatusInternalServerError, "flagset manager is not initialized") } - flagset, err := flagsetManager.GetFlagSet(apiKey) + flagset, err := flagsetManager.FlagSet(apiKey) if err != nil { return nil, echo.NewHTTPError(http.StatusBadRequest, "error while getting flagset: "+err.Error()) } diff --git a/cmd/relayproxy/helper/echo_helper_test.go b/cmd/relayproxy/helper/echo_helper_test.go index 7d551a89bfc..301edc393db 100644 --- a/cmd/relayproxy/helper/echo_helper_test.go +++ b/cmd/relayproxy/helper/echo_helper_test.go @@ -13,7 +13,7 @@ import ( "github.com/thomaspoignant/go-feature-flag/cmd/relayproxy/service" ) -func TestGetAPIKey(t *testing.T) { +func TestAPIKey(t *testing.T) { tests := []struct { name string authorization string @@ -165,7 +165,7 @@ func TestGetAPIKey(t *testing.T) { } // Call the function - result := helper.GetAPIKey(c) + result := helper.APIKey(c) // Assert the result assert.Equal(t, tt.expectedAPIKey, result, "GetAPIKey() = %v, want %v", result, tt.expectedAPIKey) @@ -173,7 +173,7 @@ func TestGetAPIKey(t *testing.T) { } } -func TestGetFlagSet(t *testing.T) { +func TestFlagSet(t *testing.T) { tests := []struct { name string flagsetManager service.FlagsetManager @@ -212,7 +212,7 @@ func TestGetFlagSet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - flagset, err := helper.GetFlagSet(tt.flagsetManager, tt.apiKey) + flagset, err := helper.FlagSet(tt.flagsetManager, tt.apiKey) if tt.wantError { assert.Error(t, err) assert.Nil(t, flagset) @@ -230,10 +230,10 @@ func TestGetFlagSet(t *testing.T) { } } -func TestGetFlagSet_Integration(t *testing.T) { +func TestFlagSet_Integration(t *testing.T) { t.Run("should handle specific error messages correctly", func(t *testing.T) { mockManager := &MockFlagsetManager{err: errors.New("test error")} - flagset, err := helper.GetFlagSet(mockManager, "test-key") + flagset, err := helper.FlagSet(mockManager, "test-key") assert.Error(t, err) assert.Nil(t, flagset) assert.Contains(t, err.Message, "error while getting flagset:") @@ -241,7 +241,7 @@ func TestGetFlagSet_Integration(t *testing.T) { t.Run("should return flagset when manager returns valid flagset", func(t *testing.T) { mockManager := &MockFlagsetManager{flagset: &ffclient.GoFeatureFlag{}} - flagset, err := helper.GetFlagSet(mockManager, "valid-key") + flagset, err := helper.FlagSet(mockManager, "valid-key") if err != nil || flagset == nil { t.Logf("DEBUG: err=%v, flagset=%v", err, flagset) } @@ -256,19 +256,19 @@ type MockFlagsetManager struct { err error } -func (m *MockFlagsetManager) GetFlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) { +func (m *MockFlagsetManager) FlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) { return m.flagset, m.err } -func (m *MockFlagsetManager) GetFlagSetName(apiKey string) (string, error) { +func (m *MockFlagsetManager) FlagSetName(apiKey string) (string, error) { return "", nil } -func (m *MockFlagsetManager) GetFlagSets() (map[string]*ffclient.GoFeatureFlag, error) { +func (m *MockFlagsetManager) AllFlagSets() (map[string]*ffclient.GoFeatureFlag, error) { return nil, nil } -func (m *MockFlagsetManager) GetDefaultFlagSet() *ffclient.GoFeatureFlag { +func (m *MockFlagsetManager) Default() *ffclient.GoFeatureFlag { return nil } diff --git a/cmd/relayproxy/main.go b/cmd/relayproxy/main.go index cfd751efedd..2fe127a67ac 100644 --- a/cmd/relayproxy/main.go +++ b/cmd/relayproxy/main.go @@ -77,7 +77,7 @@ func main() { // Init swagger docs.SwaggerInfo.Version = proxyConf.Version - docs.SwaggerInfo.Host = fmt.Sprintf("%s:%d", proxyConf.Host, proxyConf.GetServerPort(logger.ZapLogger)) + docs.SwaggerInfo.Host = fmt.Sprintf("%s:%d", proxyConf.Host, proxyConf.ServerPort(logger.ZapLogger)) // Set the version for the prometheus version collector promversion.Version = version diff --git a/cmd/relayproxy/ofrep/evaluate.go b/cmd/relayproxy/ofrep/evaluate.go index f4f95f2c127..71ee67af05c 100644 --- a/cmd/relayproxy/ofrep/evaluate.go +++ b/cmd/relayproxy/ofrep/evaluate.go @@ -82,7 +82,7 @@ func (h *EvaluateCtrl) Evaluate(c echo.Context) error { _, span := tracer.Start(c.Request().Context(), "flagEvaluation") defer span.End() - flagset, httpErr := helper.GetFlagSet(h.flagsetManager, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetManager, helper.APIKey(c)) if httpErr != nil { return httpErr } @@ -177,7 +177,7 @@ func (h *EvaluateCtrl) BulkEvaluate(c echo.Context) error { _, span := tracer.Start(c.Request().Context(), "AllFlagsState") defer span.End() - flagset, httpErr := helper.GetFlagSet(h.flagsetManager, helper.GetAPIKey(c)) + flagset, httpErr := helper.FlagSet(h.flagsetManager, helper.APIKey(c)) if httpErr != nil { return httpErr } diff --git a/cmd/relayproxy/service/flagset_manager.go b/cmd/relayproxy/service/flagset_manager.go index 98c6f9144e9..33c242138f9 100644 --- a/cmd/relayproxy/service/flagset_manager.go +++ b/cmd/relayproxy/service/flagset_manager.go @@ -22,14 +22,14 @@ const ( // FlagsetManager is the interface for managing flagsets. // It is used to retrieve the flagset linked to the API Key. type FlagsetManager interface { - // GetFlagSet returns the flag set linked to the API Key - GetFlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) - // GetFlagSetName returns the name of the flagset linked to the API Key - GetFlagSetName(apiKey string) (string, error) - // GetFlagSets returns all flag sets of the flagset manager - GetFlagSets() (map[string]*ffclient.GoFeatureFlag, error) - // GetDefaultFlagSet returns the default flagset - GetDefaultFlagSet() *ffclient.GoFeatureFlag + // FlagSet returns the flag set linked to the API Key + FlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) + // FlagSetName returns the name of the flagset linked to the API Key + FlagSetName(apiKey string) (string, error) + // AllFlagSets returns all flag sets of the flagset manager + AllFlagSets() (map[string]*ffclient.GoFeatureFlag, error) + // Default returns the default flagset + Default() *ffclient.GoFeatureFlag // IsDefaultFlagSet returns true if the manager is in default mode (no flagsets configured) IsDefaultFlagSet() bool // Close closes the flagset manager @@ -155,8 +155,8 @@ func newFlagsetManagerWithFlagsets( }, nil } -// GetFlagSet is returning the flag set linked to the API Key -func (m *flagsetManagerImpl) GetFlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) { +// FlagSet is returning the flag set linked to the API Key +func (m *flagsetManagerImpl) FlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) { switch m.mode { case flagsetManagerModeFlagsets: if apiKey == "" { @@ -180,8 +180,8 @@ func (m *flagsetManagerImpl) GetFlagSet(apiKey string) (*ffclient.GoFeatureFlag, } } -// GetFlagSetName returns the name of the flagset linked to the API Key -func (m *flagsetManagerImpl) GetFlagSetName(apiKey string) (string, error) { +// FlagSetName returns the name of the flagset linked to the API Key +func (m *flagsetManagerImpl) FlagSetName(apiKey string) (string, error) { switch m.mode { case flagsetManagerModeFlagsets: if name, ok := m.APIKeysToFlagSetName[apiKey]; ok { @@ -193,8 +193,8 @@ func (m *flagsetManagerImpl) GetFlagSetName(apiKey string) (string, error) { } } -// GetFlagSets returns the flag sets of the flagset manager. -func (m *flagsetManagerImpl) GetFlagSets() (map[string]*ffclient.GoFeatureFlag, error) { +// AllFlagSets returns the flag sets of the flagset manager. +func (m *flagsetManagerImpl) AllFlagSets() (map[string]*ffclient.GoFeatureFlag, error) { switch m.mode { case flagsetManagerModeFlagsets: if len(m.FlagSets) == 0 { @@ -211,8 +211,8 @@ func (m *flagsetManagerImpl) GetFlagSets() (map[string]*ffclient.GoFeatureFlag, } } -// GetDefaultFlagSet returns the default flagset -func (m *flagsetManagerImpl) GetDefaultFlagSet() *ffclient.GoFeatureFlag { +// Default returns the default flagset +func (m *flagsetManagerImpl) Default() *ffclient.GoFeatureFlag { return m.DefaultFlagSet } diff --git a/cmd/relayproxy/service/flagset_manager_test.go b/cmd/relayproxy/service/flagset_manager_test.go index bbf3aa3381b..67a8d6ecec6 100644 --- a/cmd/relayproxy/service/flagset_manager_test.go +++ b/cmd/relayproxy/service/flagset_manager_test.go @@ -111,7 +111,7 @@ func TestNewFlagsetManager(t *testing.T) { } } -func TestFlagsetManager_GetFlagSet(t *testing.T) { +func TestFlagsetManager_FlagSet(t *testing.T) { flagConfig := "../testdata/controller/configuration_flags.yaml" // Test flagset mode @@ -140,23 +140,23 @@ func TestFlagsetManager_GetFlagSet(t *testing.T) { defer manager.Close() t.Run("valid api key", func(t *testing.T) { - flagset, err := manager.GetFlagSet("test-api-key") + flagset, err := manager.FlagSet("test-api-key") assert.NoError(t, err) assert.NotNil(t, flagset) }) t.Run("invalid api key", func(t *testing.T) { - flagset, err := manager.GetFlagSet("invalid-key") + flagset, err := manager.FlagSet("invalid-key") assert.Error(t, err) assert.Nil(t, flagset) }) t.Run("empty api key", func(t *testing.T) { - flagset, err := manager.GetFlagSet("") + flagset, err := manager.FlagSet("") assert.Error(t, err) assert.Nil(t, flagset) }) t.Run("empty api key", func(t *testing.T) { - flagset, err := manager.GetFlagSet("") + flagset, err := manager.FlagSet("") assert.Error(t, err) assert.Nil(t, flagset) }) @@ -183,14 +183,14 @@ func TestFlagsetManager_GetFlagSet(t *testing.T) { defer manager.Close() t.Run("empty api key should work", func(t *testing.T) { - flagset, err := manager.GetFlagSet("") + flagset, err := manager.FlagSet("") assert.NoError(t, err) assert.NotNil(t, flagset) }) }) } -func TestFlagsetManager_GetFlagSetName(t *testing.T) { +func TestFlagsetManager_FlagSetName(t *testing.T) { flagConfig := "../testdata/controller/configuration_flags.yaml" // Test flagset mode @@ -219,12 +219,12 @@ func TestFlagsetManager_GetFlagSetName(t *testing.T) { defer manager.Close() t.Run("existing api key", func(t *testing.T) { - name, err := manager.GetFlagSetName("test-api-key") + name, err := manager.FlagSetName("test-api-key") assert.NoError(t, err) assert.Equal(t, "test-flagset", name) }) t.Run("non-existing api key", func(t *testing.T) { - name, err := manager.GetFlagSetName("invalid-key") + name, err := manager.FlagSetName("invalid-key") assert.Error(t, err) assert.Equal(t, "", name) assert.Equal(t, "no flag set associated to the API key", err.Error()) @@ -252,14 +252,14 @@ func TestFlagsetManager_GetFlagSetName(t *testing.T) { defer manager.Close() t.Run("empty api key should return default", func(t *testing.T) { - name, err := manager.GetFlagSetName("") + name, err := manager.FlagSetName("") assert.NoError(t, err) assert.Equal(t, "default", name) }) }) } -func TestFlagsetManager_GetFlagSets(t *testing.T) { +func TestFlagsetManager_AllFlagSets(t *testing.T) { flagConfig := "../testdata/controller/configuration_flags.yaml" // Test flagset mode @@ -297,7 +297,7 @@ func TestFlagsetManager_GetFlagSets(t *testing.T) { assert.NotNil(t, manager) defer manager.Close() - flagsets, err := manager.GetFlagSets() + flagsets, err := manager.AllFlagSets() assert.NoError(t, err) assert.Len(t, flagsets, 2) assert.Contains(t, flagsets, "test-flagset-1") @@ -338,7 +338,7 @@ func TestFlagsetManager_GetFlagSets(t *testing.T) { assert.NotNil(t, manager) defer manager.Close() - flagsets, err := manager.GetFlagSets() + flagsets, err := manager.AllFlagSets() assert.NoError(t, err) assert.Len(t, flagsets, 2) assert.NotContains(t, flagsets, "default") @@ -365,14 +365,14 @@ func TestFlagsetManager_GetFlagSets(t *testing.T) { assert.NotNil(t, manager) defer manager.Close() - flagsets, err := manager.GetFlagSets() + flagsets, err := manager.AllFlagSets() assert.NoError(t, err) assert.Len(t, flagsets, 1) assert.Contains(t, flagsets, "default") }) } -func TestFlagsetManager_GetDefaultFlagSet(t *testing.T) { +func TestFlagsetManager_Default(t *testing.T) { flagConfig := "../testdata/controller/configuration_flags.yaml" // Test default mode @@ -395,7 +395,7 @@ func TestFlagsetManager_GetDefaultFlagSet(t *testing.T) { assert.NotNil(t, manager) defer manager.Close() - defaultFlagset := manager.GetDefaultFlagSet() + defaultFlagset := manager.Default() assert.NotNil(t, defaultFlagset) }) @@ -424,7 +424,7 @@ func TestFlagsetManager_GetDefaultFlagSet(t *testing.T) { assert.NotNil(t, manager) defer manager.Close() - defaultFlagset := manager.GetDefaultFlagSet() + defaultFlagset := manager.Default() assert.Nil(t, defaultFlagset) }) } diff --git a/cmd/relayproxy/service/monitoring.go b/cmd/relayproxy/service/monitoring.go index 0cd2385b4fd..123296fa944 100644 --- a/cmd/relayproxy/service/monitoring.go +++ b/cmd/relayproxy/service/monitoring.go @@ -37,16 +37,16 @@ func (m *monitoringImpl) Info() (model.InfoResponse, error) { if m.flagsetManager == nil { return model.InfoResponse{}, fmt.Errorf("flagset manager is not initialized") } - flagSets, err := m.flagsetManager.GetFlagSets() + flagSets, err := m.flagsetManager.AllFlagSets() if err != nil { return model.InfoResponse{}, err } if m.flagsetManager.IsDefaultFlagSet() { - if m.flagsetManager.GetDefaultFlagSet() == nil { + if m.flagsetManager.Default() == nil { return model.InfoResponse{}, fmt.Errorf("no default flagset configured") } - cacheRefreshDate := m.flagsetManager.GetDefaultFlagSet().GetCacheRefreshDate() + cacheRefreshDate := m.flagsetManager.Default().GetCacheRefreshDate() return model.InfoResponse{ LatestCacheRefresh: &cacheRefreshDate, }, nil diff --git a/cmd/relayproxy/service/monitoring_test.go b/cmd/relayproxy/service/monitoring_test.go index 06d3814e072..59666142909 100644 --- a/cmd/relayproxy/service/monitoring_test.go +++ b/cmd/relayproxy/service/monitoring_test.go @@ -217,7 +217,7 @@ func TestMonitoringInfoErrors(t *testing.T) { assert.Equal(t, model.InfoResponse{}, info, "Expected empty InfoResponse") }) - t.Run("GetFlagSets error should be propagated", func(t *testing.T) { + t.Run("FlagSets error should be propagated", func(t *testing.T) { mockManager := &mock.MockFlagsetManager{ FlagSets: nil, DefaultFlagSet: nil, @@ -225,7 +225,7 @@ func TestMonitoringInfoErrors(t *testing.T) { } monitoring := service.NewMonitoring(mockManager) info, err := monitoring.Info() - assert.Error(t, err, "Expected error from GetFlagSets") + assert.Error(t, err, "Expected error from FlagSets") assert.Equal(t, "failed to get flagsets", err.Error()) assert.Equal(t, model.InfoResponse{}, info, "Expected empty InfoResponse") }) diff --git a/cmd/relayproxy/testdata/mock/flagset_manager.go b/cmd/relayproxy/testdata/mock/flagset_manager.go index f208a2dcc03..f0f65558958 100644 --- a/cmd/relayproxy/testdata/mock/flagset_manager.go +++ b/cmd/relayproxy/testdata/mock/flagset_manager.go @@ -10,19 +10,19 @@ type MockFlagsetManager struct { GetFlagSetsErr error } -func (m *MockFlagsetManager) GetFlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) { +func (m *MockFlagsetManager) FlagSet(apiKey string) (*ffclient.GoFeatureFlag, error) { return nil, nil } -func (m *MockFlagsetManager) GetFlagSetName(apiKey string) (string, error) { +func (m *MockFlagsetManager) FlagSetName(apiKey string) (string, error) { return "", nil } -func (m *MockFlagsetManager) GetFlagSets() (map[string]*ffclient.GoFeatureFlag, error) { +func (m *MockFlagsetManager) AllFlagSets() (map[string]*ffclient.GoFeatureFlag, error) { return m.FlagSets, m.GetFlagSetsErr } -func (m *MockFlagsetManager) GetDefaultFlagSet() *ffclient.GoFeatureFlag { +func (m *MockFlagsetManager) Default() *ffclient.GoFeatureFlag { return m.DefaultFlagSet } diff --git a/website/src/components/doc/featureTable/index.js b/website/src/components/doc/featureTable/index.js index 07e4f1e6cd3..08c79ed4289 100644 --- a/website/src/components/doc/featureTable/index.js +++ b/website/src/components/doc/featureTable/index.js @@ -11,84 +11,88 @@ import PropTypes from 'prop-types'; * @param {string} sdk.featureList[].description - Optional description of the feature * @returns {JSX.Element} A table showing the features and their status */ -export const FeatureTable = ({ sdk }) => { - if (!sdk || !sdk.featureList || !Array.isArray(sdk.featureList)) { - return null; - } +export const FeatureTable = ({sdk}) => { + if (!sdk || !sdk.featureList || !Array.isArray(sdk.featureList)) { + return null; + } - const getStatusIcon = (status) => { - switch (status.toLowerCase()) { - case 'done': - return ; - case 'WIP': - return - default: - return ; - } - }; + const getStatusIcon = status => { + switch (status.toLowerCase()) { + case 'done': + return ; + case 'WIP': + return ; + default: + return ; + } + }; - return ( -
- - - - - - - - - - {sdk.featureList.sort((a, b) => a.status.localeCompare(b.status)).map((feature, index) => ( - - - - - - ))} - -
- Status - - Feature - - Description -
- {getStatusIcon(feature.status)} - - {feature.name} - - {feature.description ?? "N/A"} -
+ return ( +
+ + + + + + + + + + {sdk.featureList + .sort((a, b) => a.status.localeCompare(b.status)) + .map((feature, index) => ( + + + + + + ))} + +
+ Status + + Feature + + Description +
+ {getStatusIcon(feature.status)} + + {feature.name} + + {feature.description ?? 'N/A'} +
-
- - - Implemented - - - - In-progress - - - - Not implemented yet - -
-
- ); +
+ + + Implemented + + + + In-progress + + + + Not implemented yet + +
+
+ ); }; FeatureTable.propTypes = { - sdk: PropTypes.shape({ - name: PropTypes.string, - featureList: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - status: PropTypes.string.isRequired, - description: PropTypes.string, - }) - ), - }).isRequired, + sdk: PropTypes.shape({ + name: PropTypes.string, + featureList: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + status: PropTypes.string.isRequired, + description: PropTypes.string, + }) + ), + }).isRequired, }; export default FeatureTable; diff --git a/website/src/theme/Footer/Links/Simple/index.js b/website/src/theme/Footer/Links/Simple/index.js index f5df2affa75..2a236a4e48d 100644 --- a/website/src/theme/Footer/Links/Simple/index.js +++ b/website/src/theme/Footer/Links/Simple/index.js @@ -1,9 +1,12 @@ import React from 'react'; +import PropTypes from 'prop-types'; import clsx from 'clsx'; import LinkItem from '@theme/Footer/LinkItem'; + function Separator() { return ·; } + function SimpleLinkItem({item}) { return item.html ? ( ); } + +SimpleLinkItem.propTypes = { + item: PropTypes.shape({ + html: PropTypes.string, + className: PropTypes.string, + to: PropTypes.string, + href: PropTypes.string, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + prependBaseUrlToHref: PropTypes.bool, + }).isRequired, +}; export default function FooterLinksSimple({links}) { return (
{links.map((item, i) => ( - + {links.length !== i + 1 && } @@ -30,3 +44,16 @@ export default function FooterLinksSimple({links}) {
); } + +FooterLinksSimple.propTypes = { + links: PropTypes.arrayOf( + PropTypes.shape({ + html: PropTypes.string, + className: PropTypes.string, + to: PropTypes.string, + href: PropTypes.string, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + prependBaseUrlToHref: PropTypes.bool, + }) + ).isRequired, +}; diff --git a/website/src/theme/Footer/Logo/index.js b/website/src/theme/Footer/Logo/index.js index 2fdd1ab6a39..050383a0efe 100644 --- a/website/src/theme/Footer/Logo/index.js +++ b/website/src/theme/Footer/Logo/index.js @@ -1,9 +1,11 @@ import React from 'react'; +import PropTypes from 'prop-types'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import ThemedImage from '@theme/ThemedImage'; import styles from './styles.module.css'; + function LogoImage({logo}) { const {withBaseUrl} = useBaseUrlUtils(); const sources = { @@ -21,6 +23,19 @@ function LogoImage({logo}) { /> ); } + +LogoImage.propTypes = { + logo: PropTypes.shape({ + src: PropTypes.string.isRequired, + srcDark: PropTypes.string, + className: PropTypes.string, + alt: PropTypes.string.isRequired, + width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + style: PropTypes.object, + }).isRequired, +}; + export default function FooterLogo({logo}) { return logo.href ? ( ); } + +FooterLogo.propTypes = { + logo: PropTypes.shape({ + src: PropTypes.string.isRequired, + srcDark: PropTypes.string, + className: PropTypes.string, + alt: PropTypes.string.isRequired, + width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + style: PropTypes.object, + href: PropTypes.string, + target: PropTypes.string, + }).isRequired, +}; diff --git a/website/src/theme/Footer/index.js b/website/src/theme/Footer/index.js index 7b25ad3e4a5..6a969fc294a 100644 --- a/website/src/theme/Footer/index.js +++ b/website/src/theme/Footer/index.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import {useThemeConfig} from '@docusaurus/theme-common'; import FooterLayout from './Layout'; import FooterLinks from './Links'; @@ -7,7 +8,6 @@ import FooterCopyright from './Copyright'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useIsBrowser from '@docusaurus/useIsBrowser'; import MailchimpSubscribe from 'react-mailchimp-subscribe'; -import styles from './styles.module.css'; import clsx from 'clsx'; function Footer() { @@ -16,7 +16,7 @@ function Footer() { return null; } const {copyright, links, logo, style} = footer; - const page = useIsBrowser() ? window.location : 'not_reachable'; + const page = useIsBrowser() ? globalThis.location : 'not_reachable'; const {siteConfig} = useDocusaurusContext(); const url = `${siteConfig.customFields.mailchimpURL};SIGNUP=${page}`; return ( @@ -34,7 +34,7 @@ const CustomForm = ({status, message, onValidated}) => { let email; const submit = () => email && - email.value.indexOf('@') > -1 && + email.value.includes('@') && onValidated({ EMAIL: email.value, }); @@ -73,6 +73,12 @@ const CustomForm = ({status, message, onValidated}) => { ); }; +CustomForm.propTypes = { + status: PropTypes.string, + message: PropTypes.string, + onValidated: PropTypes.func.isRequired, +}; + const NewsletterForm = ({url}) => (
@@ -102,4 +108,8 @@ const NewsletterForm = ({url}) => (
); +NewsletterForm.propTypes = { + url: PropTypes.string.isRequired, +}; + export default React.memo(Footer);