1 package ldmodel
2
3 import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/launchdarkly/go-sdk-common/v3/ldattr"
8 "github.com/launchdarkly/go-sdk-common/v3/ldtime"
9 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
10 )
11
12 var flagWithAllProperties = FeatureFlag{
13 Key: "flag-key",
14 On: true,
15 Prerequisites: []Prerequisite{
16 {
17 Key: "prereq-key",
18 Variation: 1,
19 },
20 },
21 Targets: []Target{
22 {
23 Values: []string{"user-key"},
24 Variation: 2,
25 preprocessed: targetPreprocessedData{valuesMap: map[string]struct{}{"user-key": {}}},
26 },
27 },
28 Rules: []FlagRule{
29 {
30 ID: "rule-id1",
31 Clauses: []Clause{
32 {
33 Attribute: ldattr.NewLiteralRef("name"),
34 Op: OperatorIn,
35 Values: []ldvalue.Value{ldvalue.String("clause-value")},
36 Negate: true,
37 },
38 },
39 VariationOrRollout: VariationOrRollout{
40 Variation: ldvalue.NewOptionalInt(1),
41 },
42 TrackEvents: true,
43 },
44 {
45 ID: "rule-id2",
46 Clauses: []Clause{},
47 VariationOrRollout: VariationOrRollout{
48 Rollout: Rollout{
49 Kind: RolloutKindRollout,
50 Variations: []WeightedVariation{
51 {
52 Weight: 100000,
53 Variation: 3,
54 },
55 },
56 BucketBy: ldattr.NewLiteralRef("name"),
57 },
58 },
59 },
60 {
61 ID: "rule-id3",
62 Clauses: []Clause{},
63 VariationOrRollout: VariationOrRollout{
64 Rollout: Rollout{
65 Kind: RolloutKindExperiment,
66 Variations: []WeightedVariation{
67 {
68 Weight: 10000,
69 Variation: 1,
70 },
71 {
72 Weight: 10000,
73 Variation: 2,
74 },
75 {
76 Weight: 80000,
77 Variation: 3,
78 Untracked: true,
79 },
80 },
81 BucketBy: ldattr.NewLiteralRef("name"),
82 Seed: ldvalue.NewOptionalInt(42),
83 },
84 },
85 TrackEvents: true,
86 },
87 },
88 Fallthrough: VariationOrRollout{
89 Rollout: Rollout{
90 Variations: []WeightedVariation{
91 {
92 Weight: 100000,
93 Variation: 3,
94 },
95 },
96 },
97 },
98 OffVariation: ldvalue.NewOptionalInt(3),
99 Variations: []ldvalue.Value{ldvalue.Bool(false), ldvalue.Int(9), ldvalue.String("other")},
100 ClientSideAvailability: ClientSideAvailability{
101 UsingEnvironmentID: true,
102 UsingMobileKey: true,
103 Explicit: true,
104 },
105 Salt: "flag-salt",
106 TrackEvents: true,
107 TrackEventsFallthrough: true,
108 DebugEventsUntilDate: ldtime.UnixMillisecondTime(1000),
109 Version: 99,
110 Deleted: true,
111 }
112
113 var flagWithAllPropertiesJSON = map[string]interface{}{
114 "key": "flag-key",
115 "on": true,
116 "prerequisites": []interface{}{
117 map[string]interface{}{
118 "key": "prereq-key",
119 "variation": float64(1),
120 },
121 },
122 "targets": []interface{}{
123 map[string]interface{}{
124 "values": []interface{}{"user-key"},
125 "variation": float64(2),
126 },
127 },
128 "rules": []interface{}{
129 map[string]interface{}{
130 "id": "rule-id1",
131 "clauses": []interface{}{
132 map[string]interface{}{
133 "attribute": "name",
134 "op": "in",
135 "values": []interface{}{"clause-value"},
136 "negate": true,
137 },
138 },
139 "variation": float64(1),
140 "trackEvents": true,
141 },
142 map[string]interface{}{
143 "id": "rule-id2",
144 "clauses": []interface{}{},
145 "rollout": map[string]interface{}{
146 "kind": "rollout",
147 "variations": []interface{}{
148 map[string]interface{}{
149 "weight": float64(100000),
150 "variation": float64(3),
151 },
152 },
153 "bucketBy": "name",
154 },
155 "trackEvents": false,
156 },
157 map[string]interface{}{
158 "id": "rule-id3",
159 "clauses": []interface{}{},
160 "rollout": map[string]interface{}{
161 "kind": "experiment",
162 "bucketBy": "name",
163 "variations": []interface{}{
164 map[string]interface{}{
165 "weight": float64(10000),
166 "variation": float64(1),
167 },
168 map[string]interface{}{
169 "weight": float64(10000),
170 "variation": float64(2),
171 },
172 map[string]interface{}{
173 "weight": float64(80000),
174 "variation": float64(3),
175 "untracked": true,
176 },
177 },
178 "seed": float64(42),
179 },
180 "trackEvents": true,
181 },
182 },
183 "fallthrough": map[string]interface{}{
184 "rollout": map[string]interface{}{
185 "variations": []interface{}{
186 map[string]interface{}{
187 "weight": float64(100000),
188 "variation": float64(3),
189 },
190 },
191 },
192 },
193 "offVariation": float64(3),
194 "variations": []interface{}{false, float64(9), "other"},
195 "clientSideAvailability": map[string]interface{}{
196 "usingEnvironmentId": true,
197 "usingMobileKey": true,
198 },
199 "clientSide": true,
200 "salt": "flag-salt",
201 "trackEvents": true,
202 "trackEventsFallthrough": true,
203 "debugEventsUntilDate": float64(1000),
204 "version": float64(99),
205 "deleted": true,
206 }
207
208 var flagWithMinimalProperties = FeatureFlag{
209 Key: "flag-key",
210 Fallthrough: VariationOrRollout{Variation: ldvalue.NewOptionalInt(1)},
211 Variations: []ldvalue.Value{ldvalue.Bool(false), ldvalue.Int(9), ldvalue.String("other")},
212 ClientSideAvailability: ClientSideAvailability{
213 UsingMobileKey: true,
214 Explicit: false,
215 },
216 Salt: "flag-salt",
217 Version: 99,
218 }
219
220 var flagWithMinimalPropertiesJSON = map[string]interface{}{
221 "key": "flag-key",
222 "on": false,
223 "offVariation": nil,
224 "fallthrough": map[string]interface{}{
225 "variation": float64(1),
226 },
227 "variations": []interface{}{false, float64(9), "other"},
228 "targets": []interface{}{},
229 "rules": []interface{}{},
230 "prerequisites": []interface{}{},
231 "clientSide": false,
232 "salt": "flag-salt",
233 "trackEvents": false,
234 "trackEventsFallthrough": false,
235 "debugEventsUntilDate": nil,
236 "version": float64(99),
237 "deleted": false,
238 }
239
240 var segmentWithAllProperties = Segment{
241 Key: "segment-key",
242 Included: []string{"user1"},
243 Excluded: []string{"user2"},
244 preprocessed: segmentPreprocessedData{
245 includeMap: map[string]struct{}{"user1": {}},
246 excludeMap: map[string]struct{}{"user2": {}},
247 },
248 Rules: []SegmentRule{
249 {
250 ID: "rule-id",
251 Clauses: []Clause{
252 {
253 Attribute: ldattr.NewLiteralRef("name"),
254 Op: OperatorIn,
255 Values: []ldvalue.Value{ldvalue.String("clause-value")},
256 Negate: true,
257 },
258 },
259 },
260 {
261 Weight: ldvalue.NewOptionalInt(50000),
262 BucketBy: ldattr.NewLiteralRef("name"),
263 },
264 },
265 Salt: "segment-salt",
266 Unbounded: true,
267 Version: 99,
268 Generation: ldvalue.NewOptionalInt(51),
269 Deleted: true,
270 }
271
272 var segmentWithAllPropertiesJSON = map[string]interface{}{
273 "key": "segment-key",
274 "included": []interface{}{"user1"},
275 "excluded": []interface{}{"user2"},
276 "rules": []interface{}{
277 map[string]interface{}{
278 "id": "rule-id",
279 "clauses": []interface{}{
280 map[string]interface{}{
281 "attribute": "name",
282 "op": "in",
283 "values": []interface{}{"clause-value"},
284 "negate": true,
285 },
286 },
287 },
288 map[string]interface{}{
289 "id": "",
290 "clauses": []interface{}{},
291 "weight": float64(50000),
292 "bucketBy": "name",
293 },
294 },
295 "salt": "segment-salt",
296 "unbounded": true,
297 "version": float64(99),
298 "generation": float64(51),
299 "deleted": true,
300 }
301
302 var segmentWithMinimalProperties = Segment{
303 Key: "segment-key",
304 Salt: "segment-salt",
305 Version: 99,
306 }
307
308 var segmentWithMinimalPropertiesJSON = map[string]interface{}{
309 "key": "segment-key",
310 "included": []interface{}{},
311 "excluded": []interface{}{},
312 "rules": []interface{}{},
313 "salt": "segment-salt",
314 "version": float64(99),
315 "generation": nil,
316 "deleted": false,
317 }
318
319 func makeLargeFlagJSON() []byte {
320 makeManyStrings := func() []string {
321 ret := []string{}
322 for i := 0; i < 200; i++ {
323 ret = append(ret, fmt.Sprintf("string%d", i))
324 }
325 return ret
326 }
327 makeRules := func() []map[string]interface{} {
328 ret := []map[string]interface{}{}
329 for i := 0; i < 20; i++ {
330 ret = append(ret, map[string]interface{}{
331 "id": fmt.Sprintf("rule-id%d", i),
332 "clauses": []interface{}{
333 map[string]interface{}{
334 "attribute": "name",
335 "op": "in",
336 "values": []interface{}{"clause-value"},
337 "negate": true,
338 },
339 },
340 "variation": float64(1),
341 "trackEvents": true,
342 })
343 }
344 return ret
345 }
346 data := map[string]interface{}{
347 "key": "large-flag-key",
348 "on": true,
349 "prerequisites": []interface{}{
350 map[string]interface{}{
351 "key": "prereq-key",
352 "variation": float64(1),
353 },
354 },
355 "targets": []interface{}{
356 map[string]interface{}{
357 "values": makeManyStrings(),
358 "variation": float64(2),
359 },
360 },
361 "rules": makeRules(),
362 "fallthrough": map[string]interface{}{
363 "rollout": map[string]interface{}{
364 "variations": []interface{}{
365 map[string]interface{}{
366 "weight": float64(100000),
367 "variation": float64(3),
368 },
369 },
370 },
371 },
372 "offVariation": float64(3),
373 "variations": []interface{}{false, float64(9), "other"},
374 "clientSideAvailability": map[string]interface{}{
375 "usingEnvironmentId": true,
376 "usingMobileKey": true,
377 },
378 "clientSide": true,
379 "salt": "flag-salt",
380 "trackEvents": true,
381 "trackEventsFallthrough": true,
382 "debugEventsUntilDate": float64(1000),
383 "version": float64(99),
384 "deleted": true,
385 }
386 bytes, _ := json.Marshal(data)
387 return bytes
388 }
389
390 func makeLargeSegmentJSON() []byte {
391 makeManyStrings := func() []string {
392 ret := []string{}
393 for i := 0; i < 200; i++ {
394 ret = append(ret, fmt.Sprintf("string%d", i))
395 }
396 return ret
397 }
398 makeRules := func() []map[string]interface{} {
399 ret := []map[string]interface{}{}
400 for i := 0; i < 20; i++ {
401 ret = append(ret, map[string]interface{}{
402 "id": fmt.Sprintf("rule-id%d", i),
403 "clauses": []interface{}{
404 map[string]interface{}{
405 "attribute": "name",
406 "op": "in",
407 "values": []interface{}{"clause-value"},
408 "negate": true,
409 },
410 },
411 "weight": float64(50000),
412 "bucketBy": "name",
413 })
414 }
415 return ret
416 }
417 data := map[string]interface{}{
418 "key": "large-segment-key",
419 "included": makeManyStrings(),
420 "excluded": makeManyStrings(),
421 "rules": makeRules(),
422 "salt": "segment-salt",
423 "unbounded": true,
424 "version": float64(99),
425 "deleted": true,
426 }
427 bytes, _ := json.Marshal(data)
428 return bytes
429 }
430
431 type featureFlagEquivalentStruct struct {
432 Key string `json:"key"`
433 On bool `json:"on"`
434 Prerequisites []struct {
435 Key string `json:"key"`
436 Variation int `json:"variation"`
437 } `json:"prerequisites"`
438 Targets []struct {
439 Values []string `json:"values"`
440 Variation int `json:"variation"`
441 } `json:"targets"`
442 Rules []struct {
443 Variation *int `json:"variation"`
444 Rollout *struct {
445 Variations []struct {
446 Variation int `json:"variation"`
447 Weight int `json:"weight"`
448 } `json:"variations"`
449 BucketBy *string `json:"bucketBy"`
450 } `json:"rollout"`
451 ID string `json:"id"`
452 Clauses []struct {
453 Attribute string `json:"attribute"`
454 Op string `json:"op"`
455 Values []ldvalue.Value `json:"values"`
456 Negate bool `json:"negate"`
457 } `json:"clauses"`
458 TrackEvents bool `json:"trackEvents"`
459 } `json:"rules"`
460 Fallthrough struct {
461 Variation *int `json:"variation"`
462 Rollout struct {
463 Variations []struct {
464 } `json:"variations"`
465 BucketBy *string `json:"bucketBy"`
466 } `json:"rollout"`
467 } `json:"fallthrough"`
468 OffVariation *int `json:"offVariation"`
469 Variations []ldvalue.Value `json:"variations"`
470 ClientSideAvailability *struct {
471 UsingMobileKey bool `json:"usingMobileKey"`
472 UsingEnvironmentID bool `json:"usingEnvironmentId"`
473 } `json:"clientSideAvailability"`
474 Salt string `json:"salt"`
475 TrackEvents bool `json:"trackEvents"`
476 TrackEventsFallthrough bool `json:"trackEventsFallthrough"`
477 DebugEventsUntilDate *uint64 `json:"debugEventsUntilDate"`
478 Version int `json:"version"`
479 Deleted bool `json:"deleted"`
480 }
481
482 type segmentEquivalentStruct struct {
483 Key string `json:"key"`
484 Included []string `json:"included"`
485 Excluded []string `json:"excluded"`
486 Salt string `json:"salt"`
487 Rules []struct {
488 ID string `json:"id"`
489 Clauses []struct {
490 Attribute string `json:"attribute"`
491 Op string `json:"op"`
492 Values []ldvalue.Value `json:"values"`
493 Negate bool `json:"negate"`
494 } `json:"clauses"`
495 Weight *int `json:"weight"`
496 BucketBy *string `json:"bucketBy"`
497 } `json:"rules"`
498 Unbounded bool `json:"unbounded"`
499 Version int `json:"version"`
500 Deleted bool `json:"deleted"`
501 }
502
View as plain text