From bdeea90256a33325efe911bb6553e3784a1070b8 Mon Sep 17 00:00:00 2001 From: Matthew Keeler Date: Wed, 20 Mar 2024 16:12:59 -0400 Subject: [PATCH] chore: Add hook support to contract tests --- contract-tests/client_entity.rb | 7 ++++ contract-tests/hook.rb | 68 +++++++++++++++++++++++++++++++++ contract-tests/service.rb | 1 + lib/ldclient-rb/interfaces.rb | 8 ++-- lib/ldclient-rb/ldclient.rb | 9 +++-- 5 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 contract-tests/hook.rb diff --git a/contract-tests/client_entity.rb b/contract-tests/client_entity.rb index 871c6b2b..dc932691 100644 --- a/contract-tests/client_entity.rb +++ b/contract-tests/client_entity.rb @@ -3,6 +3,7 @@ require 'net/http' require 'launchdarkly-server-sdk' require './big_segment_store_fixture' +require './hook' require 'http' class ClientEntity @@ -62,6 +63,12 @@ def initialize(log, config) } end + if config[:hooks] + opts[:hooks] = config[:hooks][:hooks].map do |hook| + Hook.new(hook[:name], hook[:callbackUri], hook[:data] || {}) + end + end + startWaitTimeMs = config[:startWaitTimeMs] || 5_000 @client = LaunchDarkly::LDClient.new( diff --git a/contract-tests/hook.rb b/contract-tests/hook.rb new file mode 100644 index 00000000..c01463b5 --- /dev/null +++ b/contract-tests/hook.rb @@ -0,0 +1,68 @@ +require 'ldclient-rb' + +class Hook + include LaunchDarkly::Interfaces::Hooks::Hook + + # + # @param name [String] + # @param callback_uri [String] + # @param data [Hash] + # + def initialize(name, callback_uri, data) + @metadata = LaunchDarkly::Interfaces::Hooks::Metadata.new(name) + @callback_uri = callback_uri + @data = data + @context_filter = LaunchDarkly::Impl::ContextFilter.new(false, []) + end + + def metadata + @metadata + end + + # + # @param hook_context [LaunchDarkly::Interfaces::Hooks::EvaluationContext] + # @param data [Hash] + # + def before_evaluation(hook_context, data) + payload = { + evaluationHookContext: { + flagKey: hook_context.key, + context: @context_filter.filter(hook_context.context), + defaultValue: hook_context.default_value, + method: hook_context.method, + }, + evaluationHookData: data, + stage: 'beforeEvaluation', + } + result = HTTP.post(@callback_uri, json: payload) + + (data || {}).merge(@data[:beforeEvaluation] || {}) + end + + + # + # @param hook_context [LaunchDarkly::Interfaces::Hooks::EvaluationContext] + # @param data [Hash] + # @param detail [LaunchDarkly::EvaluationDetail] + # + def after_evaluation(hook_context, data, detail) + payload = { + evaluationHookContext: { + flagKey: hook_context.key, + context: @context_filter.filter(hook_context.context), + defaultValue: hook_context.default_value, + method: hook_context.method, + }, + evaluationHookData: data, + evaluationDetail: { + value: detail.value, + variationIndex: detail.variation_index, + reason: detail.reason, + }, + stage: 'afterEvaluation', + } + HTTP.post(@callback_uri, json: payload) + + (data || {}).merge(@data[:afterEvaluation] || {}) + end +end diff --git a/contract-tests/service.rb b/contract-tests/service.rb index 10d66e16..5f252970 100644 --- a/contract-tests/service.rb +++ b/contract-tests/service.rb @@ -39,6 +39,7 @@ 'polling-gzip', 'inline-context', 'anonymous-redaction', + 'evaluation-hooks', ], }.to_json end diff --git a/lib/ldclient-rb/interfaces.rb b/lib/ldclient-rb/interfaces.rb index 57853236..e18b167f 100644 --- a/lib/ldclient-rb/interfaces.rb +++ b/lib/ldclient-rb/interfaces.rb @@ -952,19 +952,19 @@ def initialize(name) class EvaluationContext attr_reader :key attr_reader :context - attr_reader :value + attr_reader :default_value attr_reader :method # # @param key [String] # @param context [LaunchDarkly::LDContext] - # @param value [any] + # @param default_value [any] # @param method [Symbol] # - def initialize(key, context, value, method) + def initialize(key, context, default_value, method) @key = key @context = context - @value = value + @default_value = default_value @method = method end end diff --git a/lib/ldclient-rb/ldclient.rb b/lib/ldclient-rb/ldclient.rb index 47199419..592f19e0 100644 --- a/lib/ldclient-rb/ldclient.rb +++ b/lib/ldclient-rb/ldclient.rb @@ -244,6 +244,7 @@ def variation(key, context, default) # @return [EvaluationDetail] an object describing the result # def variation_detail(key, context, default) + context = Impl::Context::make_context(context) detail, _, _ = evaluate_with_hooks(key, context, default, :variation_detail) do evaluate_internal(key, context, default, true) end @@ -263,8 +264,8 @@ def variation_detail(key, context, default) # ``` # # @param key [String] - # @param context [String] - # @param default [String] + # @param context [LDContext] + # @param default [any] # @param method [Symbol] # @param &block [#call] Implicit passed block # @@ -632,6 +633,7 @@ def create_default_data_source(sdk_key, config, diagnostic_accumulator) # @return [Array] # def variation_with_flag(key, context, default) + context = Impl::Context::make_context(context) evaluate_with_hooks(key, context, default, :variation_detail) do evaluate_internal(key, context, default, false) end @@ -639,7 +641,7 @@ def variation_with_flag(key, context, default) # # @param key [String] - # @param context [Hash, LDContext] + # @param context [LDContext] # @param default [Object] # @param with_reasons [Boolean] # @@ -656,7 +658,6 @@ def evaluate_internal(key, context, default, with_reasons) return detail, nil, "no context provided" end - context = Impl::Context::make_context(context) unless context.valid? @config.logger.error { "[LDClient] Context was invalid for evaluation of flag '#{key}' (#{context.error}); returning default value" } detail = Evaluator.error_result(EvaluationReason::ERROR_USER_NOT_SPECIFIED, default)