Skip to content

Commit

Permalink
fix: handle protected attributes correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
cwaldren-ld committed Aug 15, 2024
1 parent 9c621cf commit 9498199
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 64 deletions.
28 changes: 13 additions & 15 deletions libs/common/include/launchdarkly/attributes_builder.hpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#pragma once

Check notice on line 1 in libs/common/include/launchdarkly/attributes_builder.hpp

View workflow job for this annotation

GitHub Actions / cpp-linter

Run clang-format on libs/common/include/launchdarkly/attributes_builder.hpp

File libs/common/include/launchdarkly/attributes_builder.hpp does not conform to Custom style guidelines. (lines 23, 34, 214, 230, 241)

#include <string>

#include <launchdarkly/attribute_reference.hpp>
#include <launchdarkly/attributes.hpp>
#include <launchdarkly/value.hpp>

namespace launchdarkly {
#include <map>
#include <string>

namespace launchdarkly {
class ContextBuilder;

/**
Expand All @@ -22,7 +22,7 @@ template <class BuilderReturn, class BuildType>
class AttributesBuilder final {
friend class ContextBuilder;

public:
public:
/**
* Create an attributes builder with the given kind and key.
* @param builder The context builder associated with this attributes
Expand All @@ -31,7 +31,8 @@ class AttributesBuilder final {
* @param key The key for the kind.
*/
AttributesBuilder(BuilderReturn& builder, std::string kind, std::string key)
: key_(std::move(key)), kind_(std::move(kind)), builder_(builder) {}
: key_(std::move(key)), kind_(std::move(kind)), builder_(builder) {
}

/**
* Crate an attributes builder with the specified kind, and pre-populated
Expand Down Expand Up @@ -96,11 +97,9 @@ class AttributesBuilder final {
*
* @param name The name of the attribute.
* @param value The value for the attribute.
* @param private_attribute If the attribute should be considered private:
* that is, the value will not be sent to LaunchDarkly in analytics events.
* @return A reference to the current builder.
*/
AttributesBuilder& Set(std::string name, launchdarkly::Value value);
AttributesBuilder& Set(std::string name, Value value);

/**
* Add or update a private attribute in the context.
Expand All @@ -115,11 +114,9 @@ class AttributesBuilder final {
*
* @param name The name of the attribute.
* @param value The value for the attribute.
* @param private_attribute If the attribute should be considered private:
* that is, the value will not be sent to LaunchDarkly in analytics events.
* @return A reference to the current builder.
*/
AttributesBuilder& SetPrivate(std::string name, launchdarkly::Value value);
AttributesBuilder& SetPrivate(std::string name, Value value);

/**
* Designate a context attribute, or properties within them, as private:
Expand Down Expand Up @@ -216,7 +213,7 @@ class AttributesBuilder final {
*/
[[nodiscard]] BuildType Build() const { return builder_.Build(); }

private:
private:
BuilderReturn& builder_;

/**
Expand All @@ -229,15 +226,16 @@ class AttributesBuilder final {
Attributes BuildAttributes() const;

Check warning on line 226 in libs/common/include/launchdarkly/attributes_builder.hpp

View workflow job for this annotation

GitHub Actions / cpp-linter

/libs/common/include/launchdarkly/attributes_builder.hpp:226:5 [modernize-use-nodiscard]

function 'BuildAttributes' should be marked [[nodiscard]]

AttributesBuilder& Set(std::string name,
launchdarkly::Value value,
Value value,
bool private_attribute);


std::string kind_;
std::string key_;
std::string name_;
bool anonymous_ = false;

std::map<std::string, launchdarkly::Value> values_;
std::map<std::string, Value> values_;
AttributeReference::SetType private_attributes_;
};
} // namespace launchdarkly
} // namespace launchdarkly
93 changes: 83 additions & 10 deletions libs/common/src/attributes_builder.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include <launchdarkly/attributes_builder.hpp>

Check notice on line 1 in libs/common/src/attributes_builder.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

Run clang-format on libs/common/src/attributes_builder.cpp

File libs/common/src/attributes_builder.cpp does not conform to Custom style guidelines. (lines 26, 27, 28, 29, 39, 41, 42, 43, 45, 49, 50, 51, 52, 53, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 75, 91, 110, 111, 118, 132, 136)
#include <launchdarkly/context_builder.hpp>

namespace launchdarkly {
#include <set>
#include <unordered_map>

namespace launchdarkly {
template <>
AttributesBuilder<ContextBuilder, Context>&
AttributesBuilder<ContextBuilder, Context>::Name(std::string name) {
Expand All @@ -17,33 +19,104 @@ AttributesBuilder<ContextBuilder, Context>::Anonymous(bool anonymous) {
return *this;
}

// These attributes values cannot be set via the public Set/SetPrivate methods.
// 'key' and 'kind' are defined in the AttributesBuilder constructor, whereas
// '_meta' is set internally by the SDK.
std::set<std::string> const& ProtectedAttributes() {
static std::set<std::string> protectedAttrs = {
"key",
"kind",
"_meta"
};
return protectedAttrs;
}

// Utility struct to enforce the type-safe setting of a particular attribute
// via a method on AttributesBuilder.
struct TypedAttribute {
enum Value::Type type;
std::function<void(AttributesBuilder<ContextBuilder, Context>& builder,

Check failure on line 38 in libs/common/src/attributes_builder.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

/libs/common/src/attributes_builder.cpp:38:10 [clang-diagnostic-error]

no template named 'function' in namespace 'std'
Value)> setter;

TypedAttribute(enum Value::Type type,

Check warning on line 41 in libs/common/src/attributes_builder.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

/libs/common/src/attributes_builder.cpp:41:5 [cppcoreguidelines-pro-type-member-init]

constructor does not initialize these fields: setter
std::function<void(

Check failure on line 42 in libs/common/src/attributes_builder.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

/libs/common/src/attributes_builder.cpp:42:25 [clang-diagnostic-error]

no template named 'function' in namespace 'std'
AttributesBuilder<ContextBuilder, Context>& builder,
Value const&)> setter)
: type(type), setter(std::move(setter)) {
}
};

// These attribute values, while able to be set via public Set/SetPrivate methods,
// are regarded as special and are type-enforced. Instead of being stored in the
// internal AttributesBuilder::values map, they are stored as individual fields.
// This is because they have special meaning/usage within the LaunchDarkly Platform.
std::unordered_map<std::string, TypedAttribute> const&
TypedAttributes() {
static std::unordered_map<std::string, TypedAttribute> typedAttrs = {

Check failure on line 55 in libs/common/src/attributes_builder.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

/libs/common/src/attributes_builder.cpp:55:60 [clang-diagnostic-error]

no matching constructor for initialization of 'std::unordered_map<std::string, TypedAttribute>' (aka 'unordered_map<basic_string<char>, launchdarkly::TypedAttribute>')
{
"anonymous", {Value::Type::kBool,
[](AttributesBuilder<
ContextBuilder, Context>
& builder,
Value const& value) {
builder.Anonymous(
value.AsBool());
}}
},
{"name", {Value::Type::kString,
[](AttributesBuilder<
ContextBuilder, Context>&
builder,
Value const& value) {
builder.Name(value.AsString());
}}}
};
return typedAttrs;
}


template <>
AttributesBuilder<ContextBuilder, Context>&
AttributesBuilder<ContextBuilder, Context>::Set(std::string name,
Value value,
bool private_attribute) {
if (name == "key" || name == "kind" || name == "anonymous" ||
name == "name" || name == "_meta") {
// Protected attributes cannot be set at all.
if (ProtectedAttributes().count(name) > 0) {
return *this;
}

// Typed attributes can be set only if the value is of the correct type;
// the setting is forwarded to one of AttributeBuilder's dedicated methods.
auto const typed_attr = TypedAttributes().find(name);
const bool is_typed = typed_attr != TypedAttributes().end();
if (is_typed && value.Type() != typed_attr->second.type) {
return *this;
}

if (is_typed) {
typed_attr->second.setter(*this, value);
} else {
values_[name] = std::move(value);
}

if (private_attribute) {
this->private_attributes_.insert(name);
this->private_attributes_.insert(std::move(name));
}
values_[std::move(name)] = std::move(value);
return *this;
}

template <>
AttributesBuilder<ContextBuilder, Context>&
AttributesBuilder<ContextBuilder, Context>::Set(std::string name, Value value) {
AttributesBuilder<ContextBuilder, Context>::Set(
std::string name,
Value value) {
return Set(std::move(name), std::move(value), false);
}

template <>
AttributesBuilder<ContextBuilder, Context>&
AttributesBuilder<ContextBuilder, Context>::SetPrivate(std::string name,
Value value) {
Value value) {
return Set(std::move(name), std::move(value), true);
}

Expand All @@ -56,8 +129,8 @@ AttributesBuilder<ContextBuilder, Context>::AddPrivateAttribute(
}

template <>
Attributes AttributesBuilder<ContextBuilder, Context>::BuildAttributes() const {
Attributes AttributesBuilder<
ContextBuilder, Context>::BuildAttributes() const {
return {key_, name_, anonymous_, values_, private_attributes_};
}

} // namespace launchdarkly
} // namespace launchdarkly
Loading

0 comments on commit 9498199

Please sign in to comment.