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
17 changes: 17 additions & 0 deletions openfeature/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,23 @@ cc_library(
include_prefix = "openfeature",
)

cc_library(
name = "openfeature_api",
srcs = ["openfeature_api.cpp"],
hdrs = ["openfeature_api.h"],
include_prefix = "openfeature",
deps = [
":client",
":client_api",
":evaluation_context",
":global_context_manager",
":metadata",
":openfeature",
":provider",
":provider_repository",
],
)

cc_library(
name = "noop_provider",
srcs = ["noop_provider.cpp"],
Expand Down
26 changes: 10 additions & 16 deletions openfeature/openfeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,15 @@ class OpenFeature {
virtual void SetProviderAndWait(
std::shared_ptr<FeatureProvider> provider) = 0;

// Sets the default provider and blocks until it initializes or a timeout
// occurs.
virtual void SetProviderAndWait(std::shared_ptr<FeatureProvider> provider,
std::chrono::milliseconds timeout) = 0;

// Sets a named provider and blocks until it successfully initializes.
virtual void SetProviderAndWait(
std::string_view domain, std::shared_ptr<FeatureProvider> provider) = 0;

// Sets a named provider and blocks until it initializes or a timeout occurs.
virtual void SetProviderAndWait(std::string_view domain,
std::shared_ptr<FeatureProvider> provider,
std::chrono::milliseconds timeout) = 0;

// If the domain is empty then GetProvider returns the default provider
// otherwise it returns the provider for the domain. If this domain has no
// provider bound, it returns the default provider.
virtual std::shared_ptr<FeatureProvider> GetProvider(
std::string_view domain = "") = 0;
std::string_view domain = "") const = 0;

virtual std::shared_ptr<Client> GetClient() = 0;

Expand All @@ -55,17 +45,21 @@ class OpenFeature {
// Sets the global evaluation context.
virtual void SetEvaluationContext(const EvaluationContext& ctx) = 0;

// Gets the global evaluation context
virtual EvaluationContext GetEvaluationContext(
std::shared_mutex& mutex, const EvaluationContext& ctx_src) = 0;
// Gets the global evaluation context.
virtual EvaluationContext GetEvaluationContext() const = 0;

// Gets the metadata for a provider bound to a specific domain.
virtual Metadata GetProviderMetadata(std::string_view domain = "") = 0;
virtual Metadata GetProviderMetadata(std::string_view domain = "") const = 0;

// Fetches the status of a provider for a domain. If the domain is not set or
// not found, it returns the default provider status.
virtual ProviderStatus GetProviderStatus(
std::string_view domain = "") const = 0;

// Shuts down all providers and resets the API to its initial state.
virtual void Shutdown() = 0;

// TODO: Add methods to add and get Hooks
// TODO: Add methods to add and get Hooks.
};

} // namespace openfeature
Expand Down
82 changes: 82 additions & 0 deletions openfeature/openfeature_api.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include "openfeature/openfeature_api.h"

#include "openfeature/client_api.h"
#include "openfeature/global_context_manager.h"

