Skip to content

Commit

Permalink
prepare 1.1.3 release (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
LaunchDarklyReleaseBot committed Sep 27, 2021
1 parent bd553e4 commit fee67a3
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 48 deletions.
40 changes: 27 additions & 13 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
version: 2
version: 2.1

workflows:
version: 2
version: 2.1
test:
jobs:
# elixir 8 is based on otp 21
# elixir 12 is based on otp 24
# test the full range
- test-elixir-8
- test-elixir-9
- test-elixir-10
- test-elixir-11
- test-elixir-12
- test-elixir-8:
install-rebar3: true
- test-elixir-9:
install-rebar3: true
- test-elixir-10:
install-rebar3: true
- test-elixir-11:
install-rebar3: true
- test-elixir-12:
install-rebar3: true
- test-21
- test-22
- test-23
- test-24

erlang-docker-template: &erlang-docker-template
parameters:
install-rebar3:
type: boolean
default: false
steps:
- checkout:
path: ~/ldclient
- when:
condition: << parameters.install-rebar3 >>
steps:
- run: sudo curl https://s3.amazonaws.com/rebar3/rebar3 --output /usr/local/bin/rebar3
- run: sudo chmod 777 /usr/local/bin/rebar3
- run:
name: install dependencies and compile
command: make compile
Expand Down Expand Up @@ -48,27 +62,27 @@ jobs:
test-elixir-8:
<<: *erlang-docker-template
docker:
- image: circleci/elixir:1.8
- image: cimg/elixir:1.8
- image: redis
test-elixir-9:
<<: *erlang-docker-template
docker:
- image: circleci/elixir:1.9
- image: cimg/elixir:1.9
- image: redis
test-elixir-10:
<<: *erlang-docker-template
docker:
- image: circleci/elixir:1.10
- image: cimg/elixir:1.10
- image: redis
test-elixir-11:
<<: *erlang-docker-template
docker:
- image: circleci/elixir:1.11
- image: cimg/elixir:1.11
- image: redis
test-elixir-12:
<<: *erlang-docker-template
docker:
- image: circleci/elixir:1.12
- image: cimg/elixir:1.12
- image: redis

test-21:
Expand All @@ -90,4 +104,4 @@ jobs:
<<: *erlang-docker-template
docker:
- image: circleci/erlang:24.0
- image: redis
- image: redis
69 changes: 68 additions & 1 deletion priv/flags-segments-put-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,74 @@
},
"missing-all-fields": {
"key": "missing-all-fields"
}
},
"missing-rollout-for-rule": {
"clientSide": true,
"clientSideAvailability": {
"usingEnvironmentId": true,
"usingMobileKey": true
},
"debugEventsUntilDate": null,
"deleted": false,
"fallthrough": {
"variation": 2
},
"key": "missing-rollout-for-rule",
"offVariation": null,
"on": true,
"prerequisites": [],
"rules": [
{
"clauses": [
{
"attribute": "key",
"negate": false,
"op": "contains",
"values": [
"Maybe"
]
}
],
"id": "e924d1cc-6719-44ea-b486-51d0e538c544",
"trackEvents": false
}
],
"salt": "26d913f26e4b4a199abae59995995026",
"targets": [],
"trackEvents": true,
"trackEventsFallthrough": false,
"variations": [
"RuleA",
"RuleB",
"FallthroughValue"
],
"version": 4243
},
"fallthrough-no-rollout-or-variation": {
"clientSide": true,
"clientSideAvailability": {
"usingEnvironmentId": true,
"usingMobileKey": true
},
"debugEventsUntilDate": null,
"deleted": false,
"fallthrough": {},
"key": "fallthrough-no-rollout-or-variation",
"offVariation": null,
"on": true,
"prerequisites": [],
"rules": [],
"salt": "41e52f2dfc644478a3b0a51f71308e76",
"targets": [],
"trackEvents": true,
"trackEventsFallthrough": false,
"variations": [
"ExpectedPrefix_A",
"ExpectedPrefix_B",
"ExpectedPrefix_C"
],
"version": 7243
}
},
"segments": {
"test-included": {
Expand Down
67 changes: 36 additions & 31 deletions src/ldclient_eval.erl
Original file line number Diff line number Diff line change
Expand Up @@ -231,56 +231,61 @@ flag_for_user_prerequisites({fail, Reason}, #{offVariation := OffVariation} = Fl
flag_for_user_prerequisites({fail, Reason}, _Flag, _User, _FeatureStore, _Tag, DefaultValue, Events) ->
% prerequisite failed, but offVariation is null or not set
{{null, DefaultValue, Reason}, Events};
flag_for_user_prerequisites(success, #{targets := Targets} = Flag, User, FeatureStore, Tag, _DefaultValue, Events) ->
check_targets(Targets, Flag, User, FeatureStore, Tag, Events).
flag_for_user_prerequisites(success, #{targets := Targets} = Flag, User, FeatureStore, Tag, DefaultValue, Events) ->
check_targets(Targets, Flag, User, FeatureStore, Tag, DefaultValue, Events).

check_targets([], Flag, User, FeatureStore, Tag, Events) ->
flag_for_user_targets(no_match, Flag, User, FeatureStore, Tag, Events);
check_targets([#{values := Values, variation := Variation}|Rest], Flag, #{key := UserKey} = User, FeatureStore, Tag, Events) ->
check_targets([], Flag, User, FeatureStore, Tag, DefaultValue, Events) ->
flag_for_user_targets(no_match, Flag, User, FeatureStore, Tag, DefaultValue, Events);
check_targets([#{values := Values, variation := Variation}|Rest], Flag, #{key := UserKey} = User, FeatureStore, Tag, DefaultValue, Events) ->
Result = {lists:member(UserKey, Values), Variation},
check_target_result(Result, Rest, Flag, User, FeatureStore, Tag, Events).
check_target_result(Result, Rest, Flag, User, FeatureStore, Tag, DefaultValue, Events).

check_target_result({false, _}, Rest, Flag, User, FeatureStore, Tag, Events) ->
check_targets(Rest, Flag, User, FeatureStore, Tag, Events);
check_target_result({true, Variation}, _Rest, Flag, User, FeatureStore, Tag, Events) ->
check_target_result({false, _}, Rest, Flag, User, FeatureStore, Tag, DefaultValue, Events) ->
check_targets(Rest, Flag, User, FeatureStore, Tag, DefaultValue, Events);
check_target_result({true, Variation}, _Rest, Flag, User, FeatureStore, Tag, DefaultValue, Events) ->
% Target matched: short-circuit
flag_for_user_targets({match, Variation}, Flag, User, FeatureStore, Tag, Events).
flag_for_user_targets({match, Variation}, Flag, User, FeatureStore, Tag, DefaultValue, Events).

flag_for_user_targets({match, Variation}, Flag, _User, _FeatureStore, _Tag, Events) ->
flag_for_user_targets({match, Variation}, Flag, _User, _FeatureStore, _Tag, _DefaultValue, Events) ->
Reason = target_match,
result_for_variation_index(Variation, Reason, Flag, Events);
flag_for_user_targets(no_match, #{rules := Rules} = Flag, User, FeatureStore, Tag, Events) ->
check_rules(Rules, Flag, User, FeatureStore, Tag, Events, 0).
flag_for_user_targets(no_match, #{rules := Rules} = Flag, User, FeatureStore, Tag, DefaultValue, Events) ->
check_rules(Rules, Flag, User, FeatureStore, Tag, DefaultValue, Events, 0).

check_rules([], Flag, User, _FeatureStore, _Tag, Events, _) ->
flag_for_user_rules(no_match, Flag, User, Events);
check_rules([Rule|Rest], Flag, User, FeatureStore, Tag, Events, Index) ->
check_rules([], Flag, User, _FeatureStore, _Tag, DefaultValue, Events, _) ->
flag_for_user_rules(no_match, Flag, User, DefaultValue, Events);
check_rules([Rule|Rest], Flag, User, FeatureStore, Tag, DefaultValue, Events, Index) ->
Result = ldclient_rule:match_user(Rule, User, FeatureStore, Tag),
check_rule_result({Result, Rule, Index}, Rest, Flag, User, FeatureStore, Tag, Events).
check_rule_result({Result, Rule, Index}, Rest, Flag, User, FeatureStore, Tag, DefaultValue, Events).

check_rule_result({no_match, _Rule, Index}, Rest, Flag, User, FeatureStore, Tag, Events) ->
check_rules(Rest, Flag, User, FeatureStore, Tag, Events, Index + 1);
check_rule_result({match, Rule, Index}, _Rest, Flag, User, _FeatureStore, _Tag, Events) ->
check_rule_result({no_match, _Rule, Index}, Rest, Flag, User, FeatureStore, Tag, DefaultValue, Events) ->
check_rules(Rest, Flag, User, FeatureStore, Tag, DefaultValue, Events, Index + 1);
check_rule_result({match, Rule, Index}, _Rest, Flag, User, _FeatureStore, _Tag, DefaultValue, Events) ->
% Rule matched: short-circuit
flag_for_user_rules({match, Rule, Index}, Flag, User, Events).
flag_for_user_rules({match, Rule, Index}, Flag, User, DefaultValue, Events).

flag_for_user_rules({match, #{id := Id, variationOrRollout := VorR}, Index}, Flag, User, Events) ->
flag_for_user_rules({match, #{id := Id, variationOrRollout := VorR}, Index}, Flag, User, DefaultValue, Events) ->
Reason = {rule_match, Index, Id},
flag_for_user_variation_or_rollout(VorR, Reason, Flag, User, Events);
flag_for_user_rules(no_match, #{fallthrough := Fallthrough} = Flag, User, Events) ->
flag_for_user_variation_or_rollout(Fallthrough, fallthrough, Flag, User, Events).
flag_for_user_variation_or_rollout(VorR, Reason, Flag, User, DefaultValue, Events);
flag_for_user_rules(no_match, #{fallthrough := Fallthrough} = Flag, User, DefaultValue, Events) ->
flag_for_user_variation_or_rollout(Fallthrough, fallthrough, Flag, User, DefaultValue, Events).

flag_for_user_variation_or_rollout(Variation, Reason, Flag, _User, Events) when is_integer(Variation) ->
flag_for_user_variation_or_rollout(Variation, Reason, Flag, _User, _DefaultValue, Events) when is_integer(Variation) ->
result_for_variation_index(Variation, Reason, Flag, Events);
flag_for_user_variation_or_rollout(Rollout, Reason, Flag, User, Events) when is_map(Rollout) ->
flag_for_user_variation_or_rollout(Rollout, Reason, Flag, User, DefaultValue, Events) when is_map(Rollout) ->
Result = ldclient_rollout:rollout_user(Rollout, Flag, User),
flag_for_user_rollout_result(Result, Reason, Flag, Events).
flag_for_user_rollout_result(Result, Reason, Flag, DefaultValue, Events);
flag_for_user_variation_or_rollout(null, _Reason, #{key := FlagKey}, _User, DefaultValue, Events) ->
error_logger:warning_msg("Data inconsistency in feature flag ~p: rule object with no variation or rollout", [FlagKey]),
Reason = {error, malformed_flag},
{{null, DefaultValue, Reason}, Events}.

flag_for_user_rollout_result(null, _Reason, #{key := FlagKey}, Events) ->
flag_for_user_rollout_result(null, _Reason, #{key := FlagKey}, DefaultValue, Events) ->
error_logger:warning_msg("Data inconsistency in feature flag ~p: variation/rollout object with no variation or rollout", [FlagKey]),
Reason = {error, malformed_flag},
{{null, null, Reason}, Events};
flag_for_user_rollout_result(Variation, Reason, Flag, Events) ->
{{null, DefaultValue, Reason}, Events};

flag_for_user_rollout_result(Variation, Reason, Flag, _DefaultValue, Events) ->
result_for_variation_index(Variation, Reason, Flag, Events).

result_for_variation_index(Variation, Reason, Flag, Events) ->
Expand Down
2 changes: 1 addition & 1 deletion src/ldclient_rule.erl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ new_from_template(#{<<"id">> := Id, <<"clauses">> := Clauses, <<"trackEvents">>
variationOrRollout => ldclient_rollout:new(Rollout)
};
new_from_template(#{<<"id">> := Id, <<"clauses">> := Clauses, <<"trackEvents">> := TrackEvents}) ->
#{id => Id, clauses => parse_clauses(Clauses), trackEvents => TrackEvents, variationOrRollout => 0}.
#{id => Id, clauses => parse_clauses(Clauses), trackEvents => TrackEvents, variationOrRollout => null}.

-spec parse_clauses([map()]) -> [ldclient_clause:clause()].
parse_clauses(Clauses) ->
Expand Down
29 changes: 27 additions & 2 deletions test/ldclient_eval_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@
variation_out_of_range/1,
extra_fields/1,
missing_some_fields/1,
missing_all_fields/1
missing_all_fields/1,
missing_rollout_for_rule/1,
missing_rollout_for_rule_match_rule/1,
fallthrough_no_rollout_or_variation/1
]).

%%====================================================================
Expand Down Expand Up @@ -112,7 +115,10 @@ all() ->
variation_out_of_range,
extra_fields,
missing_some_fields,
missing_all_fields
missing_all_fields,
missing_rollout_for_rule,
missing_rollout_for_rule_match_rule,
fallthrough_no_rollout_or_variation
].

init_per_suite(Config) ->
Expand Down Expand Up @@ -707,3 +713,22 @@ missing_all_fields(_) ->
]),
ActualEvents = lists:sort(extract_events(Events)),
ExpectedEvents = ActualEvents.

missing_rollout_for_rule(_) ->
{{2,<<"FallthroughValue">>,fallthrough}, Events} = ldclient_eval:flag_key_for_user(default, <<"missing-rollout-for-rule">>, #{key => <<"user123">>}, "DefaultValue"),
ExpectedEvents = lists:sort([{<<"missing-rollout-for-rule">>, feature_request, 2, <<"FallthroughValue">>, "DefaultValue", fallthrough, null}]),
ActualEvents = lists:sort(extract_events(Events)),
ExpectedEvents = ActualEvents.

missing_rollout_for_rule_match_rule(_) ->
% For this test the user key matters because the rule matches keys containing "maybe".
{{null, "DefaultValue", {error, malformed_flag}}, Events} = ldclient_eval:flag_key_for_user(default, <<"missing-rollout-for-rule">>, #{key => <<"key1Maybe">>}, "DefaultValue"),
ExpectedEvents = lists:sort([{<<"missing-rollout-for-rule">>, feature_request, null, "DefaultValue", "DefaultValue", {error, malformed_flag}, null}]),
ActualEvents = lists:sort(extract_events(Events)),
ExpectedEvents = ActualEvents.

fallthrough_no_rollout_or_variation(_) ->
{{null, "DefaultValue", {error, malformed_flag}}, Events} = ldclient_eval:flag_key_for_user(default, <<"fallthrough-no-rollout-or-variation">>, #{key => <<"user123">>}, "DefaultValue"),
ExpectedEvents = lists:sort([{<<"fallthrough-no-rollout-or-variation">>, feature_request, null, "DefaultValue", "DefaultValue", {error, malformed_flag}, null}]),
ActualEvents = lists:sort(extract_events(Events)),
ExpectedEvents = ActualEvents.

0 comments on commit fee67a3

Please sign in to comment.