Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
40e9f05
feat: Add attribute reference support.
kinyoklion Mar 23, 2023
1f43c9e
mend
kinyoklion Mar 23, 2023
e731ca2
Remove equality comparisons.
kinyoklion Mar 23, 2023
2ad041a
Add class comment.
kinyoklion Mar 23, 2023
cf7ffb2
Add test for out of bound component. Close namespace.
kinyoklion Mar 23, 2023
4bc1dda
Fix namespace comment.
kinyoklion Mar 23, 2023
38d5f3e
Add newlines to end of files.
kinyoklion Mar 23, 2023
f844a82
Remove extra source from tests CMAKE.
kinyoklion Mar 23, 2023
b51e895
Remove awkward comment wrap.
kinyoklion Mar 23, 2023
0cede01
Add ostream operator. Retain redaction_name for invalid references.
kinyoklion Mar 23, 2023
7f77f5c
Clang format
kinyoklion Mar 23, 2023
e4a5bd7
c is too short!
kinyoklion Mar 23, 2023
462b3f8
chore: Implement CI build/test.
kinyoklion Mar 24, 2023
9df9037
Use BOOST_ROOT
kinyoklion Mar 24, 2023
a7c4f6f
Use google test executable.
kinyoklion Mar 24, 2023
6e8ed91
Update to use shared CI workflow.
kinyoklion Mar 24, 2023
f2aca36
Add dot slash.
kinyoklion Mar 24, 2023
40bd8bc
Encapsulate libname
kinyoklion Mar 24, 2023
cd1613f
Add test setup for client-sdk
kinyoklion Mar 24, 2023
de249f0
Merge branch 'main' into rlamb/sc-193990/implement-attribute-references
kinyoklion Mar 24, 2023
b58cd6d
Merge branch 'rlamb/sc-193990/implement-attribute-references' into rl…
kinyoklion Mar 24, 2023
652f392
Add ci for all projects.
kinyoklion Mar 24, 2023
e899878
Add blank lines to end of placeholder tests.
kinyoklion Mar 24, 2023
14e4068
Remove test script, add blank lines to the end of other scripts.
kinyoklion Mar 24, 2023
a9185e2
Lint PR titles.
kinyoklion Mar 24, 2023
59875dd
feat: Implement contexts.
kinyoklion Mar 27, 2023
c266153
Improvements to value type.
kinyoklion Mar 27, 2023
0001e83
Rule of five.
kinyoklion Mar 27, 2023
3622248
Test value type.
kinyoklion Mar 27, 2023
d158782
Add assignment operators.
kinyoklion Mar 27, 2023
86ff211
Add some comments.
kinyoklion Mar 28, 2023
b3c68d9
Progress on implementing access by reference.
kinyoklion Mar 28, 2023
4c06c87
Continue test implementation.
kinyoklion Mar 28, 2023
d4e74be
Encapsulate Array and Object.
kinyoklion Mar 28, 2023
b5d35b4
Context builder in-place.
kinyoklion Mar 29, 2023
8b65916
Start on tidy
kinyoklion Mar 29, 2023
432d6ad
Merge branch 'main' into rlamb/context
kinyoklion Mar 29, 2023
ef898af
Fix terrible merge.
kinyoklion Mar 29, 2023
f99395a
Boostier.
kinyoklion Mar 29, 2023
baf6460
Another location.
kinyoklion Mar 29, 2023
8305d2d
Update cmake for boost.
kinyoklion Mar 29, 2023
0a4b1ff
std::size_t
kinyoklion Mar 29, 2023
5ab874f
Attempt to trick compiler
kinyoklion Mar 29, 2023
70b4bba
Missing import
kinyoklion Mar 29, 2023
3b5a648
More includes.
kinyoklion Mar 29, 2023
c037122
Ensure attribute reference set valid.
kinyoklion Mar 29, 2023
287b094
Initial tidy.
kinyoklion Mar 29, 2023
b982483
Move to boost::variant
kinyoklion Mar 29, 2023
48c8adb
More tidy.
kinyoklion Mar 30, 2023
5e953c3
Add ostream operators
kinyoklion Mar 30, 2023
4f8c711
Add context validation and canonical key.
kinyoklion Mar 30, 2023
5e6f98c
Refine implementation.
kinyoklion Mar 30, 2023
80e17c7
Additional tidy.
kinyoklion Mar 30, 2023
b8490e9
Further tidy.
kinyoklion Mar 30, 2023
1a7211a
Add extra blank line to end of value tests.
kinyoklion Mar 30, 2023
486edeb
PR feedback.
kinyoklion Mar 30, 2023
c12b34c
More tidy.
kinyoklion Mar 30, 2023
024dd4b
String view non-const.
kinyoklion Mar 30, 2023
a9a0564
More PR feedback.
kinyoklion Mar 30, 2023
7561602
Merge branch 'main' into rlamb/context
kinyoklion Mar 30, 2023
a3d8658
More feedback.
kinyoklion Mar 30, 2023
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
9 changes: 6 additions & 3 deletions libs/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif ()

#set(CMAKE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_FILES})

# Needed to fetch external dependencies.
include(FetchContent)

set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.80 REQUIRED)
message(STATUS "LaunchDarkly: using Boost v${Boost_VERSION}")

# Add main SDK sources.
add_subdirectory(src)

Expand Down
47 changes: 44 additions & 3 deletions libs/common/include/attribute_reference.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#pragma once

#include <cstddef>
#include <ostream>
#include <string>
#include <unordered_set>
#include <vector>

#include <boost/container_hash/hash.hpp>

