diff --git a/providers/go-feature-flag/pkg/provider.go b/providers/go-feature-flag/pkg/provider.go index 40460a6cf..0ef110099 100644 --- a/providers/go-feature-flag/pkg/provider.go +++ b/providers/go-feature-flag/pkg/provider.go @@ -21,6 +21,7 @@ type Provider struct { httpClient HTTPClient endpoint string goFeatureFlagInstance *client.GoFeatureFlag + apiKey string } // HTTPClient is a custom interface to be able to override it by any implementation @@ -64,6 +65,7 @@ func NewProvider(options ProviderOptions) (*Provider, error) { } return &Provider{ + apiKey: options.APIKey, endpoint: options.Endpoint, httpClient: httpClient, }, nil @@ -276,6 +278,9 @@ func evaluateWithRelayProxy[T model.JsonType](provider *Provider, ctx context.Co } } goffRequest.Header.Set("Content-Type", "application/json") + if provider.apiKey != "" { + goffRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", provider.apiKey)) + } response, err := provider.httpClient.Do(goffRequest) if err != nil { @@ -298,6 +303,27 @@ func evaluateWithRelayProxy[T model.JsonType](provider *Provider, ctx context.Co } } + if response.StatusCode == http.StatusUnauthorized { + return model.GenericResolutionDetail[T]{ + Value: defaultValue, + ProviderResolutionDetail: of.ProviderResolutionDetail{ + ResolutionError: of.NewGeneralResolutionError( + "invalid token used to contact GO Feature Flag relay proxy instance"), + Reason: of.ErrorReason, + }, + } + } + if response.StatusCode >= http.StatusBadRequest { + return model.GenericResolutionDetail[T]{ + Value: defaultValue, + ProviderResolutionDetail: of.ProviderResolutionDetail{ + ResolutionError: of.NewGeneralResolutionError( + "unexpected answer from the relay proxy"), + Reason: of.ErrorReason, + }, + } + } + var evalResponse model.EvalResponse[T] err = json.Unmarshal(responseStr, &evalResponse) if err != nil { diff --git a/providers/go-feature-flag/pkg/provider_options.go b/providers/go-feature-flag/pkg/provider_options.go index c69ece54a..32513ee04 100644 --- a/providers/go-feature-flag/pkg/provider_options.go +++ b/providers/go-feature-flag/pkg/provider_options.go @@ -16,4 +16,10 @@ type ProviderOptions struct { // GOFeatureFlagConfig is the configuration struct for the GO Feature Flag module. // If not nil we will launch the provider using the GO Feature Flag module. GOFeatureFlagConfig *ffclient.Config + + // APIKey (optional) If the relay proxy is configured to authenticate the requests, you should provide + // an API Key to the provider. Please ask the administrator of the relay proxy to provide an API Key. + // (This feature is available only if you are using GO Feature Flag relay proxy v1.7.0 or above) + // Default: null + APIKey string } diff --git a/providers/go-feature-flag/pkg/provider_test.go b/providers/go-feature-flag/pkg/provider_test.go index 5419a5f12..58d7d05f3 100644 --- a/providers/go-feature-flag/pkg/provider_test.go +++ b/providers/go-feature-flag/pkg/provider_test.go @@ -20,6 +20,14 @@ type mockClient struct{} func (m *mockClient) Do(req *http.Request) (*http.Response, error) { mockPath := "../testutils/mock_responses/%s.json" flagName := strings.Replace(strings.Replace(req.URL.Path, "/v1/feature/", "", -1), "/eval", "", -1) + + if flagName == "unauthorized" { + return &http.Response{ + StatusCode: http.StatusUnauthorized, + Body: io.NopCloser(bytes.NewReader([]byte(""))), + }, nil + } + content, err := os.ReadFile(fmt.Sprintf(mockPath, flagName)) if err != nil { content, _ = os.ReadFile(fmt.Sprintf(mockPath, "flag_not_found")) @@ -65,6 +73,27 @@ func TestProvider_BooleanEvaluation(t *testing.T) { args args want of.BooleanEvaluationDetails }{ + { + name: "unauthorized flag", + args: args{ + flag: "unauthorized", + defaultValue: false, + evalCtx: defaultEvaluationCtx(), + }, + want: of.BooleanEvaluationDetails{ + Value: false, + EvaluationDetails: of.EvaluationDetails{ + FlagKey: "unauthorized", + FlagType: of.Boolean, + ResolutionDetail: of.ResolutionDetail{ + Variant: "", + Reason: of.ErrorReason, + ErrorCode: of.GeneralCode, + ErrorMessage: "invalid token used to contact GO Feature Flag relay proxy instance", + }, + }, + }, + }, { name: "should resolve a valid boolean flag with TARGETING_MATCH reason", args: args{