Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions openfeature/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,21 @@ cc_library(
],
)

cc_library(
name = "hook_context",
srcs = ["hook_context.cpp"],
hdrs = ["hook_context.h"],
include_prefix = "openfeature",
deps = [
":evaluation_context",
":flag_metadata",
":flag_type_value",
":hook_data",
":metadata",
":value",
],
)

cc_library(
name = "hook_data",
srcs = ["hook_data.cpp"],
Expand Down
75 changes: 75 additions & 0 deletions openfeature/hook_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "openfeature/hook_context.h"

#include <memory>
#include <string>

#include "openfeature/evaluation_context.h"
#include "openfeature/flag_metadata.h"
#include "openfeature/flag_type_value.h"
#include "openfeature/hook_data.h"
#include "openfeature/metadata.h"
#include "openfeature/value.h"

namespace openfeature {

template <typename T>
HookContext<T>::HookContext(std::string flag_key, FlagValueType type,
T default_value, EvaluationContext ctx,
Metadata client_metadata,
Metadata provider_metadata,
std::shared_ptr<HookData> hook_data)
: flag_key_(std::move(flag_key)),
type_(type),
default_value_(std::move(default_value)),
ctx_(std::move(ctx)),
client_metadata_(std::move(client_metadata)),
provider_metadata_(std::move(provider_metadata)),
hook_data_(std::move(hook_data)) {}

template <typename T>
const std::string& HookContext<T>::GetFlagKey() const {
return flag_key_;
}

template <typename T>
FlagValueType HookContext<T>::GetType() const {
return type_;
}

template <typename T>
const T& HookContext<T>::GetDefaultValue() const {
return default_value_;
}

template <typename T>
const EvaluationContext& HookContext<T>::GetEvaluationContext() const {
return ctx_;
}

template <typename T>
void HookContext<T>::SetEvaluationContext(EvaluationContext ctx) {
ctx_ = std::move(ctx);
}

template <typename T>
const Metadata& HookContext<T>::GetClientMetadata() const {
return client_metadata_;
}

template <typename T>
const Metadata& HookContext<T>::GetProviderMetadata() const {
return provider_metadata_;
}

template <typename T>
std::shared_ptr<HookData> HookContext<T>::GetHookData() const {
return hook_data_;
}

template class HookContext<bool>;
template class HookContext<std::string>;
template class HookContext<int64_t>;
template class HookContext<double>;
template class HookContext<Value>;

} // namespace openfeature
50 changes: 50 additions & 0 deletions openfeature/hook_context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#ifndef CPP_SDK_INCLUDE_OPENFEATURE_HOOK_CONTEXT_H_
#define CPP_SDK_INCLUDE_OPENFEATURE_HOOK_CONTEXT_H_

#include <memory>
#include <string>

#include "openfeature/evaluation_context.h"
#include "openfeature/flag_metadata.h"
#include "openfeature/flag_type_value.h"
#include "openfeature/hook_data.h"
#include "openfeature/metadata.h"
#include "openfeature/value.h"

namespace openfeature {

template <typename T>
class HookContext {
public:
HookContext(std::string flag_key, FlagValueType type, T default_value,
EvaluationContext ctx, Metadata client_metadata,
Metadata provider_metadata, std::shared_ptr<HookData> hook_data);

const std::string& GetFlagKey() const;
FlagValueType GetType() const;
const T& GetDefaultValue() const;
const EvaluationContext& GetEvaluationContext() const;
void SetEvaluationContext(EvaluationContext ctx);
const Metadata& GetClientMetadata() const;
const Metadata& GetProviderMetadata() const;
std::shared_ptr<HookData> GetHookData() const;

private:
std::string flag_key_;
FlagValueType type_;
T default_value_;
EvaluationContext ctx_;
Metadata client_metadata_;
Metadata provider_metadata_;
std::shared_ptr<HookData> hook_data_;
};

// Type aliases for common types.
using BoolHookContext = HookContext<bool>;
using StringHookContext = HookContext<std::string>;
using IntHookContext = HookContext<int64_t>;
using DoubleHookContext = HookContext<double>;
using ObjectHookContext = HookContext<Value>;

} // namespace openfeature
#endif // CPP_SDK_INCLUDE_OPENFEATURE_HOOK_CONTEXT_H_
15 changes: 15 additions & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,19 @@ cc_test(
"//openfeature:hook_data",
"@googletest//:gtest_main",
],
)

