Skip to content

Commit

Permalink
More flexible default implementation of annotation_patch_merger
Browse files Browse the repository at this point in the history
  • Loading branch information
garethsb committed May 22, 2023
1 parent 9cd8985 commit 674bb36
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 18 deletions.
18 changes: 14 additions & 4 deletions Development/nmos-cpp-node/node_implementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1270,13 +1270,23 @@ nmos::channelmapping_activation_handler make_node_implementation_channelmapping_
}

// Example Annotation API patch callback to update resource labels, descriptions and tags
nmos::annotation_patch_merger make_node_implementation_annotation_patch_merger(slog::base_gate& gate)
nmos::annotation_patch_merger make_node_implementation_annotation_patch_merger(const nmos::settings& settings, slog::base_gate& gate)
{
return [&gate](const nmos::resource& resource, web::json::value& value, const web::json::value& patch)
using web::json::value;
using web::json::value_of;

return [&settings, &gate](const nmos::resource& resource, web::json::value& value, const web::json::value& patch)
{
const std::pair<nmos::id, nmos::type> id_type{ resource.id, resource.type };
slog::log<slog::severities::info>(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Updating " << id_type;
nmos::details::merge_annotation_patch(value, patch);
// this example uses the specified tags for node and device resources as defaults
const auto default_tags
= id_type.second == nmos::types::node ? impl::fields::node_tags(settings)
: id_type.second == nmos::types::device ? impl::fields::device_tags(settings)
: value::object();
// and uses the default predicate for read-only tags
nmos::details::merge_annotation_patch(value, patch, &nmos::details::is_read_only_tag, value_of({ { nmos::fields::tags, default_tags } }));
// this example does not save the new values to persistent storage or e.g. reject values that are too large
};
}

Expand Down Expand Up @@ -1434,5 +1444,5 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode
.on_connection_activated(make_node_implementation_connection_activation_handler(model, gate))
.on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required
.on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate))
.on_merge_annotation_patch(make_node_implementation_annotation_patch_merger(gate)); // may be omitted if not required
.on_merge_annotation_patch(make_node_implementation_annotation_patch_merger(model.settings, gate)); // may be omitted if not required
}
29 changes: 20 additions & 9 deletions Development/nmos/annotation_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,35 @@ namespace nmos

namespace details
{
// BCP-002-01 Group Hint tag and BCP-002-02 Asset Distinguishing Information tags are read-only
// all other tags are read/write
bool is_read_only_tag(const utility::string_t& key)
{
return boost::algorithm::starts_with(key, U("urn:x-nmos:tag:asset:"))
|| boost::algorithm::starts_with(key, U("urn:x-nmos:tag:grouphint/"));
}

void merge_annotation_patch(web::json::value& value, const web::json::value& patch)
const web::json::field_as_string_or default_label{ nmos::fields::label.key, U("") };
const web::json::field_as_string_or default_description{ nmos::fields::description.key, U("") };
const web::json::field_as_value_or default_tags{ nmos::fields::tags.key, web::json::value::object() };

void merge_annotation_patch(web::json::value& value, const web::json::value& patch, annotation_tag_predicate is_read_only_tag, const web::json::value& default_value)
{
// reject changes to read-ony tags

if (patch.has_object_field(nmos::fields::tags))
{
const auto& tags = nmos::fields::tags(patch);
auto patch_readonly = std::find_if(tags.begin(), tags.end(), [](const std::pair<utility::string_t, web::json::value>& field)
auto patch_readonly = std::find_if(tags.begin(), tags.end(), [&](const std::pair<utility::string_t, web::json::value>& field)
{
return is_read_only_tag(field.first);
});
if (tags.end() != patch_readonly) throw std::runtime_error("cannot patch read-only tag: " + utility::us2s(patch_readonly->first));
}

// save existing read-only tags
// save existing read-only tags (so that read-only tags don't need to be included in default_value)

auto readonly_tags = web::json::value_from_fields(nmos::fields::tags(value) | boost::adaptors::filtered([](const std::pair<utility::string_t, web::json::value>& field)
auto readonly_tags = web::json::value_from_fields(nmos::fields::tags(value) | boost::adaptors::filtered([&](const std::pair<utility::string_t, web::json::value>& field)
{
return is_read_only_tag(field.first);
}));
Expand All @@ -100,16 +106,21 @@ namespace nmos

// apply defaults to properties that have been reset

web::json::insert(value, std::make_pair(nmos::fields::label, U("")));
web::json::insert(value, std::make_pair(nmos::fields::description, U("")));
web::json::insert(value, std::make_pair(nmos::fields::label, details::default_label(default_value)));
web::json::insert(value, std::make_pair(nmos::fields::description, details::default_description(default_value)));
web::json::insert(value, std::make_pair(nmos::fields::tags, readonly_tags));
auto& tags = value.at(nmos::fields::tags);
for (const auto& default_tag : details::default_tags(default_value).as_object())
{
web::json::insert(tags, default_tag);
}
}

void assign_annotation_patch(web::json::value& value, web::json::value&& patch)
{
if (value.has_string_field(nmos::fields::label)) value[nmos::fields::label] = std::move(patch.at(nmos::fields::label));
if (value.has_string_field(nmos::fields::description)) value[nmos::fields::description] = std::move(patch.at(nmos::fields::description));
if (value.has_object_field(nmos::fields::tags)) value[nmos::fields::tags] = std::move(patch.at(nmos::fields::tags));
if (patch.has_string_field(nmos::fields::label)) value[nmos::fields::label] = std::move(patch.at(nmos::fields::label));
if (patch.has_string_field(nmos::fields::description)) value[nmos::fields::description] = std::move(patch.at(nmos::fields::description));
if (patch.has_object_field(nmos::fields::tags)) value[nmos::fields::tags] = std::move(patch.at(nmos::fields::tags));
}

void handle_annotation_patch(nmos::resources& resources, const nmos::resource& resource, const web::json::value& patch, const nmos::annotation_patch_merger& merge_patch, slog::base_gate& gate)
Expand Down
16 changes: 11 additions & 5 deletions Development/nmos/annotation_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ namespace nmos

namespace details
{
void merge_annotation_patch(web::json::value& value, const web::json::value& patch);
typedef std::function<bool(const utility::string_t& name)> annotation_tag_predicate;

// BCP-002-01 Group Hint tag and BCP-002-02 Asset Distinguishing Information tags are read-only
// all other tags are read/write
bool is_read_only_tag(const utility::string_t& name);

// this function merges the patch into the value with few additional constraints
// when any fields are reset using null, default values are applied if specified or
// read-write tags are removed, and label and description are set to the empty string
void merge_annotation_patch(web::json::value& value, const web::json::value& patch, annotation_tag_predicate is_read_only_tag = &nmos::details::is_read_only_tag, const web::json::value& default_value = {});
}

// this function merges the patch into the value with few additional constraints
// i.e. label, description and all tags are read/write except Group Hint and Asset Distinguishing Information
// when reset using null, tags are removed, and label and description are set to the empty string
// (this is the default patch merger)
// this is the default patch merger
inline void merge_annotation_patch(const nmos::resource& resource, web::json::value& value, const web::json::value& patch)
{
details::merge_annotation_patch(value, patch);
Expand Down

0 comments on commit 674bb36

Please sign in to comment.