Skip to content

Commit 0c601f9

Browse files
committed
added changes
1 parent a318fbe commit 0c601f9

File tree

7 files changed

+442
-2
lines changed

7 files changed

+442
-2
lines changed

.env.example

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
LOCAL_EVALUATION_CONFIG_DEBUG=false
2+
LOCAL_EVALUATION_CONFIG_SERVER_URL=https://api.lab.amplitude.com
3+
LOCAL_EVALUATION_CONFIG_POLL_INTERVAL=30
4+
LOCAL_EVALUATION_CONFIG_POLLER_REQUEST_TIMEOUT=10
5+
LOCAL_EVALUATION_DEPLOYMENT_KEY=

README.md

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
1-
# lambda-featureflag-go-sdk
2-
GO SDK for Feature Flags
1+
## Lambda-Featureflag-Go-Sdk
2+
3+
local evaluation sdks for golang.
4+
5+
### Remote Setup Instructions
6+
- add `github.com/LambdaTest/lambda-featureflag-go-sdk latest` in go.mod
7+
- run locally.
8+
```
9+
$ go mod vendor
10+
```
11+
- Add below config variable and dependent key through env variable(os.Setenv(key,value)).
12+
```
13+
LOCAL_EVALUATION_CONFIG_DEBUG = false (enables debug logs for amplitude).
14+
LOCAL_EVALUATION_CONFIG_SERVER_URL = "https://api.lab.amplitude.com/" (amplitude server url or evaluation proxy server url).
15+
LOCAL_EVALUATION_CONFIG_POLL_INTERVAL = 30 (poller interval for flag rules from amplitude).
16+
LOCAL_EVALUATION_CONFIG_POLLER_REQUEST_TIMEOUT = 10 (poller request timeout).
17+
LOCAL_EVALUATION_DEPLOYMENT_KEY = "" (server side deployment key).
18+
```

go.mod

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module github.com/LambdaTest/lambda-featureflag-go-sdk
2+
3+
go 1.20
4+
5+
require (
6+
github.com/amplitude/experiment-go-server v1.1.2
7+
github.com/joho/godotenv v1.5.1
8+
github.com/sirupsen/logrus v1.9.3
9+
gopkg.in/natefinch/lumberjack.v2 v2.2.1
10+
)
11+
12+
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect

go.sum

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
github.com/amplitude/experiment-go-server v1.1.2 h1:Dz/aunxMU/Dpi50KNsYv+7aFj/WQGTQrR8myfuUavKo=
2+
github.com/amplitude/experiment-go-server v1.1.2/go.mod h1:xebZ3xuN/nZvDc9cMeqOWENAJ6BqdjdrmMIq3B4Hczc=
3+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
7+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
8+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
9+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
10+
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
11+
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
12+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
13+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
14+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
15+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
16+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
17+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
18+
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
19+
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
20+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
21+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