namespace openfeature {

OpenFeatureAPI::OpenFeatureAPI() {
// provider_repository_ is automatically constructed.
// It guarantees a NoopProvider is set by default.
// TODO: init hooks & events.
}

OpenFeatureAPI& OpenFeatureAPI::GetInstance() {
static OpenFeatureAPI instance;
return instance;
}

void OpenFeatureAPI::SetProvider(std::shared_ptr<FeatureProvider> provider) {
provider_repository_.SetProvider(
provider,
GlobalContextManager::GetInstance().GetGlobalEvaluationContext(), false);
}

void OpenFeatureAPI::SetProvider(std::string_view domain,
std::shared_ptr<FeatureProvider> provider) {
provider_repository_.SetProvider(
domain, provider,
GlobalContextManager::GetInstance().GetGlobalEvaluationContext(), false);
}

void OpenFeatureAPI::SetProviderAndWait(
std::shared_ptr<FeatureProvider> provider) {
provider_repository_.SetProvider(
provider,
GlobalContextManager::GetInstance().GetGlobalEvaluationContext(), true);
}

void OpenFeatureAPI::SetProviderAndWait(
std::string_view domain, std::shared_ptr<FeatureProvider> provider) {
provider_repository_.SetProvider(
domain, provider,
GlobalContextManager::GetInstance().GetGlobalEvaluationContext(), true);
}

std::shared_ptr<FeatureProvider> OpenFeatureAPI::GetProvider(
std::string_view domain) const {
return provider_repository_.GetProvider(domain);
}

std::shared_ptr<Client> OpenFeatureAPI::GetClient() { return GetClient(""); }

std::shared_ptr<Client> OpenFeatureAPI::GetClient(std::string_view domain) {
auto client = std::make_shared<ClientAPI>(provider_repository_, domain);
return client;
}

void OpenFeatureAPI::SetEvaluationContext(const EvaluationContext& ctx) {
GlobalContextManager::GetInstance().SetGlobalEvaluationContext(ctx);
}

EvaluationContext OpenFeatureAPI::GetEvaluationContext() const {
return GlobalContextManager::GetInstance().GetGlobalEvaluationContext();
}

Metadata OpenFeatureAPI::GetProviderMetadata(std::string_view domain) const {
std::shared_ptr<FeatureProvider> provider =
provider_repository_.GetProvider(domain);
if (provider) {
return provider->GetMetadata();
}
return Metadata(); // Return empty metadata if provider not found
}

ProviderStatus OpenFeatureAPI::GetProviderStatus(
std::string_view domain) const {
return provider_repository_.GetProviderStatus(domain);
}

void OpenFeatureAPI::Shutdown() { provider_repository_.Shutdown(); }

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

#include <memory>
#include <shared_mutex>
#include <string_view>

#include "openfeature/client.h"
#include "openfeature/evaluation_context.h"
#include "openfeature/global_context_manager.h"
#include "openfeature/metadata.h"
#include "openfeature/openfeature.h"
#include "openfeature/provider.h"
#include "openfeature/provider_repository.h"

namespace openfeature {

// A global singleton which holds base configuration for the OpenFeature
// library.
class OpenFeatureAPI : public OpenFeature {
public:
~OpenFeatureAPI() = default;

// Get the singleton instance of the OpenFeatureAPI.
static OpenFeatureAPI& GetInstance();

OpenFeatureAPI(const OpenFeatureAPI&) = delete;
OpenFeatureAPI& operator=(const OpenFeatureAPI&) = delete;

// Set the default provider.
void SetProvider(std::shared_ptr<FeatureProvider> provider) override;

// Set a provider for a specific domain.
void SetProvider(std::string_view domain,
std::shared_ptr<FeatureProvider> provider) override;

// Set the default provider and blocks until it successfully initializes.
void SetProviderAndWait(std::shared_ptr<FeatureProvider> provider) override;

// Set a named provider and blocks until it successfully initializes.
void SetProviderAndWait(std::string_view domain,
std::shared_ptr<FeatureProvider> provider) override;
// If the domain is empty then GetProvider returns the default provider
// otherwise it returns the provider for the domain. If this domain has no
// provider bound, it returns the default provider.
std::shared_ptr<FeatureProvider> GetProvider(
std::string_view domain = "") const override;

// Gets a client for the default domain.
std::shared_ptr<Client> GetClient() override;

// Gets a client for a named domain.
std::shared_ptr<Client> GetClient(std::string_view domain) override;

// Sets the global evaluation context.
void SetEvaluationContext(const EvaluationContext& ctx) override;

// Gets the global evaluation context.
EvaluationContext GetEvaluationContext() const override;

// Get metadata about the default provider if domain is empty
// or about a named provider if domain is provided.
Metadata GetProviderMetadata(std::string_view domain = "") const override;

// Fetches the status of a provider for a domain. If the domain is not set or
// not found, it returns the default provider status.
ProviderStatus GetProviderStatus(std::string_view domain = "") const override;

// Shuts down all providers and resets the API to its initial state.
void Shutdown() override;

// TODO: Add methods to add and get Hooks.
// TODO: Add overload function for "GetClient()" to accept "Evaluation
// Options"

private:
ProviderRepository provider_repository_;

OpenFeatureAPI();
};

} // namespace openfeature

#endif // CPP_SDK_INCLUDE_OPENFEATURE_OPENFEATURE_API_H_
10 changes: 10 additions & 0 deletions openfeature/provider_repository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ void ProviderRepository::Shutdown() {
}
}
provider_manager_.clear();

// Re-initialize to the default state after shutting down
std::shared_ptr<openfeature::NoopProvider> noop_provider =
std::make_shared<NoopProvider>();
absl::StatusOr<std::unique_ptr<FeatureProviderStatusManager>> status_manager =
FeatureProviderStatusManager::Create(noop_provider);
if (status_manager.ok()) {
default_manager_ = std::move(status_manager.value());
default_manager_->SetStatus(ProviderStatus::kReady);
}
Comment on lines +119 to +127

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Shutdown() method re-initializes the repository with a NoopProvider. While this is correct for explicit shutdowns via the API, it's inefficient when Shutdown() is called from the ~ProviderRepository() destructor, as the object is about to be destroyed.

A better approach would be to avoid this re-initialization in the destructor. This could be done by parameterizing the Shutdown method. For example:

// In provider_repository.h
void Shutdown(bool reinitialize = true);

// In provider_repository.cpp
ProviderRepository::~ProviderRepository() { 
  Shutdown(false); 
}

void ProviderRepository::Shutdown(bool reinitialize) {
  // ... existing shutdown logic ...
  if (reinitialize) {
    // ... re-initialization logic from lines 119-127 ...
  }
}

Since this change spans multiple files and locations, I'm not providing a direct code suggestion. Please consider this refactoring to improve efficiency and adhere to best practices for destructors.

}

void ProviderRepository::PrepareAndInitializeProvider(
Expand Down
10 changes: 10 additions & 0 deletions test/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ cc_test(
],
)

cc_test(
name = "openfeature_api_test",
srcs = ["openfeature_api_test.cpp"],
deps = [
":mock_feature_provider",
"//openfeature:openfeature_api",
"@googletest//:gtest_main",
],
)

cc_test(
name = "global_context_manager_test",
srcs = ["global_context_manager_test.cpp"],
Expand Down
5 changes: 0 additions & 5 deletions test/global_context_manager_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,3 @@ TEST_F(GlobalContextManagerTest, ThreadSafetyStressTest) {
t.join();
}
}

int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Loading