This repository has been archived by the owner on Jan 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
posthog.go
192 lines (173 loc) · 5.71 KB
/
posthog.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package posthog
import (
"context"
"fmt"
"github.com/open-feature/go-sdk/pkg/openfeature"
"github.com/posthog/posthog-go"
)
const distinctIdKey = openfeature.TargetingKey
var _ openfeature.FeatureProvider = (*Provider)(nil)
type Provider struct {
client posthog.Client
}
// New creates an OpenFeature provider backed by PostHog.
func New(client posthog.Client) *Provider {
return &Provider{
client: client,
}
}
func (p *Provider) Metadata() openfeature.Metadata {
return openfeature.Metadata{
Name: "PostHog",
}
}
// Hooks are not currently implemented so nil is returned.
func (p *Provider) Hooks() []openfeature.Hook {
return nil
}
func (p *Provider) BooleanEvaluation(
ctx context.Context,
flag string,
defaultValue bool,
evalCtx openfeature.FlattenedContext,
) openfeature.BoolResolutionDetail {
distinctID, resDetails := extractDistinctID(evalCtx)
if resDetails != nil {
return openfeature.BoolResolutionDetail{
Value: defaultValue,
ProviderResolutionDetail: *resDetails,
}
}
// Notes:
// 1. No error if api key is incorrect. It does log "posthog 2023/09/07 15:43:34 ERROR: Error calling /decide/" which is not that useful.
// 2. It would be nice if this method returned a boolean
// 3. If the flag doesn't exist we have no way of knowing this and we just get back false
// 4. Every request logs "posthog 2023/09/07 15:04:36 ERROR: Unable to fetch feature flags%!(EXTRA <nil>)"
// 5. Not exactly related here: but if I simply specify a non-empty personal api key when constructing the client, this request succeeds
resp, err := p.client.IsFeatureEnabled(posthog.FeatureFlagPayload{
Key: flag,
DistinctId: distinctID,
})
if err != nil {
return openfeature.BoolResolutionDetail{
Value: defaultValue,
ProviderResolutionDetail: fromPostHogError(err),
}
}
respBool, ok := resp.(bool)
if !ok {
return openfeature.BoolResolutionDetail{
Value: defaultValue,
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
ResolutionError: openfeature.NewTypeMismatchResolutionError(
fmt.Sprintf("unable to convert response to boolean: %v", resp),
),
Reason: openfeature.ErrorReason,
},
}
}
return openfeature.BoolResolutionDetail{
Value: respBool,
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
// Note: I would love to return openfeature.TargetingMatchReason
// here, but we don't actually know if this was a match. The flag
// may not even exist and we would still get back false. See 3.
// in the comment above.
Reason: openfeature.UnknownReason,
},
}
}
// StringEvaluation will always return the default value and PostHog does not
// have string evaluation.
func (p *Provider) StringEvaluation(
ctx context.Context,
flag string,
defaultValue string,
evalCtx openfeature.FlattenedContext,
) openfeature.StringResolutionDetail {
return openfeature.StringResolutionDetail{
Value: defaultValue,
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
Reason: openfeature.DefaultReason,
ResolutionError: openfeature.NewGeneralResolutionError("string evaluation not implemented"),
},
}
}
// FloatEvaluation will always return the default value and PostHog does not
// have float evaluation.
func (p *Provider) FloatEvaluation(
ctx context.Context,
flag string,
defaultValue float64,
evalCtx openfeature.FlattenedContext,
) openfeature.FloatResolutionDetail {
return openfeature.FloatResolutionDetail{
Value: defaultValue,
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
Reason: openfeature.DefaultReason,
ResolutionError: openfeature.NewGeneralResolutionError("float evaluation not implemented"),
},
}
}
// IntEvaluation will always return the default value and PostHog does not
// have int evaluation.
func (p *Provider) IntEvaluation(
ctx context.Context,
flag string,
defaultValue int64,
evalCtx openfeature.FlattenedContext,
) openfeature.IntResolutionDetail {
return openfeature.IntResolutionDetail{
Value: defaultValue,
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
Reason: openfeature.DefaultReason,
ResolutionError: openfeature.NewGeneralResolutionError("int evaluation not implemented"),
},
}
}
// ObjectEvaluation will always return the default value and PostHog does not
// have object evaluation.
func (p *Provider) ObjectEvaluation(
ctx context.Context,
flag string,
defaultValue any,
evalCtx openfeature.FlattenedContext,
) openfeature.InterfaceResolutionDetail {
return openfeature.InterfaceResolutionDetail{
Value: defaultValue,
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
Reason: openfeature.DefaultReason,
ResolutionError: openfeature.NewGeneralResolutionError("object evaluation not implemented"),
},
}
}
func extractDistinctID(evalCtx openfeature.FlattenedContext) (string, *openfeature.ProviderResolutionDetail) {
for key, val := range evalCtx {
if key == distinctIdKey {
v, ok := val.(string)
if !ok {
return "", &openfeature.ProviderResolutionDetail{
ResolutionError: openfeature.NewTargetingKeyMissingResolutionError(
"value of targetingKey/distinctId cannnot be converted to string",
),
Reason: openfeature.ErrorReason,
}
}
return v, nil
}
}
return "", &openfeature.ProviderResolutionDetail{
ResolutionError: openfeature.NewTargetingKeyMissingResolutionError(
"no targetingKey/distinctId",
),
Reason: openfeature.ErrorReason,
}
}
func fromPostHogError(err error) openfeature.ProviderResolutionDetail {
return openfeature.ProviderResolutionDetail{
ResolutionError: openfeature.NewGeneralResolutionError(
fmt.Sprintf("posthog client error: %s", err.Error()),
),
Reason: openfeature.ErrorReason,
}
}