localEvaluation/localEvaluation.go

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package localEvaluation
2+
3+
import (
4+
"fmt"
5+
"github.com/LambdaTest/lambda-featureflag-go-sdk/logger"
6+
"github.com/amplitude/experiment-go-server/pkg/experiment"
7+
"github.com/amplitude/experiment-go-server/pkg/experiment/local"
8+
"github.com/joho/godotenv"
9+
"os"
10+
"strconv"
11+
"time"
12+
)
13+
14+
var (
15+
client *local.Client
16+
Logger = logger.GetLogger()
17+
LocalEvaluationConfigDebug = false
18+
LocalEvaluationConfigServerUrl = "https://api.lab.amplitude.com/"
19+
LocalEvaluationConfigPollInterval = 30
20+
LocalEvaluationConfigPollerRequestTimeout = 10
21+
LocalEvaluationDeploymentKey = ""
22+
)
23+
24+
type variant struct {
25+
Value string `json:"value,omitempty"`
26+
Payload interface{} `json:"payload,omitempty"`
27+
}
28+
29+
type UserProperties struct {
30+
OrgId string `json:"org_id,omitempty"`
31+
OrgName string `json:"org_name,omitempty"`
32+
OrgStatus string `json:"org_status,omitempty"`
33+
Username string `json:"username,omitempty"`
34+
Email string `json:"email,omitempty"`
35+
Plan string `json:"plan,omitempty"`
36+
SubscriptionType string `json:"subscription_type,omitempty"`
37+
SubscriptionStatus string `json:"subscription_status,omitempty"`
38+
HubRegion string `json:"hub_region,omitempty"`
39+
}
40+
41+
func init() {
42+
err := godotenv.Load()
43+
if err != nil {
44+
Logger.Infof("No .env file found")
45+
} else {
46+
Logger.Infof(".env file loaded")
47+
}
48+
49+
if os.Getenv("LOCAL_EVALUATION_CONFIG_DEBUG") != "" {
50+
LocalEvaluationConfigDebug, _ = strconv.ParseBool(os.Getenv("LOCAL_EVALUATION_CONFIG_DEBUG"))
51+
}
52+
if os.Getenv("LOCAL_EVALUATION_CONFIG_SERVER_URL") != "" {
53+
LocalEvaluationConfigServerUrl = os.Getenv("LOCAL_EVALUATION_CONFIG_SERVER_URL")
54+
}
55+
if os.Getenv("LOCAL_EVALUATION_CONFIG_POLL_INTERVAL") != "" {
56+
LocalEvaluationConfigPollInterval, _ = strconv.Atoi(os.Getenv("LOCAL_EVALUATION_CONFIG_POLL_INTERVAL"))
57+
}
58+
if os.Getenv("LOCAL_EVALUATION_CONFIG_POLLER_REQUEST_TIMEOUT") != "" {
59+
LocalEvaluationConfigPollerRequestTimeout, _ = strconv.Atoi(os.Getenv("LOCAL_EVALUATION_CONFIG_POLLER_REQUEST_TIMEOUT"))
60+
}
61+
if os.Getenv("LOCAL_EVALUATION_DEPLOYMENT_KEY") != "" {
62+
LocalEvaluationDeploymentKey = os.Getenv("LOCAL_EVALUATION_DEPLOYMENT_KEY")
63+
}
64+
}
65+
66+
func Initialize() error {
67+
config := local.Config{
68+
Debug: LocalEvaluationConfigDebug,
69+
ServerUrl: LocalEvaluationConfigServerUrl,
70+
FlagConfigPollerInterval: time.Duration(LocalEvaluationConfigPollInterval) * time.Second,
71+
FlagConfigPollerRequestTimeout: time.Duration(LocalEvaluationConfigPollerRequestTimeout) * time.Second,
72+
}
73+
Logger.Debugf("config payload: %v, deployment key:%s", config, LocalEvaluationDeploymentKey)
74+
client = local.Initialize(LocalEvaluationDeploymentKey, &config)
75+
err := client.Start()
76+
if err != nil {
77+
err = fmt.Errorf("unable to create local evaluation client with given config %v with error %s", config, err.Error())
78+
return err
79+
}
80+
return nil
81+
}
82+
83+
func fetch(flagName string, user UserProperties) variant {
84+
flagKeys := []string{flagName}
85+
userProp := map[string]interface{}{
86+
"org_id": user.OrgId,
87+
"org_name": user.OrgName,
88+
"org_status": user.OrgStatus,
89+
"username": user.Username,
90+
"email": user.Email,
91+
"plan": user.Plan,
92+
"subscription_type": user.SubscriptionType,
93+
"subscription_status": user.SubscriptionStatus,
94+
"hub_region": user.HubRegion,
95+
}
96+
97+
expUser := experiment.User{
98+
UserProperties: userProp,
99+
}
100+
101+
Logger.Debugf("user payload: %v, flag: %s", expUser, flagName)
102+
103+
variants, err := client.Evaluate(&expUser, flagKeys)
104+
if err != nil {
105+
Logger.Errorf("error in evaluating flag: %s error: %s", flagName, err.Error())
106+
return variant{}
107+
}
108+
109+
return variant(variants[flagName])
110+
}
111+
112+
func GetFeatureFlagString(flagName string, user UserProperties) string {
113+
data := fetch(flagName, user)
114+
return data.Value
115+
}
116+
117+
func GetFeatureFlagBool(flagName string, user UserProperties) bool {
118+
data := fetch(flagName, user)
119+
if val, err := strconv.ParseBool(data.Value); err == nil {
120+
return val
121+
}
122+
return false
123+
}
124+
125+
func GetFeatureFlagPayload(flagName string, user UserProperties) map[string]interface{} {
126+
data := fetch(flagName, user)
127+
mapData := make(map[string]interface{})
128+
mapData["value"] = data.Value
129+
mapData["payload"] = data.Payload
130+
return mapData
131+
}

logger/contextLogging.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package logger
2+
3+
import (
4+
"github.com/sirupsen/logrus"
5+
)
6+
7+
type ContextFields logrus.Fields
8+
9+
10+
func (ctxFields ContextFields) Set(key string, data interface{}) {
11+
ctxFields[key] = data
12+
}
13+
14+
func (ctxFields ContextFields) Append(data logrus.Fields) {
15+
for k, v := range data {
16+
vm, ok := v.(map[string]interface{})
17+
if ok {
18+
ctxFields[k] = DeepCopyMap(vm)
19+
} else {
20+
ctxFields[k] = v
21+
}
22+
}
23+
}
24+
25+
//func (c ContextFields) Get(key string) interface{} {
26+
// return c[key]
27+
//}
28+
//
29+
//func (c ContextFields) Delete(key string) bool{
30+
// delete(c, key)
31+
// return true
32+
//}
33+
34+
func (ctxFields ContextFields) GetAll() logrus.Fields {
35+
logrusFields := logrus.Fields{}
36+
contextFieldsData:=DeepCopyMap(ctxFields)
37+
for k, v := range contextFieldsData {
38+
vm, ok := v.(map[string]interface{})
39+
if ok {
40+
logrusFields[k] = DeepCopyMap(vm)
41+
} else {
42+
logrusFields[k] = v
43+
}
44+
}
45+
return logrusFields
46+
47+
}
48+
49+
50+
func DeepCopyMap(m map[string]interface{}) map[string]interface{} {
51+
cp := make(map[string]interface{})
52+
for k, v := range m {
53+
vm, ok := v.(map[string]interface{})
54+
if ok {
55+
cp[k] = DeepCopyMap(vm)
56+
} else {
57+
cp[k] = v
58+
}
59+
}
60+
61+
return cp
62+
}

0 commit comments

Comments
 (0)