cc_test(
name = "hook_context_test",
srcs = ["hook_context_test.cpp"],
deps = [
"//openfeature:evaluation_context",
"//openfeature:flag_metadata",
"//openfeature:flag_type_value",
"//openfeature:hook_context",
"//openfeature:hook_data",
"//openfeature:metadata",
"//openfeature:value",
"@googletest//:gtest_main",
],
)
180 changes: 180 additions & 0 deletions test/hook_context_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#include "openfeature/hook_context.h"

#include <gtest/gtest.h>

#include <memory>
#include <string>

#include "openfeature/evaluation_context.h"
#include "openfeature/flag_metadata.h"
#include "openfeature/flag_type_value.h"
#include "openfeature/hook_data.h"
#include "openfeature/metadata.h"
#include "openfeature/value.h"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

namespace openfeature {

class HookContextTest : public ::testing::Test {
protected:
HookContextTest()
: initial_ctx_(EvaluationContext::Builder()
.WithTargetingKey("initial-user")
.WithAttribute("env", std::string("test"))
.build()) {}

EvaluationContext initial_ctx_;
Metadata client_metadata_{"test-client"};
Metadata provider_metadata_{"test-provider"};
std::shared_ptr<HookData> hook_data_{std::make_shared<HookData>()};
};

TEST_F(HookContextTest, ConstructorAndAccessorsForBool) {
constexpr FlagValueType kType = FlagValueType::kBoolean;
constexpr bool kDefaultValue = true;

BoolHookContext hook_ctx("bool-flag", kType, kDefaultValue, initial_ctx_,
client_metadata_, provider_metadata_, hook_data_);

EXPECT_EQ(hook_ctx.GetFlagKey(), "bool-flag");
EXPECT_EQ(hook_ctx.GetType(), kType);
EXPECT_EQ(hook_ctx.GetDefaultValue(), kDefaultValue);

ASSERT_TRUE(hook_ctx.GetEvaluationContext().GetTargetingKey().has_value());
EXPECT_EQ(hook_ctx.GetEvaluationContext().GetTargetingKey().value(),
"initial-user");

EXPECT_EQ(hook_ctx.GetClientMetadata().name, "test-client");
EXPECT_EQ(hook_ctx.GetProviderMetadata().name, "test-provider");
EXPECT_EQ(hook_ctx.GetHookData(), hook_data_);
}

TEST_F(HookContextTest, ConstructorAndAccessorsForString) {
constexpr FlagValueType kType = FlagValueType::kString;
const std::string default_value = "default-string";

StringHookContext hook_ctx("string-flag", kType, default_value, initial_ctx_,
client_metadata_, provider_metadata_, hook_data_);

EXPECT_EQ(hook_ctx.GetFlagKey(), "string-flag");
EXPECT_EQ(hook_ctx.GetType(), kType);
EXPECT_EQ(hook_ctx.GetDefaultValue(), default_value);

ASSERT_TRUE(hook_ctx.GetEvaluationContext().GetTargetingKey().has_value());
EXPECT_EQ(hook_ctx.GetEvaluationContext().GetTargetingKey().value(),
"initial-user");

EXPECT_EQ(hook_ctx.GetClientMetadata().name, "test-client");
EXPECT_EQ(hook_ctx.GetProviderMetadata().name, "test-provider");
EXPECT_EQ(hook_ctx.GetHookData(), hook_data_);
}

TEST_F(HookContextTest, ConstructorAndAccessorsForInteger) {
constexpr FlagValueType kType = FlagValueType::kInteger;
constexpr int64_t kDefaultValue = 123456789LL;

IntHookContext hook_ctx("int-flag", kType, kDefaultValue, initial_ctx_,
client_metadata_, provider_metadata_, hook_data_);

EXPECT_EQ(hook_ctx.GetFlagKey(), "int-flag");
EXPECT_EQ(hook_ctx.GetType(), kType);
EXPECT_EQ(hook_ctx.GetDefaultValue(), kDefaultValue);

ASSERT_TRUE(hook_ctx.GetEvaluationContext().GetTargetingKey().has_value());
EXPECT_EQ(hook_ctx.GetEvaluationContext().GetTargetingKey().value(),
"initial-user");

EXPECT_EQ(hook_ctx.GetClientMetadata().name, "test-client");
EXPECT_EQ(hook_ctx.GetProviderMetadata().name, "test-provider");
EXPECT_EQ(hook_ctx.GetHookData(), hook_data_);
}

TEST_F(HookContextTest, ConstructorAndAccessorsForDouble) {
constexpr FlagValueType kType = FlagValueType::kDouble;
constexpr double kDefaultValue = 3.14159265359;

DoubleHookContext hook_ctx("double-flag", kType, kDefaultValue, initial_ctx_,
client_metadata_, provider_metadata_, hook_data_);

EXPECT_EQ(hook_ctx.GetFlagKey(), "double-flag");
EXPECT_EQ(hook_ctx.GetType(), kType);
EXPECT_DOUBLE_EQ(hook_ctx.GetDefaultValue(), kDefaultValue);

ASSERT_TRUE(hook_ctx.GetEvaluationContext().GetTargetingKey().has_value());
EXPECT_EQ(hook_ctx.GetEvaluationContext().GetTargetingKey().value(),
"initial-user");

EXPECT_EQ(hook_ctx.GetClientMetadata().name, "test-client");
EXPECT_EQ(hook_ctx.GetProviderMetadata().name, "test-provider");
EXPECT_EQ(hook_ctx.GetHookData(), hook_data_);
}

TEST_F(HookContextTest, ConstructorAndAccessorsForObject) {
constexpr FlagValueType kType = FlagValueType::kObject;
Value default_value(std::string("json-or-structure"));

ObjectHookContext hook_ctx("object-flag", kType, default_value, initial_ctx_,
client_metadata_, provider_metadata_, hook_data_);

EXPECT_EQ(hook_ctx.GetFlagKey(), "object-flag");
EXPECT_EQ(hook_ctx.GetType(), kType);
ASSERT_TRUE(hook_ctx.GetDefaultValue().IsString());
EXPECT_EQ(hook_ctx.GetDefaultValue().AsString().value(), "json-or-structure");

ASSERT_TRUE(hook_ctx.GetEvaluationContext().GetTargetingKey().has_value());
EXPECT_EQ(hook_ctx.GetEvaluationContext().GetTargetingKey().value(),
"initial-user");

EXPECT_EQ(hook_ctx.GetClientMetadata().name, "test-client");
EXPECT_EQ(hook_ctx.GetProviderMetadata().name, "test-provider");
EXPECT_EQ(hook_ctx.GetHookData(), hook_data_);
}

TEST_F(HookContextTest, SetEvaluationContextUpdatesContext) {
BoolHookContext hook_ctx("flag-key", FlagValueType::kBoolean, false,
initial_ctx_, client_metadata_, provider_metadata_,
hook_data_);

ASSERT_TRUE(hook_ctx.GetEvaluationContext().GetTargetingKey().has_value());
EXPECT_EQ(hook_ctx.GetEvaluationContext().GetTargetingKey().value(),
"initial-user");

EvaluationContext updated_ctx = EvaluationContext::Builder()
.WithTargetingKey("updated-user")
.WithAttribute("env", std::string("prod"))
.build();

hook_ctx.SetEvaluationContext(std::move(updated_ctx));

ASSERT_TRUE(hook_ctx.GetEvaluationContext().GetTargetingKey().has_value());
EXPECT_EQ(hook_ctx.GetEvaluationContext().GetTargetingKey().value(),
"updated-user");

const std::any* env_val = hook_ctx.GetEvaluationContext().GetValue("env");
ASSERT_NE(env_val, nullptr);
EXPECT_EQ(std::any_cast<std::string>(*env_val), "prod");
}

TEST_F(HookContextTest, HookDataSharesStateAndAllowsMutations) {
BoolHookContext hook_ctx("flag-key", FlagValueType::kBoolean, true,
initial_ctx_, client_metadata_, provider_metadata_,
hook_data_);

ASSERT_NE(hook_ctx.GetHookData(), nullptr);
EXPECT_EQ(hook_ctx.GetHookData()->Get("custom_key"), nullptr);

hook_ctx.GetHookData()->Set("custom_key", std::string("stage_before"));

auto* retrieved = hook_ctx.GetHookData()->GetAs<std::string>("custom_key");
ASSERT_NE(retrieved, nullptr);
EXPECT_EQ(*retrieved, "stage_before");
}

TEST_F(HookContextTest, HandlesNullptrHookData) {
BoolHookContext hook_ctx("flag-key", FlagValueType::kBoolean, false,
initial_ctx_, client_metadata_, provider_metadata_,
nullptr);

EXPECT_EQ(hook_ctx.GetHookData(), nullptr);
}

} // namespace openfeature