-
Notifications
You must be signed in to change notification settings - Fork 6
feat: get evaluation get result #88
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
base: feat/evaluation-context-mappers
Are you sure you want to change the base?
feat: get evaluation get result #88
Conversation
| 'Integer' => ->(v) { | ||
| i = v.to_i; | ||
| i.to_s == v.to_s ? i : v | ||
| }, | ||
| 'Float' => ->(v) { | ||
| f = v.to_f; | ||
| f.to_s == v.to_s ? f : v | ||
| } |
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.
This implementation is problematic and was fixed in incoming PR.
This would end up coerciting 123abc to 123abc 🔴
| # Simple JSONPath implementation - handle basic cases | ||
| path_parts = json_path.sub('$.', '').split('.') | ||
| current = context | ||
|
|
||
| path_parts.each do |part| | ||
| return nil unless current.is_a?(Hash) | ||
|
|
||
| current = current[part.to_sym] |
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.
Proper JSON path implementation done in following PR
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.
Leaving some comments as a first pass.
I'd really like to see which engine tests fail for this PR — if it's something to better pursue locally rather than in CI, let me know, @Zaimwa9.
|
|
||
| # Returns { segments: EvaluationResultSegments; segmentOverrides: Record<string, SegmentOverride>; } | ||
| def evaluate_segments(evaluation_context) | ||
| return [], {} if evaluation_context[:identity].nil? || evaluation_context[:segments].nil? |
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.
I'm insisting that if evaluation_context[:identity].nil? part should happen in the mapper layer. We're not supposed to prevent identity-less evaluation in the engine, we're maintaining compatibility with /v1/flags for the get_environment_flags SDK interface.
| # | ||
| # @param context [Hash] Evaluation context containing identity and segment definitions | ||
| # @return [Array<Hash>] Array of segments that the identity matches | ||
| def get_identity_segments_from_context(context) |
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.
nit: For complete correctness:
| def get_identity_segments_from_context(context) | |
| def get_segments_from_context(context) |
As stated previously, we're not shutting the door for identity-less contexts.
| name: segment[:name] | ||
| } | ||
|
|
||
| result[:metadata] = segment[:metadata].dup if segment[:metadata] |
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.
Are we forced to use dup here? Does a simple result[:metadata] = segment[:metadata] imply clone?
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.
From what I’ve read, assignment does not imply a clone — it just copies the reference to the same object. Since this struct is created fresh for every request, I think we can remove the dup here.
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.
Agreed.
| evaluated = if has_override | ||
| { value: final_feature[:value], reason: nil } | ||
| else | ||
| evaluate_feature_value(final_feature, get_identity_key(evaluation_context)) | ||
| end |
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.
This probably reproduces the bug existing in the reference implementation, covered by test_multivariate__segment_override__expected_allocation.
Can we add tests to CI so we don't have to guess?
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.
That's not part of this sprint?
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.
I think you're confusing with Flagsmith/flagsmith-python-client#172 (and other similar issues in Flagsmith/flagsmith#5977)
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.
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.
Yes it is — I don't think we should ship with that bug. The test covering it is part of engine-test-data anyway.
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.
As far as I remember, I raised this during the standup to fix it in Rust, but we decided not to include it in this sprint. Is this bug present in all the SDKs?
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.
We’re not releasing without the fix, but it can be included in a separate pull request. As far as I remember, that was the decision — please correct me if I’m mistaken.
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.
As discussed offline, there's no reason not to inlclude the fix in this PR.
|
|
||
| # Set reason | ||
| flag_result[:reason] = evaluated[:reason] || | ||
| get_targeting_match_reason({ type: 'SEGMENT', override: segment_override }) |
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.
I'm not sure why we need this additional level of misdirection with match_object and get_targeting_match_reason utility, when evaluate_feature_value and get_multivariate_feature_value could've simply set the reason then and there? Am I missing something?
|
|
||
| # returns boolean | ||
| def higher_priority?(priority_a, priority_b) | ||
| (priority_a || Float::INFINITY) < (priority_b || Float::INFINITY) |
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.
nit: Maybe have a WEAKEST_PRIORITY constant set to Float::INFINITY.
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.
I can see it addressed in #89; feel free to resolve or integrate the changes here.
| end | ||
|
|
||
| # returns boolean | ||
| def higher_priority?(priority_a, priority_b) |
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.
| def higher_priority?(priority_a, priority_b) | |
| def stronger_priority?(priority_a, priority_b) |
Or maybe even
| def higher_priority?(priority_a, priority_b) | |
| def is_stronger_priority?(priority_a, priority_b) |
…h/flagsmith-ruby-client into feat/evaluation-get-result
| def non_primitive?(value) | ||
| return false if value.nil? | ||
|
|
||
| value.is_a?(Hash) || value.is_a?(Array) |
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.
Seems this is used in a negative boolean expression. For readability's sake, I'd prefer to have a is_primitive? method instead.
| # @return [Boolean] True if the condition matches | ||
| def traits_match_segment_condition_from_context(condition, segment_key, context) | ||
| if condition[:operator] == PERCENTAGE_SPLIT | ||
| context_value_key = get_context_value(condition[:property], context) || get_identity_key_from_context(context) |
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.
Maybe optimise this by memorising the key in the context itself, like Python engine does.
No description provided.