1 package ldmodel
2
3 import (
4 "github.com/launchdarkly/go-sdk-common/v3/ldattr"
5 "github.com/launchdarkly/go-sdk-common/v3/ldcontext"
6 "github.com/launchdarkly/go-sdk-common/v3/ldtime"
7 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
8
9 "github.com/launchdarkly/go-jsonstream/v3/jreader"
10 )
11
12 func unmarshalFeatureFlagFromBytes(data []byte) (FeatureFlag, error) {
13 r := jreader.NewReader(data)
14 parsed := unmarshalFeatureFlagFromReader(&r)
15 if err := r.Error(); err != nil {
16 return FeatureFlag{}, jreader.ToJSONError(err, &parsed)
17 }
18 return parsed, nil
19 }
20
21 func unmarshalFeatureFlagFromReader(r *jreader.Reader) FeatureFlag {
22 var parsed FeatureFlag
23 readFeatureFlag(r, &parsed)
24 if r.Error() == nil {
25 PreprocessFlag(&parsed)
26 }
27 return parsed
28 }
29
30 func unmarshalSegmentFromBytes(data []byte) (Segment, error) {
31 r := jreader.NewReader(data)
32 parsed := unmarshalSegmentFromReader(&r)
33 if err := r.Error(); err != nil {
34 return Segment{}, jreader.ToJSONError(err, &parsed)
35 }
36 return parsed, nil
37 }
38
39 func unmarshalSegmentFromReader(r *jreader.Reader) Segment {
40 var parsed Segment
41 readSegment(r, &parsed)
42 if r.Error() == nil {
43 PreprocessSegment(&parsed)
44 }
45 return parsed
46 }
47
48 func readFeatureFlag(r *jreader.Reader, flag *FeatureFlag) {
49 deprecatedClientSide := false
50
51 for obj := r.Object(); obj.Next(); {
52 name := obj.Name()
53 switch string(name) {
54 case "key":
55 flag.Key = r.String()
56 case "on":
57 flag.On = r.Bool()
58 case "prerequisites":
59 readPrerequisites(r, &flag.Prerequisites)
60 case "targets":
61 readTargets(r, &flag.Targets)
62 case "contextTargets":
63 readTargets(r, &flag.ContextTargets)
64 case "rules":
65 readFlagRules(r, &flag.Rules)
66 case "fallthrough":
67 readVariationOrRollout(r, &flag.Fallthrough)
68 case "offVariation":
69 flag.OffVariation.ReadFromJSONReader(r)
70 case "variations":
71 readValueList(r, &flag.Variations)
72 case "clientSideAvailability":
73 readClientSideAvailability(r, &flag.ClientSideAvailability)
74 case "clientSide":
75 deprecatedClientSide = r.Bool()
76 case "salt":
77 flag.Salt = r.String()
78 case "trackEvents":
79 flag.TrackEvents = r.Bool()
80 case "trackEventsFallthrough":
81 flag.TrackEventsFallthrough = r.Bool()
82 case "debugEventsUntilDate":
83 val, _ := r.Float64OrNull()
84 flag.DebugEventsUntilDate = ldtime.UnixMillisecondTime(val)
85 case "version":
86 flag.Version = r.Int()
87 case "deleted":
88 flag.Deleted = r.Bool()
89 }
90 }
91
92 if !flag.ClientSideAvailability.Explicit {
93 flag.ClientSideAvailability = ClientSideAvailability{
94 UsingMobileKey: true,
95 UsingEnvironmentID: deprecatedClientSide,
96 Explicit: false,
97 }
98 }
99 }
100
101 func readPrerequisites(r *jreader.Reader, out *[]Prerequisite) {
102 for arr := r.ArrayOrNull(); arr.Next(); {
103 var prereq Prerequisite
104 for obj := r.Object(); obj.Next(); {
105 switch string(obj.Name()) {
106 case "key":
107 prereq.Key = r.String()
108 case "variation":
109 prereq.Variation = r.Int()
110 }
111 }
112 *out = append(*out, prereq)
113 }
114 }
115
116 func readTargets(r *jreader.Reader, out *[]Target) {
117 for arr := r.ArrayOrNull(); arr.Next(); {
118 var t Target
119 for obj := r.Object(); obj.Next(); {
120 switch string(obj.Name()) {
121 case "contextKind":
122 t.ContextKind = ldcontext.Kind(r.String())
123 case "values":
124 readStringList(r, &t.Values)
125 case "variation":
126 t.Variation = r.Int()
127 }
128 }
129 *out = append(*out, t)
130 }
131 }
132
133 func readFlagRules(r *jreader.Reader, out *[]FlagRule) {
134 for arr := r.ArrayOrNull(); arr.Next(); {
135 rule := FlagRule{}
136 for obj := r.Object(); obj.Next(); {
137 switch string(obj.Name()) {
138 case "id":
139 rule.ID = r.String()
140 case "variation":
141 rule.Variation.ReadFromJSONReader(r)
142 case "rollout":
143 readRollout(r, &rule.Rollout)
144 case "clauses":
145 readClauses(r, &rule.Clauses)
146 case "trackEvents":
147 rule.TrackEvents = r.Bool()
148 }
149 }
150 *out = append(*out, rule)
151 }
152 }
153
154 func readClauses(r *jreader.Reader, out *[]Clause) {
155 for arr := r.ArrayOrNull(); arr.Next(); {
156 var clause Clause
157 var attrStr string
158 for obj := r.Object(); obj.Next(); {
159 switch string(obj.Name()) {
160 case "contextKind":
161 clause.ContextKind = ldcontext.Kind(r.String())
162 case "attribute":
163 attrStr, _ = r.StringOrNull()
164 case "op":
165 clause.Op = Operator(r.String())
166 case "values":
167 readValueList(r, &clause.Values)
168 case "negate":
169 clause.Negate = r.Bool()
170 }
171 }
172 setAttrNameOrRef(attrStr, clause.ContextKind, &clause.Attribute)
173 *out = append(*out, clause)
174 }
175 }
176
177 func readVariationOrRollout(r *jreader.Reader, out *VariationOrRollout) {
178 for obj := r.Object(); obj.Next(); {
179 switch string(obj.Name()) {
180 case "variation":
181 out.Variation.ReadFromJSONReader(r)
182 case "rollout":
183 readRollout(r, &out.Rollout)
184 }
185 }
186 }
187
188 func readRollout(r *jreader.Reader, out *Rollout) {
189 obj := r.ObjectOrNull()
190 if !obj.IsDefined() {
191 *out = Rollout{}
192 return
193 }
194 var bucketByStr string
195 for obj.Next() {
196 switch string(obj.Name()) {
197 case "kind":
198 out.Kind = RolloutKind(r.String())
199 case "contextKind":
200 out.ContextKind = ldcontext.Kind(r.String())
201 case "variations":
202 for arr := r.Array(); arr.Next(); {
203 var wv WeightedVariation
204 for wrObj := r.Object(); wrObj.Next(); {
205 switch string(wrObj.Name()) {
206 case "variation":
207 wv.Variation = r.Int()
208 case "weight":
209 wv.Weight = r.Int()
210 case "untracked":
211 wv.Untracked = r.Bool()
212 }
213 }
214 out.Variations = append(out.Variations, wv)
215 }
216 case "bucketBy":
217 bucketByStr, _ = r.StringOrNull()
218 case "seed":
219 if n, ok := r.IntOrNull(); ok {
220 out.Seed = ldvalue.NewOptionalInt(n)
221 }
222 }
223 }
224 setAttrNameOrRef(bucketByStr, out.ContextKind, &out.BucketBy)
225 }
226
227 func readClientSideAvailability(r *jreader.Reader, out *ClientSideAvailability) {
228 obj := r.ObjectOrNull()
229 out.Explicit = obj.IsDefined()
230 for obj.Next() {
231 switch string(obj.Name()) {
232 case "usingEnvironmentId":
233 out.UsingEnvironmentID = r.Bool()
234 case "usingMobileKey":
235 out.UsingMobileKey = r.Bool()
236 }
237 }
238 }
239
240 func readSegment(r *jreader.Reader, segment *Segment) {
241 for obj := r.Object(); obj.Next(); {
242 switch string(obj.Name()) {
243 case "key":
244 segment.Key = r.String()
245 case "version":
246 segment.Version = r.Int()
247 case "generation":
248 segment.Generation.ReadFromJSONReader(r)
249 case "deleted":
250 segment.Deleted = r.Bool()
251 case "included":
252 readStringList(r, &segment.Included)
253 case "excluded":
254 readStringList(r, &segment.Excluded)
255 case "includedContexts":
256 readSegmentTargets(r, &segment.IncludedContexts)
257 case "excludedContexts":
258 readSegmentTargets(r, &segment.ExcludedContexts)
259 case "rules":
260 for rulesArr := r.ArrayOrNull(); rulesArr.Next(); {
261 rule := SegmentRule{}
262 var bucketByStr string
263 for ruleObj := r.Object(); ruleObj.Next(); {
264 switch string(ruleObj.Name()) {
265 case "id":
266 rule.ID = r.String()
267 case "clauses":
268 readClauses(r, &rule.Clauses)
269 case "weight":
270 if v, ok := r.IntOrNull(); ok {
271 rule.Weight = ldvalue.NewOptionalInt(v)
272 }
273 case "bucketBy":
274 bucketByStr, _ = r.StringOrNull()
275 case "rolloutContextKind":
276 rule.RolloutContextKind = ldcontext.Kind(r.String())
277 }
278 }
279 setAttrNameOrRef(bucketByStr, rule.RolloutContextKind, &rule.BucketBy)
280 segment.Rules = append(segment.Rules, rule)
281 }
282 case "salt":
283 segment.Salt = r.String()
284 case "unbounded":
285 segment.Unbounded = r.Bool()
286 case "unboundedContextKind":
287 segment.UnboundedContextKind = ldcontext.Kind(r.String())
288 }
289 }
290 }
291
292 func readSegmentTargets(r *jreader.Reader, out *[]SegmentTarget) {
293 for arr := r.ArrayOrNull(); arr.Next(); {
294 var t SegmentTarget
295 for obj := r.Object(); obj.Next(); {
296 switch string(obj.Name()) {
297 case "contextKind":
298 t.ContextKind = ldcontext.Kind(r.String())
299 case "values":
300 readStringList(r, &t.Values)
301 }
302 }
303 *out = append(*out, t)
304 }
305 }
306
307 func readStringList(r *jreader.Reader, out *[]string) {
308 for arr := r.ArrayOrNull(); arr.Next(); {
309 *out = append(*out, r.String())
310 }
311 }
312
313 func readValueList(r *jreader.Reader, out *[]ldvalue.Value) {
314 for arr := r.ArrayOrNull(); arr.Next(); {
315 var v ldvalue.Value
316 v.ReadFromJSONReader(r)
317 *out = append(*out, v)
318 }
319 }
320
321 func setAttrNameOrRef(value string, contextKind ldcontext.Kind, out *ldattr.Ref) {
322 switch {
323 case value == "":
324 *out = ldattr.Ref{}
325
326
327
328
329 case contextKind == "":
330
331
332
333 *out = ldattr.NewLiteralRef(value)
334
335 default:
336 *out = ldattr.NewRef(value)
337 }
338 }
339
View as plain text