-
Notifications
You must be signed in to change notification settings - Fork 1
[FME-14339] Added new fetcher #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nmayorsplit
wants to merge
5
commits into
FME-14399
Choose a base branch
from
FME-14451
base: FME-14399
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,594
−0
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
3a86455
Added new fetcher and logic to map a RuleChangesDTO
nmayorsplit f94247e
Updated tests
nmayorsplit ca30095
Updated fetcher to use split interface
nmayorsplit 0570d3e
Updated dtos
nmayorsplit f9bc698
Updated config mapper
nmayorsplit File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| package dtos | ||
|
|
||
| import "encoding/json" | ||
|
|
||
| // ConvertConfigToSplit converts a ConfigDTO to a SplitDTO | ||
| // This mapper bridges the /configs endpoint response to the internal Split representation | ||
| // used by the evaluator. | ||
| func ConvertConfigToSplit(config ConfigDTO) SplitDTO { | ||
| // Apply defaults as per SDK spec | ||
| trafficTypeName := config.TrafficTypeName | ||
| if trafficTypeName == "" { | ||
| trafficTypeName = "user" | ||
| } | ||
|
|
||
| // Default to ACTIVE if status is missing | ||
| status := config.Status | ||
| if status == "" { | ||
| status = "ACTIVE" | ||
| } | ||
|
|
||
| // Map Targeting.Default to DefaultTreatment | ||
| defaultTreatment := config.Targeting.Default | ||
| if defaultTreatment == "" { | ||
| defaultTreatment = "control" | ||
| } | ||
|
|
||
| // Build configurations map from Variants | ||
| // Each variant's Definition (acts as directive for evaluator) is marshaled to JSON | ||
| configurations := make(map[string]string) | ||
| for _, variant := range config.Variants { | ||
| if variant.Definition != nil { | ||
| // Marshal the Definition (directive) to JSON string | ||
| definitionJSON, err := json.Marshal(variant.Definition) | ||
| if err == nil { | ||
| configurations[variant.Name] = string(definitionJSON) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Transform raw conditions from /configs format to evaluator ConditionDTO format | ||
| conditions := make([]ConditionDTO, 0, len(config.Targeting.Conditions)+1) | ||
| for _, rawCondition := range config.Targeting.Conditions { | ||
| conditions = append(conditions, convertRawCondition(rawCondition)) | ||
| } | ||
|
|
||
| // ALWAYS append default rule condition at the end | ||
| conditions = append(conditions, ConditionDTO{ | ||
| ConditionType: "ROLLOUT", | ||
| Label: "default rule", | ||
| MatcherGroup: MatcherGroupDTO{ | ||
| Combiner: "AND", | ||
| Matchers: []MatcherDTO{ | ||
| { | ||
| MatcherType: "ALL_KEYS", | ||
| Negate: false, | ||
| KeySelector: nil, | ||
| }, | ||
| }, | ||
| }, | ||
| Partitions: []PartitionDTO{ | ||
| { | ||
| Treatment: defaultTreatment, | ||
| Size: 100, | ||
| }, | ||
| }, | ||
| }) | ||
|
|
||
| // Build the SplitDTO for the evaluator | ||
| return SplitDTO{ | ||
| Name: config.Name, | ||
| Killed: config.Killed, // Defaults to false if missing in JSON | ||
| ChangeNumber: config.ChangeNumber, | ||
| Configurations: configurations, | ||
| DefaultTreatment: defaultTreatment, | ||
| TrafficTypeName: trafficTypeName, | ||
| Status: status, | ||
| Algo: 2, // Explicitly set to 2 (Murmur3) - not provided by /configs endpoint | ||
| Seed: config.Targeting.Seed, | ||
| TrafficAllocation: config.Targeting.TrafficAllocation, | ||
| TrafficAllocationSeed: config.Targeting.TrafficAllocationSeed, | ||
| Conditions: conditions, | ||
| Sets: config.Sets, // Feature flag sets | ||
| } | ||
| } | ||
|
|
||
| // convertRawCondition transforms a raw condition from /configs format to ConditionDTO | ||
| func convertRawCondition(raw RawConditionDTO) ConditionDTO { | ||
| // Determine condition type based on matchers | ||
| // If any matcher is WHITELIST type, the whole condition is WHITELIST, otherwise ROLLOUT | ||
| conditionType := "ROLLOUT" | ||
| for _, matcher := range raw.Matchers { | ||
| if matcher.Type == "WHITELIST" { | ||
| conditionType = "WHITELIST" | ||
| break | ||
| } | ||
| } | ||
|
|
||
| // Convert matchers | ||
| matchers := make([]MatcherDTO, 0, len(raw.Matchers)) | ||
| for _, rawMatcher := range raw.Matchers { | ||
| matchers = append(matchers, convertMatcher(rawMatcher)) | ||
| } | ||
|
|
||
| // Convert partitions: variant -> treatment | ||
| partitions := make([]PartitionDTO, 0, len(raw.Partitions)) | ||
| for _, rawPartition := range raw.Partitions { | ||
| partitions = append(partitions, PartitionDTO{ | ||
| Treatment: rawPartition.Variant, // Map "variant" to "treatment" | ||
| Size: rawPartition.Size, | ||
| }) | ||
| } | ||
|
|
||
| return ConditionDTO{ | ||
| ConditionType: conditionType, | ||
| Label: raw.Label, | ||
| MatcherGroup: MatcherGroupDTO{ | ||
| Combiner: "AND", // Always AND for /configs conditions | ||
| Matchers: matchers, | ||
| }, | ||
| Partitions: partitions, | ||
| } | ||
| } | ||
|
|
||
| // convertMatcher transforms a raw matcher from /configs format to MatcherDTO | ||
| func convertMatcher(raw RawMatcherDTO) MatcherDTO { | ||
| // Build keySelector if attribute is provided | ||
| var keySelector *KeySelectorDTO | ||
| if raw.Attribute != "" { | ||
| attr := raw.Attribute | ||
| keySelector = &KeySelectorDTO{ | ||
| TrafficType: "user", | ||
| Attribute: &attr, | ||
| } | ||
| } | ||
|
|
||
| // Transform based on matcher type | ||
| switch raw.Type { | ||
| case "WHITELIST": | ||
| // Extract strings from data | ||
| var whitelist []string | ||
| if stringsData, ok := raw.Data["strings"].([]interface{}); ok { | ||
| whitelist = make([]string, 0, len(stringsData)) | ||
| for _, s := range stringsData { | ||
| if str, ok := s.(string); ok { | ||
| whitelist = append(whitelist, str) | ||
| } | ||
| } | ||
| } | ||
| return MatcherDTO{ | ||
| MatcherType: "WHITELIST", | ||
| Negate: false, | ||
| KeySelector: keySelector, | ||
| Whitelist: &WhitelistMatcherDataDTO{ | ||
| Whitelist: whitelist, | ||
| }, | ||
| } | ||
|
|
||
| case "IS_EQUAL_TO": | ||
| // Map to EQUAL_TO with unary numeric data | ||
| var dataType string | ||
| var value int64 | ||
| if dt, ok := raw.Data["type"].(string); ok { | ||
| dataType = dt | ||
| } | ||
| if num, ok := raw.Data["number"].(float64); ok { | ||
| value = int64(num) | ||
| } | ||
| return MatcherDTO{ | ||
| MatcherType: "EQUAL_TO", | ||
| Negate: false, | ||
| KeySelector: keySelector, | ||
| UnaryNumeric: &UnaryNumericMatcherDataDTO{ | ||
| DataType: dataType, | ||
| Value: value, | ||
| }, | ||
| } | ||
|
|
||
| default: | ||
| // Default to ALL_KEYS for unknown types | ||
| return MatcherDTO{ | ||
| MatcherType: "ALL_KEYS", | ||
| Negate: false, | ||
| KeySelector: keySelector, | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| package dtos | ||
|
|
||
| import "encoding/json" | ||
|
|
||
| // VariantDTO represents a variant (treatment) with its definition (directive for evaluator) | ||
| type VariantDTO struct { | ||
| Name string `json:"name"` | ||
| Definition interface{} `json:"definition"` // Acts as directive for the evaluator | ||
| } | ||
|
|
||
| // RawMatcherDTO represents a matcher as returned by the /configs endpoint (needs transformation) | ||
| type RawMatcherDTO struct { | ||
| Type string `json:"type"` // e.g., "WHITELIST", "IS_EQUAL_TO", "GREATER_THAN_OR_EQUAL_TO" | ||
| Attribute string `json:"attribute"` // Attribute name (optional for ALL_KEYS) | ||
| Data map[string]interface{} `json:"data"` // Flexible data structure | ||
| } | ||
|
|
||
| // RawPartitionDTO represents a partition as returned by the /configs endpoint | ||
| type RawPartitionDTO struct { | ||
| Variant string `json:"variant"` // Uses "variant" instead of "treatment" | ||
| Size int `json:"size"` | ||
| } | ||
|
|
||
| // RawConditionDTO represents a condition as returned by the /configs endpoint (needs transformation) | ||
| type RawConditionDTO struct { | ||
| Label string `json:"label"` | ||
| Partitions []RawPartitionDTO `json:"partitions"` | ||
| Matchers []RawMatcherDTO `json:"matchers"` // Flat array, not MatcherGroup | ||
| // Note: No ConditionType field in raw conditions | ||
| } | ||
|
|
||
| // TargetingDTO represents the targeting rules for a config | ||
| type TargetingDTO struct { | ||
| Default string `json:"default"` // The default variant name | ||
| Seed int64 `json:"seed"` // Seed for hashing | ||
| TrafficAllocation int `json:"trafficAllocation"` // Percentage of traffic allocated | ||
| TrafficAllocationSeed int64 `json:"trafficAllocationSeed"` // Seed for traffic allocation | ||
| Conditions []RawConditionDTO `json:"conditions"` // Raw targeting conditions (need transformation) | ||
| } | ||
|
|
||
| // ConfigDTO represents a configuration definition fetched from the /configs endpoint | ||
| type ConfigDTO struct { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, missing variants and targeting elements
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
| Name string `json:"name"` | ||
| TrafficTypeName string `json:"trafficTypeName"` | ||
| ChangeNumber int64 `json:"changeNumber"` | ||
| Status string `json:"status"` // Defaults to "ACTIVE" if missing | ||
| Killed bool `json:"killed"` // Defaults to false if missing | ||
| Sets []string `json:"sets"` // Feature flag sets | ||
| Variants []VariantDTO `json:"variants"` | ||
| Targeting TargetingDTO `json:"targeting"` | ||
| } | ||
|
|
||
| // ConfigsResponseDTO represents the response from the /configs endpoint | ||
| type ConfigsResponseDTO struct { | ||
| Updated []ConfigDTO `json:"updated"` // List of updated configs | ||
| Since int64 `json:"since"` // Starting change number | ||
| Till int64 `json:"till"` // Ending change number | ||
| RBS []RuleBasedSegmentDTO `json:"rbs"` // Rule-based segments | ||
| } | ||
|
|
||
| // FFResponseConfigs implements FFResponse interface for configs endpoint responses | ||
| type FFResponseConfigs struct { | ||
| configsResponse ConfigsResponseDTO | ||
| } | ||
|
|
||
| // NewFFResponseConfigs creates a new FFResponseConfigs instance from JSON data | ||
| func NewFFResponseConfigs(data []byte) (FFResponse, error) { | ||
| var configsResponse ConfigsResponseDTO | ||
| err := json.Unmarshal(data, &configsResponse) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return &FFResponseConfigs{ | ||
| configsResponse: configsResponse, | ||
| }, nil | ||
| } | ||
|
|
||
| // NeedsAnotherFetch checks if another fetch is needed based on the since and till values | ||
| func (f *FFResponseConfigs) NeedsAnotherFetch() bool { | ||
| return f.configsResponse.Since == f.configsResponse.Till | ||
| } | ||
|
|
||
| // RuleBasedSegments returns the list of rule-based segments from the response | ||
| func (f *FFResponseConfigs) RuleBasedSegments() []RuleBasedSegmentDTO { | ||
| return f.configsResponse.RBS | ||
| } | ||
|
|
||
| // FeatureFlags returns the list of feature flags (splits) converted from configs | ||
| func (f *FFResponseConfigs) FeatureFlags() []SplitDTO { | ||
| splits := make([]SplitDTO, 0, len(f.configsResponse.Updated)) | ||
| for _, config := range f.configsResponse.Updated { | ||
| splits = append(splits, ConvertConfigToSplit(config)) | ||
| } | ||
| return splits | ||
| } | ||
|
|
||
| // FFTill returns the till value for feature flags | ||
| func (f *FFResponseConfigs) FFTill() int64 { | ||
| return f.configsResponse.Till | ||
| } | ||
|
|
||
| // RBTill returns the till value for rule-based segments | ||
| func (f *FFResponseConfigs) RBTill() int64 { | ||
| return f.configsResponse.Till | ||
| } | ||
|
|
||
| // FFSince returns the since value for feature flags | ||
| func (f *FFResponseConfigs) FFSince() int64 { | ||
| return f.configsResponse.Since | ||
| } | ||
|
|
||
| // RBSince returns the since value for rule-based segments | ||
| func (f *FFResponseConfigs) RBSince() int64 { | ||
| return f.configsResponse.Since | ||
| } | ||
|
|
||
| var _ FFResponse = (*FFResponseConfigs)(nil) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to extract variants element as well, see comment below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated