Skip to content

Commit

Permalink
feat(configcat-provider): Add ConfigCat provider (#242)
Browse files Browse the repository at this point in the history
Signed-off-by: Rob Crowe <nobby.crowe@gmail.com>
  • Loading branch information
rcrowe authored Jun 19, 2023
1 parent 6621eed commit 1c9cdc6
Show file tree
Hide file tree
Showing 8 changed files with 3,286 additions and 123 deletions.
1 change: 1 addition & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"hooks/open-telemetry": "0.2.5",
"hooks/validator": "0.1.3",
"providers/configcat": "0.1.0",
"providers/flagd": "0.1.13",
"providers/from-env": "0.1.2",
"providers/go-feature-flag": "0.1.20",
Expand Down
41 changes: 41 additions & 0 deletions providers/configcat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ConfigCat OpenFeature provider for Go

OpenFeature Go provider implementation for [ConfigCat](https://configcat.com) that uses the official [ConfigCat Go SDK](https://github.com/configcat/go-sdk).

## Installation

```shell
# ConfigCat SDK
go get github.com/configcat/go-sdk/v7

# OpenFeature SDK
go get github.com/open-feature/go-sdk/pkg/openfeature
go get github.com/open-feature/go-sdk-contrib/providers/configcat
```

## Usage

Here's a basic example:

```go
import (
"context"
"fmt"

sdk "github.com/configcat/go-sdk/v7"
configcat "github.com/open-feature/go-sdk-contrib/providers/configcat/pkg"
"github.com/open-feature/go-sdk/pkg/openfeature"
)

func main() {
provider := configcat.NewProvider(sdk.NewClient("..."))
openfeature.SetProvider(provider)

client := openfeature.NewClient("app")

val, err := client.BooleanValue(context.Background(), "flag_name", false, openfeature.NewEvaluationContext("123", map[string]any{
configcat.EmailKey: "test@example.com",
}))
fmt.Printf("val: %+v - error: %v\n", val, err)
}
```
14 changes: 14 additions & 0 deletions providers/configcat/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/open-feature/go-sdk-contrib/providers/configcat

go 1.13

require (
github.com/configcat/go-sdk/v7 v7.10.1
github.com/open-feature/go-sdk v1.4.0
github.com/stretchr/testify v1.8.4
)

require (
github.com/sirupsen/logrus v1.9.3 // indirect
golang.org/x/sys v0.9.0 // indirect
)
1,984 changes: 1,984 additions & 0 deletions providers/configcat/go.sum

Large diffs are not rendered by default.

171 changes: 171 additions & 0 deletions providers/configcat/internal/clienttest/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package clienttest

import (
"sync"

sdk "github.com/configcat/go-sdk/v7"
)

// NewClient creates enough of the ConfigCat client to record flag interactions.
func NewClient() *Client {
return &Client{}
}

type Client struct {
mu sync.Mutex
requests []Request

boolEvaluation func(req Request) sdk.BoolEvaluationDetails
stringEvaluation func(req Request) sdk.StringEvaluationDetails
floatEvaluation func(req Request) sdk.FloatEvaluationDetails
intEvaluation func(req Request) sdk.IntEvaluationDetails
}

type Request struct {
Key string
DefaultValue interface{}
User sdk.User
}

func (r *Request) UserData() sdk.UserData {
userData, ok := r.User.(*sdk.UserData)
if !ok {
panic("user is not of type sdk.UserData")
}
return *userData
}

func (c *Client) Reset() {
c.mu.Lock()
defer c.mu.Unlock()
c.requests = nil
c.boolEvaluation = nil
c.stringEvaluation = nil
c.floatEvaluation = nil
c.intEvaluation = nil
}

func (c *Client) GetRequests() []Request {
c.mu.Lock()
defer c.mu.Unlock()
requests := make([]Request, len(c.requests))
copy(requests, c.requests)
return requests
}

func (c *Client) WithBoolEvaluation(eval func(req Request) sdk.BoolEvaluationDetails) {
c.mu.Lock()
defer c.mu.Unlock()
c.boolEvaluation = eval
}

func (c *Client) WithStringEvaluation(eval func(req Request) sdk.StringEvaluationDetails) {
c.mu.Lock()
defer c.mu.Unlock()
c.stringEvaluation = eval
}

func (c *Client) WithFloatEvaluation(eval func(req Request) sdk.FloatEvaluationDetails) {
c.mu.Lock()
defer c.mu.Unlock()
c.floatEvaluation = eval
}

func (c *Client) WithIntEvaluation(eval func(req Request) sdk.IntEvaluationDetails) {
c.mu.Lock()
defer c.mu.Unlock()
c.intEvaluation = eval
}

func (c *Client) GetBoolValueDetails(key string, defaultValue bool, user sdk.User) sdk.BoolEvaluationDetails {
c.mu.Lock()
defer c.mu.Unlock()

req := Request{
Key: key,
DefaultValue: defaultValue,
User: user,
}
c.requests = append(c.requests, req)

if c.boolEvaluation == nil {
return sdk.BoolEvaluationDetails{
Data: evalNotFound(key, user),
Value: defaultValue,
}
}

return c.boolEvaluation(req)
}

func (c *Client) GetStringValueDetails(key string, defaultValue string, user sdk.User) sdk.StringEvaluationDetails {
c.mu.Lock()
defer c.mu.Unlock()

req := Request{
Key: key,
DefaultValue: defaultValue,
User: user,
}
c.requests = append(c.requests, req)

if c.stringEvaluation == nil {
return sdk.StringEvaluationDetails{
Data: evalNotFound(key, user),
Value: defaultValue,
}
}

return c.stringEvaluation(req)
}

func (c *Client) GetFloatValueDetails(key string, defaultValue float64, user sdk.User) sdk.FloatEvaluationDetails {
c.mu.Lock()
defer c.mu.Unlock()

req := Request{
Key: key,
DefaultValue: defaultValue,
User: user,
}
c.requests = append(c.requests, req)

if c.floatEvaluation == nil {
return sdk.FloatEvaluationDetails{
Data: evalNotFound(key, user),
Value: defaultValue,
}
}

return c.floatEvaluation(req)
}

func (c *Client) GetIntValueDetails(key string, defaultValue int, user sdk.User) sdk.IntEvaluationDetails {
c.mu.Lock()
defer c.mu.Unlock()

req := Request{
Key: key,
DefaultValue: defaultValue,
User: user,
}
c.requests = append(c.requests, req)

if c.intEvaluation == nil {
return sdk.IntEvaluationDetails{
Data: evalNotFound(key, user),
Value: defaultValue,
}
}

return c.intEvaluation(req)
}

func evalNotFound(key string, user sdk.User) sdk.EvaluationDetailsData {
return sdk.EvaluationDetailsData{
Key: key,
User: user,
IsDefaultValue: true,
Error: sdk.ErrKeyNotFound{Key: key},
}
}
Loading

0 comments on commit 1c9cdc6

Please sign in to comment.