namespace launchdarkly {

/**
Expand All @@ -28,6 +32,19 @@ namespace launchdarkly {
*/
class AttributeReference {
public:
/**
* Provides a hashing function for use with unordered sets.
*/
struct HashFunction {
Copy link
Member Author

Choose a reason for hiding this comment

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

Hashing to work with an unordered set.

std::size_t operator()(AttributeReference const& ref) const {
return boost::hash_range(ref.components_.begin(),
ref.components_.end());
}
};

using SetType = std::unordered_set<AttributeReference,
AttributeReference::HashFunction>;

/**
* Get the component of the attribute reference at the specified depth.
*
Expand All @@ -38,15 +55,15 @@ class AttributeReference {
* @return The component at the specified depth or an empty string if the
* depth is out of bounds.
*/
std::string const& component(size_t depth) const;
std::string const& component(std::size_t depth) const;

/**
* Get the total depth of the reference.
*
* For example, depth() on the reference `/a/b/c` would return 3.
* @return
*/
size_t depth() const;
std::size_t depth() const;

/**
* Check if the reference is a "kind" reference. Either `/kind` or `kind`.
Expand Down Expand Up @@ -94,10 +111,34 @@ class AttributeReference {
return os;
}

/**
* Construct an attribute reference from a string.
* @param ref_str The string to make an attribute reference from.
*/
AttributeReference(std::string ref_str);

/**
* Construct an attribute reference from a constant string.
* @param ref_str The string to make an attribute reference from.
*/
AttributeReference(char const* ref_str);

bool operator==(AttributeReference const& other) const {
Copy link
Member Author

Choose a reason for hiding this comment

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

May as well work with an ordered set as well.

return components_ == other.components_;
}

bool operator!=(AttributeReference const& other) const {
return !(*this == other);
}

bool operator<(AttributeReference const& rhs) const {
return components_ < rhs.components_;
}

private:
AttributeReference(std::string str, bool is_literal);

bool valid_;
bool valid_ = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can this be initialized in the constructor instead? (or rather - is there a reason to do it here)

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know that one is better than the other. It is just easier to make sure that it is initialized regardless of what constructor is used, and it doesn't really cost anything.


std::string redaction_name_;
std::vector<std::string> components_;
Expand Down
151 changes: 151 additions & 0 deletions libs/common/include/attributes.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#pragma once

#include <string>
#include <unordered_set>

#include "attribute_reference.hpp"
#include "value.hpp"

namespace launchdarkly {

/**
* A collection of attributes that can be present within a context.
* A multi-context has multiple sets of attributes keyed by their "kind".
*/
class Attributes {
public:
/**
* Get the key for the context.
* @return A reference to the context key.
*/
std::string const& key() const;

/**
* Get the name for the context.
Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe I should use a Value here. It is actually kind of convenient. :-P

*
* @return A reference to the context name, or an empty string if no name
* is set.
*/
std::string const& name() const;

/**
* Is the context anonymous or not. Defaults to false.
* @return True if the context is anonymous.
*/
bool anonymous() const;

/**
* Get a set of the private attributes for the context.
* @return The set of private attributes for the context.
*/
AttributeReference::SetType const& private_attributes() const {
return private_attributes_;
}

/**
* Gets the item by the specified attribute reference, or returns a null
* Value.
* @param ref The reference to get an attribute by.
* @return A Value containing the requested field, or a Value representing
* null.
*/
launchdarkly::Value const& get(
launchdarkly::AttributeReference const& ref) const {
if (!ref.valid()) {
// Cannot index by invalid references.
return launchdarkly::Value::null();
}
if (ref.is_kind()) {
// Cannot access kind.
return launchdarkly::Value::null();
}

if (ref.depth() == 1) {
// Handle built-in attributes.
if (ref.component(0) == "key") {
return key_;
}
if (ref.component(0) == "name") {
return name_;
}
if (ref.component(0) == "anonymous") {
return anonymous_;
}
}

launchdarkly::Value const* node = &custom_attributes_;
bool found = true;
for (size_t index = 0; index < ref.depth(); index++) {
auto const& component = ref.component(index);
if (node->is_object()) {
auto const& map = node->as_object();
if (auto search = map.find(component); search != map.end()) {
node = &search->second;
} else {
found = false;
break;
}
} else {
found = false;
}
}
if (!found) {
return launchdarkly::Value::null();
}
return *node;
}

/**
* Construct a set of attributes. This is used internally by the SDK
* but is not intended to used by consumers of the SDK.
*
* @param key The key for the context.
* @param name The name of the context.
* @param anonymous If the context is anonymous.
* @param attributes Additional attributes for the context.
* @param private_attributes A list of attributes that should be private.
*/
Attributes(std::string key,
std::optional<std::string> name,
bool anonymous,
launchdarkly::Value attributes,
AttributeReference::SetType private_attributes =
AttributeReference::SetType())
: key_(std::move(key)),
name_(std::move(name)),
anonymous_(anonymous),
custom_attributes_(std::move(attributes)),
private_attributes_(std::move(private_attributes)) {}

friend std::ostream& operator<<(std::ostream& out,
Attributes const& attrs) {
out << "{key: " << attrs.key_ << ", "
<< " name: " << attrs.name_ << " anonymous: " << attrs.anonymous_
<< " private: [";
bool first = true;
for (auto const& private_attribute : attrs.private_attributes_) {
if (first) {
first = false;
} else {
out << ", ";
}
out << private_attribute;
}
out << "] "
<< " custom: " << attrs.custom_attributes_ << "}";

return out;
}

private:
// Built-in attributes.
launchdarkly::Value key_;
launchdarkly::Value name_;
launchdarkly::Value anonymous_;
AttributeReference::SetType private_attributes_;

launchdarkly::Value custom_attributes_;

// Kinds are contained at the context level, not inside attributes.
};
} // namespace launchdarkly
Loading