Skip to content
2 changes: 1 addition & 1 deletion Development/nmos/control_protocol_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace nmos

// find the relevant nc_property_descriptor
const auto& property = nc::find_property_descriptor(details::parse_property_id(property_id), details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor);
if (!property.is_null())
if (!property.is_null() && resource.data.has_field(nmos::fields::nc::name(property)))
{
return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, resource.data.at(nmos::fields::nc::name(property)));
}
Expand Down
24 changes: 23 additions & 1 deletion Development/nmos/control_protocol_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,28 @@ namespace nmos
}
}

// Validate that the resource has been correctly constructed according to the class_descriptor
void validate_resource(const resource& resource, const experimental::control_class_descriptor& class_descriptor)
{
// Validate properties
for (const auto& property_descriptor : class_descriptor.property_descriptors.as_array())
{
if ((resource.data.is_null()) || (!resource.data.has_field(nmos::fields::nc::name(property_descriptor))))
{
throw control_protocol_exception("missing control resource property: " + utility::us2s(nmos::fields::nc::name(property_descriptor)));
}
}

// Validate methods
for (const auto& method_descriptor : class_descriptor.method_descriptors)
{
if (!method_descriptor.second)
{
throw control_protocol_exception("method not implemented: " + utility::us2s(nmos::fields::nc::name(method_descriptor.first)));
}
}
}

resources::const_iterator find_resource_by_role_path(const resources& resources, const web::json::array& role_path_)
{
auto role_path = role_path_;
Expand Down Expand Up @@ -1299,7 +1321,7 @@ namespace nmos
{
// A monitor is expected to go through a period of instability upon activation. Therefore, on monitor activation
// domain specific statuses offering an Inactive option MUST transition immediately to the Healthy state.
// Furthermore, after activation, as long as the monitor isnt being deactivated, it MUST delay the reporting
// Furthermore, after activation, as long as the monitor isn't being deactivated, it MUST delay the reporting
// of non Healthy states for the duration specified by statusReportingDelay, and then transition to any other appropriate state.
const auto& found = find_resource(resources, utility::s2us(std::to_string(oid)));
if (resources.end() != found && nc::is_status_monitor(nc::details::parse_class_id(nmos::fields::nc::class_id(found->data))))
Expand Down
3 changes: 3 additions & 0 deletions Development/nmos/control_protocol_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ namespace nmos
// method parameters constraints validation, may throw nmos::control_protocol_exception
void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor);

// Validate that the resource has been correctly constructed according to the class_descriptor
void validate_resource(const resource& resource, const experimental::control_class_descriptor& class_descriptor);

resources::const_iterator find_touchpoint_resource(const resources& resources, const resource& resource);

// insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values
Expand Down
99 changes: 99 additions & 0 deletions Development/nmos/test/control_protocol_utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1254,3 +1254,102 @@ BST_TEST_CASE(testFindMembersByClassId)
BST_REQUIRE_EQUAL(example_class_id_2, nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(members.at(0))));
}
}

BST_TEST_CASE(testValidateResourceProperties)
{
using web::json::value_of;
using web::json::value;

nmos::experimental::control_protocol_state control_protocol_state;
auto get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state);

auto root_block = nmos::make_root_block();
auto oid = nmos::root_block_oid;
// root, ClassManager
auto class_manager = nmos::make_class_manager(++oid, control_protocol_state);
auto receiver_block_oid = ++oid;
// root, receivers
auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block"));

// root, receivers, mon1
auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} }));

auto block_class_descriptor = get_control_protocol_class_descriptor(nmos::nc_block_class_id);

BST_CHECK_NO_THROW(nmos::nc::validate_resource(root_block, block_class_descriptor));
BST_CHECK_NO_THROW(nmos::nc::validate_resource(receivers, block_class_descriptor));

auto class_manager_class_descriptor = get_control_protocol_class_descriptor(nmos::nc_class_manager_class_id);

BST_CHECK_NO_THROW(nmos::nc::validate_resource(class_manager, class_manager_class_descriptor));

auto receiver_monitor_class_descriptor = get_control_protocol_class_descriptor(nmos::nc_receiver_monitor_class_id);

BST_CHECK_NO_THROW(nmos::nc::validate_resource(monitor1, receiver_monitor_class_descriptor));

// Negative tests
BST_CHECK_THROW(nmos::nc::validate_resource(root_block, receiver_monitor_class_descriptor), nmos::control_protocol_exception);
BST_CHECK_THROW(nmos::nc::validate_resource(receivers, class_manager_class_descriptor), nmos::control_protocol_exception);
BST_CHECK_THROW(nmos::nc::validate_resource(class_manager, block_class_descriptor), nmos::control_protocol_exception);
BST_CHECK_THROW(nmos::nc::validate_resource(monitor1, block_class_descriptor), nmos::control_protocol_exception);
}

BST_TEST_CASE(testValidateResourceMethods)
{
using web::json::value_of;
using web::json::value;

nmos::experimental::control_protocol_state control_protocol_state;
auto get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state);

auto root_block = nmos::make_root_block();

auto example_method_with_no_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate)
{
return nmos::nc::details::make_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok });
};

auto control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 2 });
// Define class descriptor with method and associated method - should succeed on validate
{
std::vector<web::json::value> property_descriptors = {};

std::vector<nmos::experimental::method> method_descriptors =
{
{ nmos::experimental::make_control_class_method_descriptor(U("Example method with no arguments"), { 3, 1 }, U("MethodNoArgs"), U("NcMethodResult"), {}, false, example_method_with_no_args) }
};

auto control_class_descriptor = nmos::experimental::make_control_class_descriptor(U(""), nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 2 }), U("ControlClass"), property_descriptors, method_descriptors, {});

auto make_control_object = [&](nmos::nc_oid oid)
{
auto data = nmos::nc::details::make_worker(control_class_id, oid, true, 1, U("role"), value::string(U("")), U(""), value::null(), value::null(), true);
return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true };
};

auto control_object = make_control_object(3);

BST_CHECK_NO_THROW(nmos::nc::validate_resource(control_object, control_class_descriptor));
}
// Define class descriptor with method with no associated method - should throw on validate
{
std::vector<web::json::value> property_descriptors = {};

std::vector<nmos::experimental::method> method_descriptors =
{
{ nmos::experimental::make_control_class_method_descriptor(U("Example method with no arguments"), { 3, 1 }, U("MethodNoArgs"), U("NcMethodResult"), {}, false, nullptr) }
};

auto control_class_descriptor = nmos::experimental::make_control_class_descriptor(U(""), nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 2 }), U("ControlClass"), property_descriptors, method_descriptors, {});

auto make_control_object = [&](nmos::nc_oid oid)
{
auto data = nmos::nc::details::make_worker(control_class_id, oid, true, 1, U("role"), value::string(U("")), U(""), value::null(), value::null(), true);
return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true };
};

auto control_object = make_control_object(3);

BST_CHECK_THROW(nmos::nc::validate_resource(control_object, control_class_descriptor), nmos::control_protocol_exception);
}
}
Loading