diff --git a/contract-tests/client_entity.rb b/contract-tests/client_entity.rb index faab8c9d..9c840912 100644 --- a/contract-tests/client_entity.rb +++ b/contract-tests/client_entity.rb @@ -35,6 +35,7 @@ def initialize(log, config) opts[:all_attributes_private] = !!events[:allAttributesPrivate] opts[:private_attributes] = events[:globalPrivateAttributes] opts[:flush_interval] = (events[:flushIntervalMs] / 1_000) unless events[:flushIntervalMs].nil? + opts[:omit_anonymous_contexts] = !!events[:omitAnonymousContexts] else opts[:send_events] = false end diff --git a/contract-tests/service.rb b/contract-tests/service.rb index 5f252970..954715ee 100644 --- a/contract-tests/service.rb +++ b/contract-tests/service.rb @@ -40,6 +40,7 @@ 'inline-context', 'anonymous-redaction', 'evaluation-hooks', + 'omit-anonymous-contexts', ], }.to_json end diff --git a/lib/ldclient-rb/config.rb b/lib/ldclient-rb/config.rb index 09347e20..9de83df0 100644 --- a/lib/ldclient-rb/config.rb +++ b/lib/ldclient-rb/config.rb @@ -43,6 +43,7 @@ class Config # @option opts [BigSegmentsConfig] :big_segments See {#big_segments}. # @option opts [Hash] :application See {#application} # @option opts [String] :payload_filter_key See {#payload_filter_key} + # @option opts [Boolean] :omit_anonymous_contexts See {#omit_anonymous_contexts} # @option hooks [Array "summary") + ) + + summary = output.detect { |e| e[:kind] == "summary" } + expect(summary[:features][:flagkey][:contextKinds]).to contain_exactly("org") + end + end + + it "ignore single-kind anonymous context if omit_anonymous_contexts" do + with_processor_and_sender(omit_anonymous_contexts_config, starting_timestamp) do |ep, sender| + flag = { key: "flagkey", version: 11 } + ep.record_eval_event(anon_context, 'flagkey', 11, 1, 'value', nil, nil, true) + + output = flush_and_get_events(ep, sender) + expect(output).to contain_exactly( + eq(feature_event(omit_anonymous_contexts_config, flag, anon_context, 1, 'value')), + include(:kind => "summary") + ) + + summary = output.detect { |e| e[:kind] == "summary" } + expect(summary[:features][:flagkey][:contextKinds]).to contain_exactly("org") + end + end + + it "ignore anonymous contexts from multi-kind if omit_anonymous_contexts" do + with_processor_and_sender(omit_anonymous_contexts_config, starting_timestamp) do |ep, sender| + flag = { key: "flagkey", version: 11 } + multi = LDContext.create_multi([context, anon_context]) + ep.record_eval_event(multi, 'flagkey', 11, 1, 'value', nil, nil, true) + + output = flush_and_get_events(ep, sender) + expect(output).to contain_exactly( + eq(index_event(omit_anonymous_contexts_config, context)), + eq(feature_event(omit_anonymous_contexts_config, flag, multi, 1, 'value')), + include(:kind => "summary") + ) + + summary = output.detect { |e| e[:kind] == "summary" } + expect(summary[:features][:flagkey][:contextKinds]).to contain_exactly("user", "org") + end + end + + it "handles mult-kind context being completely anonymous if omit_anonymous_contexts" do + with_processor_and_sender(omit_anonymous_contexts_config, starting_timestamp) do |ep, sender| + flag = { key: "flagkey", version: 11 } + anon_user = LDContext.create({ kind: "user", key: "userkey", name: "User name", anonymous: true }) + multi = LDContext.create_multi([anon_user, anon_context]) + ep.record_eval_event(multi, 'flagkey', 11, 1, 'value', nil, nil, true) + + output = flush_and_get_events(ep, sender) + expect(output).to contain_exactly( + eq(feature_event(omit_anonymous_contexts_config, flag, multi, 1, 'value')), + include(:kind => "summary") + ) + + summary = output.detect { |e| e[:kind] == "summary" } + expect(summary[:features][:flagkey][:contextKinds]).to contain_exactly("user", "org") + end + end + + it "anonymous context does not prevent subsequent index events if omit_anonymous_contexts" do + with_processor_and_sender(omit_anonymous_contexts_config, starting_timestamp) do |ep, sender| + flag = { key: "flagkey", version: 11 } + ep.record_eval_event(anon_context, 'flagkey', 11, 1, 'value', nil, nil, false) + non_anon_context = LDContext.create({ kind: "org", key: "orgkey", name: "Organization", anonymous: false }) + ep.record_eval_event(non_anon_context, 'flagkey', 11, 1, 'value', nil, nil, false) + + output = flush_and_get_events(ep, sender) + expect(output).to contain_exactly( + eq(index_event(omit_anonymous_contexts_config, non_anon_context, starting_timestamp + 1)), + include(:kind => "summary") + ) + + summary = output.detect { |e| e[:kind] == "summary" } + expect(summary[:features][:flagkey][:contextKinds]).to contain_exactly("org") + end end end @@ -274,7 +411,7 @@ module LaunchDarkly it "treats nil value for custom the same as an empty hash" do with_processor_and_sender(default_config, starting_timestamp) do |ep, sender| - user_with_nil_custom = LDContext.create({ key: "userkey", custom: nil }) + user_with_nil_custom = LDContext.create({ key: "userkey", kind: "user", custom: nil }) ep.record_identify_event(user_with_nil_custom) output = flush_and_get_events(ep, sender) @@ -721,7 +858,7 @@ def custom_event(context, key, data, metric_value) def flush_and_get_events(ep, sender) ep.flush ep.wait_until_inactive - sender.analytics_payloads.pop + sender.analytics_payloads.pop unless sender.analytics_payloads.empty? end end end