diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index b49f7873..81ab44fa 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -834,6 +834,90 @@ target_include_directories(nmos_is12_schemas PUBLIC list(APPEND NMOS_CPP_TARGETS nmos_is12_schemas) add_library(nmos-cpp::nmos_is12_schemas ALIAS nmos_is12_schemas) +# nmos_is14_schemas library + +set(NMOS_IS14_SCHEMAS_HEADERS + nmos/is14_schemas/is14_schemas.h + ) + +set(NMOS_IS14_V1_0_TAG v1.0.x) + +set(NMOS_IS14_V1_0_SCHEMAS_JSON + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/base.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-get-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-set-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-set-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-validate-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-validate-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/descriptor-get.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/methods-base.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/ms05-error.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/properties-base.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-descriptor.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-value-get.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-value-put-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-value-put-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/rolePath.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/rolePaths-base.json + ) + +set(NMOS_IS14_SCHEMAS_JSON_MATCH "third_party/is-14/([^/]+)/APIs/schemas/([^;]+)\\.json") +set(NMOS_IS14_SCHEMAS_SOURCE_REPLACE "${CMAKE_CURRENT_BINARY_DIR_REPLACE}/nmos/is14_schemas/\\1/\\2.cpp") +string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}(;|$)" "${NMOS_IS14_SCHEMAS_SOURCE_REPLACE}\\3" NMOS_IS14_V1_0_SCHEMAS_SOURCES "${NMOS_IS14_V1_0_SCHEMAS_JSON}") + +foreach(JSON ${NMOS_IS14_V1_0_SCHEMAS_JSON}) + string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}" "${NMOS_IS14_SCHEMAS_SOURCE_REPLACE}" SOURCE "${JSON}") + string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}" "\\1" NS "${JSON}") + string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}" "\\2" VAR "${JSON}") + string(MAKE_C_IDENTIFIER "${NS}" NS) + string(MAKE_C_IDENTIFIER "${VAR}" VAR) + + file(WRITE "${SOURCE}.in" "\ +// Auto-generated from: ${JSON}\n\ +\n\ +namespace nmos\n\ +{\n\ + namespace is14_schemas\n\ + {\n\ + namespace ${NS}\n\ + {\n\ + const char* ${VAR} = R\"-auto-generated-(") + + file(READ "${JSON}" RAW) + file(APPEND "${SOURCE}.in" "${RAW}") + + file(APPEND "${SOURCE}.in" ")-auto-generated-\";\n\ + }\n\ + }\n\ +}\n") + + configure_file("${SOURCE}.in" "${SOURCE}" COPYONLY) +endforeach() + +add_library( + nmos_is14_schemas STATIC + ${NMOS_IS14_SCHEMAS_HEADERS} + ${NMOS_IS14_V1_0_SCHEMAS_SOURCES} + ) + +source_group("nmos\\is14_schemas\\Header Files" FILES ${NMOS_IS14_SCHEMAS_HEADERS}) +source_group("nmos\\is14_schemas\\${NMOS_IS14_V1_0_TAG}\\Source Files" FILES ${NMOS_IS14_V1_0_SCHEMAS_SOURCES}) + +target_link_libraries( + nmos_is14_schemas PRIVATE + nmos-cpp::compile-settings + ) +target_include_directories(nmos_is14_schemas PUBLIC + $ + $ + ) + +list(APPEND NMOS_CPP_TARGETS nmos_is14_schemas) +add_library(nmos-cpp::nmos_is14_schemas ALIAS nmos_is14_schemas) + # nmos-cpp library set(NMOS_CPP_BST_SOURCES @@ -921,6 +1005,10 @@ set(NMOS_CPP_NMOS_SOURCES nmos/channels.cpp nmos/client_utils.cpp nmos/components.cpp + nmos/configuration_api.cpp + nmos/configuration_methods.cpp + nmos/configuration_resources.cpp + nmos/configuration_utils.cpp nmos/connection_activation.cpp nmos/connection_api.cpp nmos/connection_events_activation.cpp @@ -1014,6 +1102,11 @@ set(NMOS_CPP_NMOS_HEADERS nmos/colorspace.h nmos/components.h nmos/copyable_atomic.h + nmos/configuration_api.h + nmos/configuration_handlers.h + nmos/configuration_methods.h + nmos/configuration_resources.h + nmos/configuration_utils.h nmos/connection_activation.h nmos/connection_api.h nmos/connection_events_activation.h @@ -1048,6 +1141,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/is09_versions.h nmos/is10_versions.h nmos/is12_versions.h + nmos/is14_versions.h nmos/issuers.h nmos/json_fields.h nmos/json_schema.h @@ -1198,6 +1292,7 @@ target_link_libraries( nmos-cpp::nmos_is09_schemas nmos-cpp::nmos_is10_schemas nmos-cpp::nmos_is12_schemas + nmos-cpp::nmos_is14_schemas nmos-cpp::mdns nmos-cpp::slog nmos-cpp::OpenSSL diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 31fa6c21..bcfdefc4 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -43,7 +43,12 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp + nmos/test/configuration_methods_test.cpp + nmos/test/configuration_resources_test.cpp + nmos/test/configuration_utils_test.cpp nmos/test/control_protocol_test.cpp + nmos/test/control_protocol_test.cpp + nmos/test/control_protocol_utils_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp nmos/test/json_validator_test.cpp diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index e4b420fa..9e710aa1 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.filter_property_value_holders, node_implementation.modify_rebuildable_block); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 6450c1fb..2c55939d 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -19,6 +19,10 @@ #include "nmos/channelmapping_resources.h" #include "nmos/clock_name.h" #include "nmos/colorspace.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_methods.h" +#include "nmos/configuration_resources.h" +#include "nmos/configuration_utils.h" #include "nmos/connection_resources.h" #include "nmos/connection_events_activation.h" #include "nmos/control_protocol_resources.h" @@ -928,7 +932,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr if (0 <= nmos::fields::control_protocol_ws_port(model.settings)) { // example to create a non-standard Gain control class - const auto gain_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + const auto gain_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); const web::json::field_as_number gain_value{ U("gainValue") }; { // Gain control class property descriptors @@ -950,7 +954,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }; // example to create a non-standard Example control class - const auto example_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 2 }); + const auto example_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 2 }); const web::json::field_as_number enum_property{ U("enumProperty") }; const web::json::field_as_string string_property{ U("stringProperty") }; const web::json::field_as_number number_property{ U("numberProperty") }; @@ -1169,7 +1173,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }; // example to create a non-standard Temperature Sensor control class - const auto temperature_sensor_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 3 }); + const auto temperature_sensor_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 3 }); const web::json::field_as_number temperature{ U("temperature") }; const web::json::field_as_string unit{ U("uint") }; { @@ -1206,6 +1210,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example class manager auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + // example bulk properties manager + auto bulk_properties_manager = nmos::make_bulk_properties_manager(++oid); + // example stereo gain const auto stereo_gain_oid = ++oid; auto stereo_gain = nmos::make_block(stereo_gain_oid, nmos::root_block_oid, U("stereo-gain"), U("Stereo gain"), U("Stereo gain block")); @@ -1217,14 +1224,14 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto left_gain = make_gain_control(++oid, channel_gain_oid, U("left-gain"), U("Left gain"), U("Left channel gain"), value::null(), value::null(), 0.0); auto right_gain = make_gain_control(++oid, channel_gain_oid, U("right-gain"), U("Right gain"), U("Right channel gain"), value::null(), value::null(), 0.0); // add left-gain and right-gain to channel gain - nmos::push_back(channel_gain, left_gain); - nmos::push_back(channel_gain, right_gain); + nmos::nc::push_back(channel_gain, left_gain); + nmos::nc::push_back(channel_gain, right_gain); // example master-gain auto master_gain = make_gain_control(++oid, channel_gain_oid, U("master-gain"), U("Master gain"), U("Master gain block"), value::null(), value::null(), 0.0); // add channel-gain and master-gain to stereo-gain - nmos::push_back(stereo_gain, channel_gain); - nmos::push_back(stereo_gain, master_gain); + nmos::nc::push_back(stereo_gain, channel_gain); + nmos::nc::push_back(stereo_gain, master_gain); // example example-control auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), U("Example control worker"), @@ -1251,6 +1258,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { make_example_datatype(example_enum::Alpha, U("example"), 50, false), make_example_datatype(example_enum::Gamma, U("different"), 75, true) } ); + const auto receiver_block_oid = ++oid; + auto receiver_block = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors")); + nmos::make_rebuildable(receiver_block); + // example receiver-monitor(s) { int count = 0; @@ -1263,10 +1274,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("monitor-") << ++count; const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); - const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_nmos_resource_types::receiver, receiver_id}) } })); + const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); // add receiver-monitor to root-block - nmos::push_back(root_block, receiver_monitor); + nmos::nc::push_back(receiver_block, receiver_monitor); } } } @@ -1274,16 +1285,20 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example temperature-sensor const auto temperature_sensor = make_temperature_sensor(++oid, nmos::root_block_oid, U("temperature-sensor"), U("Temperature Sensor"), U("Temperature Sensor block"), value::null(), value::null(), 0.0, U("Celsius")); + // add receiver monitor block + nmos::nc::push_back(root_block, receiver_block); // add temperature-sensor to root-block - nmos::push_back(root_block, temperature_sensor); + nmos::nc::push_back(root_block, temperature_sensor); // add example-control to root-block - nmos::push_back(root_block, example_control); + nmos::nc::push_back(root_block, example_control); // add stereo-gain to root-block - nmos::push_back(root_block, stereo_gain); + nmos::nc::push_back(root_block, stereo_gain); // add class-manager to root-block - nmos::push_back(root_block, class_manager); + nmos::nc::push_back(root_block, class_manager); // add device-manager to root-block - nmos::push_back(root_block, device_manager); + nmos::nc::push_back(root_block, device_manager); + // add bulk-properties-manager to root-block + nmos::nc::push_back(root_block, bulk_properties_manager); // insert control protocol resources to model insert_root_after(delay_millis, root_block, gate); @@ -1353,7 +1368,7 @@ void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) // update temperature sensor { - const auto temperature_sensor_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 3 }); + const auto temperature_sensor_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 3 }); const web::json::field_as_number temperature{ U("temperature") }; auto& resources = model.control_protocol_resources; @@ -1370,7 +1385,7 @@ void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) { {3, 1}, nmos::nc_property_change_type::type::value_changed, web::json::value(temp.scaled_value()) } }); - nmos::modify_control_protocol_resource(model.control_protocol_resources, found->id, [&](nmos::resource& resource) + nmos::nc::modify_resource(model.control_protocol_resources, found->id, [&](nmos::resource& resource) { resource.data[temperature] = temp.scaled_value(); @@ -1714,6 +1729,346 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control }; } +// Example Device Configuration callback for validating a back-up dataset +nmos::filter_property_value_holders_handler make_filter_property_value_holders_handler(nmos::resources& resources, slog::base_gate& gate) +{ + return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + // Use this function to filter which of the properties in the object should be modified by the configuration API + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_value_holders"; + + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + + auto modifiable_property_value_holders = web::json::value::array(); + + for (const auto& property_value : property_values) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + + // In this example we are only allowing writable properties to be modified + if (bool(nmos::fields::nc::is_read_only(property_descriptor))) + { + // We need to create a notice for any properties that will not be updated + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value), nmos::nc_property_restore_notice_type::warning, U("Update of read only properties not supported")); + web::json::push_back(property_restore_notices, property_restore_notice); + } + else + { + web::json::push_back(modifiable_property_value_holders, property_value); + } + } + return modifiable_property_value_holders.as_array(); + }; +} + +// Example Device Configuration callback for restoring a back-up dataset +nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::node_model& model, slog::base_gate& gate) +{ + return [&model, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + auto object_properties_set_validations = web::json::value::array(); + + nmos::resources& resources = model.control_protocol_resources; + + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; + + // Validate the object_properties_holder + + // Find object_properties_holder for resource + const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, nmos::get_role_path(resources, resource)); + + if (filtered_holders.size() != 1) + { + auto status_message = U("Either can't find associated object_properties_holder, or there's more than one (ambiguous)"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; + } + + if (!nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + { + auto status_message = U("Expected an NcBlock"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; + } + const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_property_id(2, 2)); + + if (block_members_properties_holder == web::json::value::null()) + { + auto status_message = U("No NcBlockMembersPropertiesHolder found"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; + } + + const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); + const auto& reference_members = nmos::fields::nc::members(resource.data); + + std::vector members_to_remove; + std::vector members_to_add; + + // Iterate through the members of the block and compare to the members in the backup dataset + for (const auto& reference_member : reference_members) + { + auto child_role_path = web::json::value_from_elements(target_role_path); + web::json::push_back(child_role_path, nmos::fields::nc::role(reference_member)); + + const auto& filtered_members = boost::copy_range>(restore_members.as_array() + | boost::adaptors::filtered([&reference_member](const web::json::value& member) + { + return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this oid in restore dataset, so member has been removed + // get the receiver monitor resource + auto found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); + + const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(model.node_resources, *found); + + if (touchpoint_resource != resources.end()) + { + bool success = erase_resource(model.node_resources, nmos::fields::id(touchpoint_resource->data)); + + if (!success) + { + auto status_message = U("Unable to erase resource"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + const auto oid = nmos::fields::nc::oid(found->data); + success = erase_resource(resources, found->id); + if (success) + { + members_to_remove.push_back(oid); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + } + } + } + else + { + const auto restore_member = *filtered_members.begin(); + // We ignore the description and user label as these are non-normative + if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) + || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) + || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) + || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) + { + // Modify existing resource + // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner + // Do nothing, return warning + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; + } + else + { + // Do nothing + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + } + } + } + for (const auto& restore_member : restore_members.as_array()) + { + auto child_role_path = web::json::value_from_elements(target_role_path); + web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); + + const auto& filtered_members = boost::copy_range>(reference_members + | boost::adaptors::filtered([&restore_member](const web::json::value& member) + { + return nmos::fields::nc::oid(restore_member) == nmos::fields::nc::oid(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this oid in existing members, so member has been added + // Add this resource + // Get example resource from the exising members to get node_id, device_id + if (reference_members.size() == 0) + { + auto status_message = U("Cannot duplicate resources when none exist"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + const auto& example_monitor = *reference_members.begin(); + const auto& found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(example_monitor))); + const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(model.node_resources, *found); + if (touchpoint_resource == resources.end()) + { + auto status_message = U("Cannot duplicate resources when none exist"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + const auto& device_id = nmos::fields::device_id(touchpoint_resource->data); + + // Find the object_properties_holder that describes the new receiver monitor + const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) + { + return nmos::fields::nc::path(object_properties_holder) == child_role_path.as_array(); + }) + ); + + if (filtered_child_object_properties_holders.size() != 1) + { + auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + + const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); + + const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 2)); + + if (oid_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find OID object property value holder"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + + const auto& touchpoint_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 7)); + + if (touchpoint_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find touchpoint object property value holder"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + const auto& oid2 = nmos::fields::nc::value(oid_property_holder); + + const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); + + if (touchpoints.size() != 1) + { + auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); + + // Make resources + const auto host_interfaces = nmos::get_host_interfaces(model.settings); + const auto& host_address = nmos::fields::host_address(model.settings); + // the interface corresponding to the host address is used for the example node's WebSocket senders and receivers + const auto host_interface_ = impl::find_interface(host_interfaces, host_address); + if (host_interfaces.end() == host_interface_) + { + slog::log(gate, SLOG_FLF) << "No network interface corresponding to host_address?"; + throw node_implementation_init_exception(); + } + + const auto& primary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::front(nmos::fields::host_addresses(model.settings)).as_string() : host_address; + const auto& secondary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::back(nmos::fields::host_addresses(model.settings)).as_string() : host_address; + const auto primary_interface_ = impl::find_interface(host_interfaces, primary_address); + const auto secondary_interface_ = impl::find_interface(host_interfaces, secondary_address); + if (host_interfaces.end() == primary_interface_ || host_interfaces.end() == secondary_interface_) + { + slog::log(gate, SLOG_FLF) << "No network interface corresponding to one of the host_addresses?"; + throw node_implementation_init_exception(); + } + const auto& primary_interface = *primary_interface_; + const auto& secondary_interface = *secondary_interface_; + const auto smpte2022_7 = impl::fields::smpte2022_7(model.settings); + const auto interface_names = smpte2022_7 + ? std::vector{ primary_interface.name, secondary_interface.name } + : std::vector{ primary_interface.name }; + + auto receiver = nmos::make_receiver(touchpoint_uuid.as_string(), device_id, nmos::transports::rtp, interface_names, model.settings); + + const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 4)); + if (owner_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find owner property value holder."); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + + const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 5)); + if (role_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find role property value holder."); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + + const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); + const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); + + auto receiver_monitor = nmos::make_receiver_monitor(oid2.as_integer(), true, owner, role, U(""), U(""), web::json::value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})}})); + + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U(""), role, oid2.as_integer(), true, nmos::nc_receiver_monitor_class_id, U(""), owner); + + members_to_add.push_back(block_member_descriptor); + // insert resources + insert_resource(model.node_resources, std::move(receiver)); + insert_resource(resources, std::move(receiver_monitor)); + + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + } + } + + // Update the members of the receivers block + if (members_to_remove.size() > 0 || members_to_add.size() > 0) + { + auto modified_members = web::json::value::array(); + + for (const auto& member : reference_members) + { + const auto& remove_member = boost::copy_range>(members_to_remove | boost::adaptors::filtered([&member](int oid) + { + return oid == nmos::fields::nc::oid(member); + }) + ); + + if (remove_member.size() == 0) + { + web::json::push_back(modified_members, member); + } + } + for (const auto& member : members_to_add) + { + web::json::push_back(modified_members, member); + } + + nmos::nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::members] = modified_members; + + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_property_id(2, 2), nmos::nc_property_change_type::type::value_changed, modified_members } })); + } + + return object_properties_set_validations; + }; +} + namespace impl { nmos::interlace_mode get_interlace_mode(const nmos::settings& settings) @@ -1868,5 +2223,7 @@ 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_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)); // may be omitted if IS-12 not required + .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required + .on_filter_property_value_holders(make_filter_property_value_holders_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/api_utils.h b/Development/nmos/api_utils.h index 0f8b132a..f6ef29e6 100644 --- a/Development/nmos/api_utils.h +++ b/Development/nmos/api_utils.h @@ -57,6 +57,8 @@ namespace nmos const route_pattern channelmapping_api = make_route_pattern(U("api"), U("channelmapping")); // IS-09 System API (originally specified in JT-NM TR-1001-1:2018 Annex A) const route_pattern system_api = make_route_pattern(U("api"), U("system")); + // IS-14 Configuration API + const route_pattern configuration_api = make_route_pattern(U("api"), U("configuration")); // API version pattern const route_pattern version = make_route_pattern(U("version"), U("v[0-9]+\\.[0-9]+")); @@ -87,6 +89,11 @@ namespace nmos const route_pattern outputSubroute = make_route_pattern(U("outputSubroute"), U("properties|sourceid|channels|caps")); const route_pattern activationId = make_route_pattern(U("activationId"), U("[a-zA-Z0-9\\-_]+")); + // Configuration API + const route_pattern rolePath = make_route_pattern(U("rolePath"), U("root|root\\.[a-zA-Z0-9\\-_\\.]+")); + const route_pattern propertyId = make_route_pattern(U("propertyId"), U("[0-9]+p[0-9]+")); + const route_pattern methodId = make_route_pattern(U("methodId"), U("[0-9]+m[0-9]+")); + // Common patterns const route_pattern resourceId = make_route_pattern(U("resourceId"), U("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")); } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp new file mode 100644 index 00000000..88a4440f --- /dev/null +++ b/Development/nmos/configuration_api.cpp @@ -0,0 +1,736 @@ +#include "nmos/configuration_api.h" + +#include +#include +#include "cpprest/json_validator.h" +#include "nmos/api_utils.h" +#include "nmos/configuration_methods.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/control_protocol_methods.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/is14_versions.h" +#include "nmos/json_schema.h" +#include "nmos/log_manip.h" +#include "nmos/model.h" + +namespace nmos +{ + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + { + using namespace web::http::experimental::listener::api_router_using_declarations; + + api_router configuration_api; + + configuration_api.support(U("/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("x-nmos/") }, req, res)); + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/x-nmos/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("configuration/") }, req, res)); + return pplx::task_from_result(true); + }); + + if (validate_authorization) + { + configuration_api.support(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/?"), validate_authorization); + configuration_api.support(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/.*"), validate_authorization); + } + + const auto versions = with_read_lock(model.mutex, [&model] { return nmos::is14_versions::from_settings(model.settings); }); + configuration_api.support(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/?"), methods::GET, [versions](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(nmos::make_api_version_sub_routes(versions), req, res)); + return pplx::task_from_result(true); + }); + + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, filter_property_value_holders, modify_rebuildable_block, property_changed, gate)); + + return configuration_api; + } + + namespace details + { + void build_role_paths(const resources& resources, const nmos::resource& resource, const utility::string_t& base_role_path, std::set& role_paths) + { + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + for (const auto& member : members) + { + const auto role_path = base_role_path + U(".") + nmos::fields::nc::role(member); + role_paths.insert(role_path + U("/")); + + // get members on all NcBlock(s) + if (nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + { + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + build_role_paths(resources, *found, role_path, role_paths); + } + } + } + } + } + + nc_property_id parse_formatted_property_id(const utility::string_t& property_id) + { + // Assume that property_id is in form "p" as validated by the propertyId regular expression pattern + const utility::string_t::size_type delimiter = property_id.find('p'); + auto level = property_id.substr(0, delimiter); + auto index = property_id.substr(delimiter + 1); + return { uint16_t(web::json::value::parse(level).as_integer()), uint16_t(web::json::value::parse(index).as_integer()) }; + } + + // format nc_property_id to the form of "p" + utility::string_t make_formatted_property_id(const web::json::value& property_descriptor) + { + auto property_id = nmos::fields::nc::id(property_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(property_id) << 'p' << nmos::fields::nc::index(property_id); + return os.str(); + } + + nc_method_id parse_formatted_method_id(const utility::string_t& method_id) + { + // Assume that method_id is in form "m" as validated by the methodId regular expression pattern + const utility::string_t::size_type delimiter = method_id.find('m'); + auto level = method_id.substr(0, delimiter); + auto index = method_id.substr(delimiter + 1); + return { uint16_t(web::json::value::parse(level).as_integer()), uint16_t(web::json::value::parse(index).as_integer()) }; + } + + // format nc_method_id to the form of "m" + utility::string_t make_formatted_method_id(const web::json::value& method_descriptor) + { + auto method_id = nmos::fields::nc::id(method_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(method_id) << 'm' << nmos::fields::nc::index(method_id); + return os.str(); + } + + static const web::json::experimental::json_validator& configurationapi_validator() + { + // hmm, could be based on supported API versions from settings, like other APIs' validators? + static const web::json::experimental::json_validator validator + { + nmos::experimental::load_json_schema, + boost::copy_range>(boost::range::join(boost::range::join(boost::range::join( + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_method_patch_request_schema_uri), + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_property_value_put_request_schema_uri)), + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_validate_request_schema_uri)), + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_set_request_schema_uri))) + }; + return validator; + } + + bool parse_recurse_query_parameter(const utility::string_t& query) + { + web::json::value arguments = web::json::value_from_query(query); + + if (arguments.has_field(fields::nc::recurse)) + { + return U("false") != arguments.at(fields::nc::recurse).as_string(); + } + + return true; + } + } + + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + { + using namespace web::http::experimental::listener::api_router_using_declarations; + + api_router configuration_api; + + // check for supported API version + const auto versions = with_read_lock(model.mutex, [&model] { return nmos::is14_versions::from_settings(model.settings); }); + configuration_api.support(U(".*"), details::make_api_version_handler(versions, gate_)); + + configuration_api.support(U("/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("rolePaths/") }, req, res)); + return pplx::task_from_result(true); + }); + + // GET /rolePaths + configuration_api.support(U("/rolePaths/?"), methods::GET, [&model](http_request req, http_response res, const string_t&, const route_parameters&) + { + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + std::set role_paths; + + // start at the root block resource + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + // add root to role_paths + const auto role_path = nmos::fields::nc::role(resource->data); + role_paths.insert(role_path + U("/")); + + // add rest to the role_paths + details::build_role_paths(resources, *resource, role_path, role_paths); + } + + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(role_paths, req, res)); + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath} + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/?"), methods::GET, [&model, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + + if (resources.end() != resource) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptor/"), U("methods/"), U("properties/") }, req, res)); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath}/properties + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + std::set properties_routes; + + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& property_descriptors = control_class.property_descriptors.as_array(); + + auto properties_route = boost::copy_range>(property_descriptors | boost::adaptors::transformed([](const web::json::value& property_descriptor) + { + return details::make_formatted_property_id(property_descriptor) + U("/"); + })); + + properties_routes.insert(properties_route.begin(), properties_route.end()); + + class_id.pop_back(); + } + + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(properties_routes, req, res)); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath}/methods + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + std::set methods_routes; + + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& method_descriptors = control_class.method_descriptors; + + auto methods_route = boost::copy_range>(method_descriptors | boost::adaptors::transformed([](const nmos::experimental::method& method) + { + auto make_method_id = [](const nmos::experimental::method& method) + { + // method tuple definition described in control_protocol_handlers.h + auto& nc_method_descriptor = std::get<0>(method); + return details::make_formatted_method_id(nc_method_descriptor); + }; + + return make_method_id(method) + U("/"); + })); + + methods_routes.insert(methods_route.begin(), methods_route.end()); + + class_id.pop_back(); + } + + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(methods_routes, req, res)); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath}/descriptor + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + + if (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + + auto& description = control_class.description; + auto& name = control_class.name; + auto& fixed_role = control_class.fixed_role; + auto property_descriptors = control_class.property_descriptors; + auto method_descriptors = value::array(); + for (const auto& method_descriptor : control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + auto event_descriptors = control_class.event_descriptors; + + auto inherited_class_id = class_id; + inherited_class_id.pop_back(); + + while (!inherited_class_id.empty()) + { + const auto& inherited_control_class = get_control_protocol_class_descriptor(inherited_class_id); + { + for (const auto& property_descriptor : inherited_control_class.property_descriptors.as_array()) { web::json::push_back(property_descriptors, property_descriptor); } + for (const auto& method_descriptor : inherited_control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + for (const auto& event_descriptor : inherited_control_class.event_descriptors.as_array()) { web::json::push_back(event_descriptors, event_descriptor); } + } + inherited_class_id.pop_back(); + } + + auto class_descriptor = fixed_role.is_null() + ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + + auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); + set_reply(res, status_codes::OK, method_result); + } + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath}/properties/{propertyId} + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + } + else + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("descriptor/"), U("value/") }, req, res)); + } + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath}/properties/{propertyId}/descriptor + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + } + else + { + auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, property_descriptor); + set_reply(res, status_codes::OK, method_result); + } + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath}/properties/{propertyId}/value - invokes get method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + auto arguments = value_of({ + { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + }); + + auto result = get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // GET /rolePaths/{rolePath}/methods/{methodId} - invokes method specified by {methodId} + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/") + nmos::patterns::methodId.pattern + U("/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + nmos::api_gate gate(gate_, req, parameters); + return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + { + auto lock = model.write_lock(); + + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_method_patch_request_schema_uri(version)); + + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const auto method_id = parameters.at(nmos::patterns::methodId.name); + + auto& resources = model.control_protocol_resources; + auto& arguments = nmos::fields::nc::arguments(body); + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); + auto& nc_method_descriptor = method.first; + auto& control_method_handler = method.second; + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + if (control_method_handler) + { + try + { + // do method arguments constraints validation + nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); + + // execute the relevant control method handler, then accumulating up their response to reponses + method_result = control_method_handler(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("invalid argument: ") << arguments.serialize() << " error: " << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + } + else + { + // unknown methodId + utility::stringstream_t ss; + ss << U("unsupported method_id: ") << method_id + << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); + + code = status_codes::NotFound; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return true; + }); + }); + + // PUT /rolePaths/{rolePath}/properties/{propertyId}/value - invokes set method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + nmos::api_gate gate(gate_, req, parameters); + return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + { + auto lock = model.write_lock(); + + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_property_value_put_request_schema_uri(version)); + + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + } + else + { + auto arguments = value_of({ + { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + { nmos::fields::nc::value, nmos::fields::nc::value(body)} + }); + + auto result = set(resources, *resource, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); + + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); + model.notify(); + } + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return true; + }); + }); + + // GET /rolePaths/{rolePath}/bulkProperties - invokes get_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + + if (resources.end() != resource) + { + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + try + { + bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + + method_result = get_properties_by_path(resources, *resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable + { + auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); + + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + try + { + const auto& arguments = nmos::fields::nc::arguments(body); + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& backup_data_set = nmos::fields::nc::data_set(arguments); + + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + return true; + }); + + return pplx::task_from_result(true); + }); + + // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable + { + auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); + + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + try + { + const auto& arguments = nmos::fields::nc::arguments(body); + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& backup_data_set = nmos::fields::nc::data_set(arguments); + + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + model.notify(); + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return true; + }); + + return pplx::task_from_result(true); + }); + + return configuration_api; + } +} diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h new file mode 100644 index 00000000..5bf4df97 --- /dev/null +++ b/Development/nmos/configuration_api.h @@ -0,0 +1,22 @@ +#ifndef NMOS_CONFIGURATION_API_H +#define NMOS_CONFIGURATION_API_H + +#include "cpprest/api_router.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/configuration_handlers.h" + +namespace slog +{ + class base_gate; +} + +// Configuration API implementation +// See https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html +namespace nmos +{ + struct node_model; + + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); +} + +#endif diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h new file mode 100644 index 00000000..7ca6940e --- /dev/null +++ b/Development/nmos/configuration_handlers.h @@ -0,0 +1,30 @@ +#ifndef NMOS_CONFIGURATION_HANDLERS_H +#define NMOS_CONFIGURATION_HANDLERS_H + +#include +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + namespace experimental + { + struct control_protocol_state; + } + // This callback is invoked if attempting to modify read only properties when restoring a configuration. + // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object + typedef std::function filter_property_value_holders_handler; + + // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. + // This function should handle the modification of the Device Model and any corresponding NMOS resources + // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added + typedef std::function modify_rebuildable_block_handler; +} + +#endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp new file mode 100644 index 00000000..9e3b39a8 --- /dev/null +++ b/Development/nmos/configuration_methods.cpp @@ -0,0 +1,147 @@ +#include "nmos/configuration_methods.h" + +#include +#include "cpprest/json_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_utils.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" + +namespace nmos +{ + namespace details + { + web::json::array make_property_value_holders(const nmos::resource& resource, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + using web::json::value; + + value property_value_holders = value::array(); + + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + + // make NcPropertyValueHolder objects + while (!class_id.empty()) + { + const auto& control_class_descriptor = get_control_protocol_class_descriptor(class_id); + + for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) + { + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), nmos::fields::nc::name(property_descriptor), nmos::fields::nc::type_name(property_descriptor), nmos::fields::nc::is_read_only(property_descriptor), resource.data.at(nmos::fields::nc::name(property_descriptor))); + + web::json::push_back(property_value_holders, property_value_holder); + } + class_id.pop_back(); + } + return property_value_holders.as_array(); + } + + void populate_object_property_holder(const nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, bool recurse, web::json::value& object_properties_holders) + { + using web::json::value; + + // Get property_value_holders for this resource + const auto& property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor); + + const auto role_path = get_role_path(resources, resource); + + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, nmos::fields::nc::is_rebuildable(resource.data)); + + web::json::push_back(object_properties_holders, object_properties_holder); + + // Recurse into members...if we want to...and the object has them + if (recurse && nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + { + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + for (const auto& member : members) + { + const auto& found = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); + + if (resources.end() != found) + { + populate_object_property_holder(resources, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders); + } + } + } + } + } + + std::size_t generate_validation_fingerprint(const nmos::resources& resources, const nmos::resource& resource) + { + // Generate a hash based on structure of the Device Model + size_t hash(0); + + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + + boost::hash_combine(hash, class_id); + boost::hash_combine(hash, nmos::fields::nc::role(resource.data)); + + // Recurse into members...if we want to...and the object has them + if (nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + { + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + // Generate hash for block members + for (const auto& member : members) + { + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + size_t sub_hash = generate_validation_fingerprint(resources, *found); + boost::hash_combine(hash, sub_hash); + } + } + } + } + + return hash; + } + } + + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) + { + using web::json::value; + using web::json::value_of; + + value object_properties_holders = value::array(); + + details::populate_object_property_holder(resources, get_control_protocol_class_descriptor, resource, recurse, object_properties_holders); + + size_t validation_fingerprint = details::generate_validation_fingerprint(resources, resource); + + utility::ostringstream_t ss; + ss << validation_fingerprint; + + auto bulk_values_holder = nmos::details::make_nc_bulk_values_holder(ss.str(), object_properties_holders); + + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder); + } + + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + // Do something with validation fingerprint? + const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); + + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + } + + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + // Do something with validation fingerprint? + const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); + + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + } + +} \ No newline at end of file diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h new file mode 100644 index 00000000..65d9eaa4 --- /dev/null +++ b/Development/nmos/configuration_methods.h @@ -0,0 +1,25 @@ +#ifndef NMOS_CONFIGURATION_METHODS_H +#define NMOS_CONFIGURATION_METHODS_H + +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + struct control_protocol_resource; + + // Implementation of IS-14 function for creating backup dataset from a Device Model + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); + + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); +} + +#endif diff --git a/Development/nmos/configuration_resources.cpp b/Development/nmos/configuration_resources.cpp new file mode 100644 index 00000000..949fa3fd --- /dev/null +++ b/Development/nmos/configuration_resources.cpp @@ -0,0 +1,28 @@ +#include "nmos/configuration_resources.h" + +#include "cpprest/json_utils.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/slog.h" + +namespace nmos +{ + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message) + { + return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::string(status_message)); + } + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices) + { + return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::null()); + } + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status) + { + return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::null()); + } + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const utility::string_t& status_message) + { + return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::string(status_message)); + } +} diff --git a/Development/nmos/configuration_resources.h b/Development/nmos/configuration_resources.h new file mode 100644 index 00000000..c4569233 --- /dev/null +++ b/Development/nmos/configuration_resources.h @@ -0,0 +1,18 @@ +#ifndef NMOS_CONFIGURATION_RESOURCES_H +#define NMOS_CONFIGURATION_RESOURCES_H + +#include "nmos/control_protocol_typedefs.h" +#include "nmos/resources.h" + +namespace nmos +{ + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message); + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices); + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status); + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const utility::string_t& status_message); +} + +#endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp new file mode 100644 index 00000000..f6a72e28 --- /dev/null +++ b/Development/nmos/configuration_utils.cpp @@ -0,0 +1,370 @@ +#include "nmos/configuration_methods.h" + +#include +#include "cpprest/json_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_resources.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/slog.h" + +namespace nmos +{ + namespace details + { + bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, bool is_rebuildable) + { + const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); + bool is_valid = true; + // Check the name of the property is correct + if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value)) + { + utility::ostringstream_t os; + os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value), nmos::nc_property_restore_notice_type::error, os.str()); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + // Check the type of the property value is correct + if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value)) + { + utility::ostringstream_t os; + os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value), nmos::nc_property_restore_notice_type::error, os.str()); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + // Only allow modification of read only properties when in Rebuild mode + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) + && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) + { + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + // Only allow modification of read only properties when object is rebuildable + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) + && !is_rebuildable) + { + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + + return is_valid; + } + + bool is_contains_read_only_property(const web::json::array& property_values, const nmos::nc_class_id& class_id, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + for (const auto& property_value : property_values) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + + if (bool(nmos::fields::nc::is_read_only(property_descriptor))) + { + return true; + } + } + return false; + } + } + + // Check to see if root_role_path is root of role_path + bool is_role_path_root(const web::json::array& role_path_root, const web::json::array& role_path) + { + if (role_path_root.size() > role_path.size()) + { + // root can't be longed that the path + return false; + } + for (size_t i = 0; i < role_path_root.size(); ++i) + { + if (role_path_root.at(i) != role_path.at(i)) + { + return false; + } + } + return true; + } + + bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder) + { + // Are they blocks? + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + if (!nmos::nc::is_block(class_id)) + { + return false; + } + const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([](const web::json::value& property_value_holder) + { + return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (block_members_properties_holders.size() == 1) + { + const auto& members_property_holder = *block_members_properties_holders.begin(); + const auto& restore_members = nmos::fields::nc::value(members_property_holder); + const auto& reference_members = nmos::fields::nc::members(resource.data); + + if (reference_members.size() != restore_members.as_array().size()) + { + return true; + } + for (const auto& reference_member : reference_members) + { + const auto& filtered_members = boost::copy_range>(restore_members.as_array() + | boost::adaptors::filtered([&reference_member](const web::json::value& member) + { + return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this oid, so member has been removed + return true; + } + const auto& restore_member = *filtered_members.begin(); + // We ignore the description and user label as these are non-normative + if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) + || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) + || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) + || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) + { + return true; + } + } + } + + return false; + } + + web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path) + { + const auto& target_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return target_role_path == nmos::fields::nc::path(object_properties_holder); + }) + ); + return web::json::value_from_elements(target_object_properties_holders).as_array(); + } + + web::json::array get_child_object_properties_holders(const web::json::array& object_properties_holders, const web::json::array& target_role_path) + { + const auto& child_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); + }) + ); + return web::json::value_from_elements(child_object_properties_holders).as_array(); + } + + web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + auto object_properties_set_validation_values = web::json::value::array(); + + // Filter for the target_role_path and child objects + // + // hmmmmm, I don't like this two step filter process - creating a boost array and then converting to a json array. + // Could this be done in a single step? + const auto& child_object_properties_holders = get_child_object_properties_holders(object_properties_holders, target_role_path); + + // get object_properties_holder for the target role path, if there is one + const auto& target_object_properties_holders = get_object_properties_holder(object_properties_holders, target_role_path); + + // there should be 0 or 1 object_properties_holder for any role path. + if (target_object_properties_holders.size() > 1) + { + // Error in the backup dataset + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, U("more than one object_properties_holder for role path")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + return object_properties_set_validation_values; + } + + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + + if (nmos::nc::is_block(class_id)) + { + // if rebuildable and the block has changed then callback + if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) + { + if (modify_rebuildable_block) + { + // call back to application code which will return an object_properties_set_validation_values object + return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, validate, get_control_protocol_class_descriptor); + } + else + { + // Rebuilding blocks not supported + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, U("Rebuilding of Device Model blocks not supported")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + return object_properties_set_validation_values; + } + } + // iterate through child objects + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + for (const auto& member : members) + { + const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); + + if (resources.end() != child) + { + // Append the role of the child to the target role path to create the child role path + auto child_role_path = web::json::value_from_elements(target_role_path); + web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); + + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + // Hmmm, there must be a better way of merging two json array objects + for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); + } + } + } + } + } + for (const auto& target_object_properties_holder : target_object_properties_holders) + { + auto property_restore_notices = web::json::value::array(); + // Validate property_values - filter out the incorrect, ignored or unallowed values + const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) + | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + + return resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value) + && details::is_property_value_valid(property_restore_notices, property_value, property_descriptor, restore_mode, bool(nmos::fields::nc::is_rebuildable(resource.data))); + }) + ); + auto property_modify_list = web::json::value_from_elements(filtered_property_values).as_array(); + + if (details::is_contains_read_only_property(property_modify_list, class_id, get_control_protocol_class_descriptor)) + { + if (filter_property_value_holders) + { + // If the property_modify_list contains read only properties then we call back to the application code to + // check that it's OK to change those value. Bear in mind that they could be the class Id, or the oid or some other + // property that we don't want changed + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); + } + else + { + // Modify of read only properties not supported + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + continue; + } + } + for (const auto& property_value : property_modify_list) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + + // hmmm, ideally we would pass the value into modify_resource with the validate + // flag, so that it's subject to property contraints and also the application code can decide if it's a legal value + if (!validate) + { + // modify control protocol resources + const auto& value = nmos::fields::nc::value(property_value); + + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource_) + { + resource_.data[nmos::fields::nc::name(property_value)] = value; + + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { {property_id, nmos::nc_property_change_type::type::value_changed, value} })); + } + } + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + } + + return object_properties_set_validation_values; + } + + web::json::array get_role_path(const nmos::resources& resources, const nmos::resource& resource) + { + // Find role path for object + // Hmmm do we not have a library function to do this? + using web::json::value; + + auto role_path = value::array(); + web::json::push_back(role_path, nmos::fields::nc::role(resource.data)); + + auto oid = nmos::fields::nc::id(resource.data); + nmos::resource found_resource = resource; + + while (utility::s2us(std::to_string(nmos::root_block_oid)) != oid.as_string()) + { + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::owner(found_resource.data)))); + if (resources.end() == found) + { + break; + } + + found_resource = (*found); + web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data)); + oid = nmos::fields::nc::id(found_resource.data); + } + + std::reverse(role_path.as_array().begin(), role_path.as_array().end()); + + return role_path.as_array(); + } + + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + auto object_properties_set_validation_values = web::json::value::array(); + + const auto target_role_path = get_role_path(resources, resource); + + // Detect and warn if there are any object_properties_holders outside of the target role path's scope + // Hmmm, can this be done as a one step process rather than filtering and then iterating over filtered list? + const auto& orphan_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return !nmos::is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); + }) + ); + for (const auto& orphan_object_properties_holder : orphan_object_properties_holders) + { + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, U("object role path not found under target role path")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + } + + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + // Hmmm - there must be a better way to append an array + for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); + } + + return object_properties_set_validation_values; + } + + web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id) + { + const auto& filtered_property_value_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([&property_id](const web::json::value& property_value_holder) + { + return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (filtered_property_value_holders.size() != 1) + { + // Error + return web::json::value::null(); + } + + return *filtered_property_value_holders.begin(); + } +} diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h new file mode 100644 index 00000000..5bf6fa8e --- /dev/null +++ b/Development/nmos/configuration_utils.h @@ -0,0 +1,31 @@ +#ifndef NMOS_CONFIGURATION_UTILS_H +#define NMOS_CONFIGURATION_UTILS_H + +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace nmos +{ + struct control_protocol_resource; + + // Check to see if role_path is sub path of parent_role_path + bool is_role_path_root(const web::json::array& role_path_, const web::json::array& parent_role_path); + + bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder); + + // Get role path of resource given the Device Model resources + web::json::array get_role_path(const nmos::resources& resources, const nmos::resource& resource); + + // Get object_properties_holder for specified target_role_path + web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); + + // Get object_properties_holder for specified target_role_path and all its child object_properties_holders + web::json::array get_child_object_properties_holders(const web::json::array& object_properties_holders, const web::json::array& target_role_path); + + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + + web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); +} + +#endif diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 522bcc96..8fb51622 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -74,7 +74,7 @@ namespace nmos { return [&resources](const resource& connection_resource) { - auto found = find_control_protocol_resource(resources, nmos::types::nc_receiver_monitor, connection_resource.id); + auto found = nc::find_resource(resources, nmos::types::nc_receiver_monitor, connection_resource.id); if (resources.end() != found && nc_receiver_monitor_class_id == details::parse_nc_class_id(nmos::fields::nc::class_id(found->data))) { // update receiver-monitor's connectionStatus and payloadStatus properties @@ -91,7 +91,7 @@ namespace nmos { nc_receiver_monitor_payload_status_property_id, nc_property_change_type::type::value_changed, payload_status } }); - modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource) + nc::modify_resource(resources, found->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::connection_status] = connection_status; resource.data[nmos::fields::nc::payload_status] = payload_status; diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 9894cd5e..ed14ee8b 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -12,7 +12,7 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -21,7 +21,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { return details::make_nc_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))); @@ -46,7 +46,7 @@ namespace nmos // find the relevant nc_property_descriptor const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -70,10 +70,10 @@ namespace nmos try { // do property constraints validation - nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); // update property - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)] = val; @@ -104,7 +104,7 @@ namespace nmos } // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -114,7 +114,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -159,7 +159,7 @@ namespace nmos // find the relevant nc_property_descriptor const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -183,10 +183,10 @@ namespace nmos try { // do property constraints validation - nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); // update property - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)][index] = val; @@ -237,7 +237,7 @@ namespace nmos // find the relevant nc_property_descriptor const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -260,10 +260,10 @@ namespace nmos try { // do property constraints validation - nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); // update property - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } @@ -306,7 +306,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -322,7 +322,7 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); sequence.erase(index); @@ -347,7 +347,7 @@ namespace nmos } // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -358,7 +358,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (!nmos::fields::nc::is_sequence(property)) @@ -416,7 +416,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; auto descriptors = value::array(); - nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); + nmos::nc::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } @@ -506,7 +506,7 @@ namespace nmos } auto descriptors = value::array(); - nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); + nmos::nc::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } @@ -533,14 +533,14 @@ namespace nmos // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... auto descriptors = value::array(); - nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); + nmos::nc::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { using web::json::value; @@ -595,7 +595,7 @@ namespace nmos } // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, slog::base_gate& gate) + web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index 957f9362..b406b886 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -13,11 +13,11 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Set property value web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Set sequence item web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Add item to sequence @@ -25,7 +25,7 @@ namespace nmos // Delete sequence item web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // NcBlock methods implementation // Get descriptors of members of the block @@ -39,9 +39,9 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, slog::base_gate& gate); + web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_nmos_resource_type.h b/Development/nmos/control_protocol_nmos_resource_type.h index 436b7225..b6c4aa16 100644 --- a/Development/nmos/control_protocol_nmos_resource_type.h +++ b/Development/nmos/control_protocol_nmos_resource_type.h @@ -8,15 +8,15 @@ namespace nmos { // resourceType // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos - DEFINE_STRING_ENUM(ncp_nmos_resource_type) - namespace ncp_nmos_resource_types + DEFINE_STRING_ENUM(ncp_touchpoint_resource_type) + namespace ncp_touchpoint_resource_types { - const ncp_nmos_resource_type node{ U("node") }; - const ncp_nmos_resource_type device{ U("device") }; - const ncp_nmos_resource_type source{ U("source") }; - const ncp_nmos_resource_type flow{ U("flow") }; - const ncp_nmos_resource_type sender{ U("sender") }; - const ncp_nmos_resource_type receiver{ U("receiver") }; + const ncp_touchpoint_resource_type node{ U("node") }; + const ncp_touchpoint_resource_type device{ U("device") }; + const ncp_touchpoint_resource_type source{ U("source") }; + const ncp_touchpoint_resource_type flow{ U("flow") }; + const ncp_touchpoint_resource_type sender{ U("sender") }; + const ncp_touchpoint_resource_type receiver{ U("receiver") }; } } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 83ccaad0..5eb18305 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -712,6 +712,9 @@ namespace nmos data[nmos::fields::nc::touchpoints] = touchpoints; data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // IS-14 isRebuilable flag + // use make_rebuildable function to declare an control protocl resource rebuildable + data[nmos::fields::nc::is_rebuildable] = value::boolean(false); return data; } @@ -827,7 +830,88 @@ namespace nmos { nmos::fields::nc::change_type, property_changed_event_data.change_type }, { nmos::fields::nc::value, property_changed_event_data.value }, { nmos::fields::nc::sequence_item_index, property_changed_event_data.sequence_item_index } - }); + }, true + ); + } + + // TODO: add link + web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + auto data = make_nc_manager(nc_bulk_properties_manager_class_id, oid, true, owner, U("BulkPropertiesManager"), user_label, description, touchpoints, runtime_property_constraints); + + return data; + } + + web::json::value make_nc_bulk_values_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::validation_fingerprint, validation_fingerprint }, + { nmos::fields::nc::values, object_properties_holders } + }, true + ); + } + + // TODO: add link + web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::name, value::string(name)}, + { nmos::fields::nc::type_name, value::string(type_name)}, + { nmos::fields::nc::is_read_only, value::boolean(is_read_only)}, + { nmos::fields::nc::value, property_value}, + }, true); + } + + // TODO: add link + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, bool is_rebuildable) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, + { nmos::fields::nc::values, web::json::value_from_elements(property_value_holders)}, + { nmos::fields::nc::is_rebuildable, is_rebuildable} + }, true + ); + + } + + // TODO: add link + web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::name, value::string(name)}, + { nmos::fields::nc::notice_type, value::number(notice_type)}, + { nmos::fields::nc::notice_message, value::string(notice_message)} + }, true + ); + } + + // TODO: add link + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, + { nmos::fields::nc::status, value::number(status)}, + { nmos::fields::nc::notices, web::json::value_from_elements(notices)}, + { nmos::fields::nc::status_message, status_message} + }, true + ); } } @@ -1227,6 +1311,52 @@ namespace nmos return value::array(); } + // Device configuration classes + // NcBulkPropertiesManager + // TODO: add link + web::json::value make_nc_bulk_properties_manager_properties() + { + using web::json::value; + + return value::array(); + } + web::json::value make_nc_bulk_properties_manager_methods() + { + using web::json::value; + + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkValuesHolder"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + } + + return methods; + } + web::json::value make_nc_bulk_properties_manager_events() + { + using web::json::value; + + return value::array(); + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html web::json::value make_nc_object_class() { @@ -1301,6 +1431,15 @@ namespace nmos return details::make_nc_class_descriptor(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); } + // Device configuration feature set control classes + // TODO: add link + web::json::value make_nc_bulk_properties_manager_class() + { + using web::json::value; + + return details::make_nc_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), make_nc_bulk_properties_manager_properties(), make_nc_bulk_properties_manager_methods(), make_nc_bulk_properties_manager_events()); + } + // Primitive datatypes // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives web::json::value make_nc_boolean_datatype() @@ -2049,4 +2188,122 @@ namespace nmos web::json::push_back(items, details::make_nc_enum_item_descriptor(U("A payload error was encountered"), U("PayloadError"), 3)); return details::make_nc_datatype_descriptor_enum(U("Connection status enum data typee"), U("NcPayloadStatus"), items, value::null()); } + + // Device Configuration datatypes + // TODO: add link + web::json::value make_nc_restore_mode_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Modify"), U("Modify"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Rebuild"), U("Rebuild"), 1)); + + return details::make_nc_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); + } + // TODO: add link + web::json::value make_nc_property_value_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property type name. If null it means the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Is the property ReadOnly?"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Property value holder descriptor"), U("NcPropertyValueHolder"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_object_properties_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties values"), nmos::fields::nc::values, U("NcPropertyValueHolder"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_bulk_values_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional vendor specific fingerprinting mechanism used for validation purposes"), nmos::fields::nc::validation_fingerprint, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Values by rolePath"), nmos::fields::nc::values, U("NcObjectPropertiesHolder"), false, true, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Bulk values holder descriptor"), U("NcBulkValuesHolder"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_restore_validation_status_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore was successful"), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed"), U("Failed"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), U("NotFound"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); + return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); + } + // TODO: add link + web::json::value make_nc_property_restore_notice_type_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), nc_property_restore_notice_type::warning)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), nc_property_restore_notice_type::error)); + return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); + } + // TODO: add link + web::json::value make_nc_property_restore_notice_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice type"), nmos::fields::nc::notice_type, U("NcPropertyRestoreNoticeType"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice message"), nmos::fields::nc::notice_message, U("NcString"), false, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Property restore notice descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_object_properties_set_validation_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcRestoreValidationStatus"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation property notices"), nmos::fields::nc::notices, U("NcPropertyRestoreNotice"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_method_result_bulk_values_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Bulk values holder value"), nmos::fields::nc::value, U("NcBulkValuesHolder"), false, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk values holder descriptor"), U("NcMethodResultBulkValuesHolder"), fields, U("NcMethodResult"), value::null()); + } + // TODO: add link + web::json::value make_nc_method_result_object_properties_set_validation_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties set path validation"), nmos::fields::nc::value, U("NcObjectPropertiesSetValidation"), false, true, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Method result containing object properties set validation descriptor"), U("NcMethodResultObjectPropertiesSetValidation"), fields, U("NcMethodResult"), value::null()); + } } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 5f341949..632cd7ab 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -191,6 +191,24 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); + + // TODO: add link + web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + + // TODO: add link + web::json::value make_nc_bulk_values_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); + + // TODO: add link + web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); + + // TODO: add link + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, bool is_rebuildable); + + // TODO: add link + web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); + + // TODO: add link + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message); } // command message response @@ -281,6 +299,12 @@ namespace nmos web::json::value make_nc_ident_beacon_methods(); web::json::value make_nc_ident_beacon_events(); + // Device configuration classes + // TODO: add link + web::json::value make_nc_bulk_properties_manager_properties(); + web::json::value make_nc_bulk_properties_manager_methods(); + web::json::value make_nc_bulk_properties_manager_events(); + // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev // @@ -428,6 +452,29 @@ namespace nmos web::json::value make_nc_connection_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus web::json::value make_nc_payload_status_datatype(); + + // Device configuration feature set datatypes + // TODO: add link + // + web::json::value make_nc_restore_mode_datatype(); + // + web::json::value make_nc_property_value_holder_datatype(); + // + web::json::value make_nc_object_properties_holder_datatype(); + // + web::json::value make_nc_bulk_values_holder_datatype(); + // + web::json::value make_nc_restore_validation_status_datatype(); + // + web::json::value make_nc_property_restore_notice_type_datatype(); + // + web::json::value make_nc_property_restore_notice_datatype(); + // + web::json::value make_nc_object_properties_set_validation_datatype(); + // + web::json::value make_nc_method_result_bulk_values_holder_datatype(); + // + web::json::value make_nc_method_result_object_properties_set_validation_datatype(); } #endif diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 0fde54c3..69c782a0 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -27,12 +27,21 @@ namespace nmos return details::make_block(oid, value(owner), role, user_label, description, touchpoints, runtime_property_constraints, members); } + control_protocol_resource make_rebuildable(control_protocol_resource& control_protocol_resource) + { + using web::json::value; + + control_protocol_resource.data[nmos::fields::nc::is_rebuildable] = value::boolean(true); + + return control_protocol_resource; + } + // create Root block resource control_protocol_resource make_root_block() { using web::json::value; - return details::make_block(1, value::null(), U("root"), U("Root"), U("Root block"), value::null(), value::null(), value::array()); + return details::make_block(nmos::root_block_oid, value::null(), nmos::root_block_role, U("Root"), U("Root block"), value::null(), value::null(), value::array()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager @@ -98,4 +107,16 @@ namespace nmos return{ is12_versions::v1_0, types::nc_ident_beacon, std::move(data), true }; } + + // Device Configuration feature set control classes + // + // TODO: add link + control_protocol_resource make_bulk_properties_manager(nc_oid oid) + { + using web::json::value; + + auto data = details::make_nc_bulk_properties_manager(oid, root_block_oid, value::string(U("Bulk properties manager")), U("The bulk properties manager offers a central model for getting and setting properties of multiple role paths"), value::null(), value::null()); + + return{ is12_versions::v1_0, types::nc_bulk_properties_manager, std::move(data), true }; + } } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 4ad7d85d..25526cca 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -16,6 +16,9 @@ namespace nmos // create block resource control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + // make object rebuildable - for IS-14 dynamic configuration of Device Model + control_protocol_resource make_rebuildable(control_protocol_resource& control_protocol_resource); + // create Root block resource control_protocol_resource make_root_block(); @@ -49,6 +52,11 @@ namespace nmos control_protocol_resource make_ident_beacon(nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), bool enabled = true, bool active = false ); + + // Device Configuration feature set control classes + // + // create Bulk Properties Manager resource + control_protocol_resource make_bulk_properties_manager(nc_oid oid); } #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index dd4b1911..e775950a 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -1,7 +1,9 @@ #include "nmos/control_protocol_state.h" +#include "cpprest/http_utils.h" #include "nmos/control_protocol_methods.h" #include "nmos/control_protocol_resource.h" +#include "nmos/configuration_methods.h" namespace nmos { @@ -89,9 +91,9 @@ namespace nmos { nmos::experimental::control_protocol_method_handler make_nc_get_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_set_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed) @@ -103,9 +105,9 @@ namespace nmos } nmos::experimental::control_protocol_method_handler make_nc_get_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get_sequence_item(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_set_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed) @@ -131,9 +133,9 @@ namespace nmos } nmos::experimental::control_protocol_method_handler make_nc_get_sequence_length_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_sequence_length(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get_sequence_length(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_member_descriptors_handler() @@ -166,24 +168,85 @@ namespace nmos } nmos::experimental::control_protocol_method_handler make_nc_get_control_class_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_control_class(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get_control_class(arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_datatype_handler(get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { - return [get_control_protocol_datatype_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_datatype_descriptor](nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_datatype(resources, resource, arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); + return get_datatype(arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); + }; + } + nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) + { + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + bool recurse = nmos::fields::nc::recurse(arguments); + + return nmos::get_properties_by_path(resources, resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + }; + } + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) + { + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& data_set = nmos::fields::nc::data_set(arguments); + + if (data_set.is_null()) + { + return nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + } + + auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); + if (filter_property_value_holders && modify_rebuildable_block) + { + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + const auto& status = nmos::fields::nc::status(result); + if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) + { + return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + } + } + return result; + }; + } + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) + { + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& data_set = nmos::fields::nc::data_set(arguments); + + if (data_set.is_null()) + { + return nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + } + + auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); + if (filter_property_value_holders && modify_rebuildable_block) + { + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + const auto& status = nmos::fields::nc::status(result); + if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) + { + return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + } + } + return result; }; } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) { - using web::json::value; - auto to_vector = [](const web::json::value& data) { if (!data.is_null()) @@ -314,7 +377,17 @@ namespace nmos // NcReceiverMonitorProtected methods to_methods_vector(make_nc_receiver_monitor_protected_methods(), {}), // NcReceiverMonitorProtected events - to_vector(make_nc_receiver_monitor_protected_events())) } + to_vector(make_nc_receiver_monitor_protected_events())) }, + // NcBulkPropertiesManager + { nc_bulk_properties_manager_class_id, make_control_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), + to_vector(make_nc_bulk_properties_manager_properties()), + to_methods_vector(make_nc_bulk_properties_manager_methods(), + { + { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, modify_rebuildable_block) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, modify_rebuildable_block) } + }), + to_vector(make_nc_bulk_properties_manager_events())) } }; // setup the standard datatypes @@ -393,7 +466,19 @@ namespace nmos // Monitoring feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes { U("NcConnectionStatus"), {make_nc_connection_status_datatype()} }, - { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} } + { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} }, + // Device configuration feature set + // TODO: add link + { U("NcRestoreMode"), {make_nc_restore_mode_datatype()} }, + { U("NcPropertyValueHolder"), {make_nc_property_value_holder_datatype()}}, + { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()}}, + { U("NcBulkValuesHolder"), {make_nc_bulk_values_holder_datatype()}}, + { U("NcRestoreValidationStatus"), {make_nc_restore_validation_status_datatype()}}, + { U("NcPropertyRestoreNoticeType"), {make_nc_property_restore_notice_type_datatype()}}, + { U("NcPropertyRestoreNotice"), {make_nc_property_restore_notice_datatype()}}, + { U("NcObjectPropertiesSetValidation"), {make_nc_object_properties_set_validation_datatype()}}, + { U("NcMethodResultBulkValuesHolder"), {make_nc_method_result_bulk_values_holder_datatype()}}, + { U("NcMethodResultObjectPropertiesSetValidation"), {make_nc_method_result_object_properties_set_validation_datatype()}} }; } diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index b49d4e50..65833693 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -3,6 +3,7 @@ #include #include "cpprest/json_utils.h" +#include "nmos/configuration_handlers.h" #include "nmos/control_protocol_handlers.h" #include "nmos/control_protocol_typedefs.h" #include "nmos/mutex.h" @@ -58,8 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed); - + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, filter_property_value_holders_handler filter_property_value_holders = nullptr, modify_rebuildable_block_handler modify_rebuildable_block = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 7afcefed..e2d97575 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -124,6 +124,38 @@ namespace nmos }; } + // NcRestoreMode + namespace nc_restore_mode + { + enum restore_mode + { + modify = 0, // Restore mode is Modify + rebuild = 1 // Restore mode is Rebuild + }; + } + + // NcRestoreValidationStatus + namespace nc_restore_validation_status + { + enum status + { + ok = 200, // Restore was successful + failed = 400, // Restore failed because relevant backup data set provided is invalid + not_found = 404, // Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set + device_error = 500 // Restore failed due to an internal device error preventing the restore from happening + }; + } + + // NcPropertyRestoreNoticeType + namespace nc_property_restore_notice_type + { + enum type + { + warning = 300, // Warning property restore notice + error = 400 // Error property restore notice + }; + } + // NcElementId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid struct nc_element_id @@ -171,6 +203,11 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager const nc_method_id nc_class_manager_get_control_class_method_id(3, 1); const nc_method_id nc_class_manager_get_datatype_method_id(3, 2); + // NcMethodsIds for NcBulkPropertiesManager + // TODO: add link + const nc_method_id nc_bulk_properties_manager_get_properties_by_path_method_id(3, 1); + const nc_method_id nc_bulk_properties_manager_validate_set_properties_by_path_method_id(3, 2); + const nc_method_id nc_bulk_properties_manager_set_properties_by_path_method_id(3, 3); // NcPropertyId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid @@ -232,8 +269,6 @@ namespace nmos // NcOid // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncoid typedef uint32_t nc_oid; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Blocks.html - const nc_oid root_block_oid{ 1 }; // NcUri // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncuri @@ -272,6 +307,8 @@ namespace nmos const nc_class_id nc_receiver_monitor_class_id({ 1, 2, 3 }); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); + // TODO: add link + const nc_class_id nc_bulk_properties_manager_class_id({ 1, 3, 3 }); // NcTouchpoint // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint @@ -353,7 +390,7 @@ namespace nmos , id(id) {} - nc_touchpoint_resource_nmos(const ncp_nmos_resource_type& resource_type, nc_uuid id) + nc_touchpoint_resource_nmos(const ncp_touchpoint_resource_type& resource_type, nc_uuid id) : nc_touchpoint_resource(resource_type.name) , id(id) {} @@ -381,6 +418,14 @@ namespace nmos friend bool operator!=(const nc_touchpoint_resource_nmos_channel_mapping& lhs, const nc_touchpoint_resource_nmos_channel_mapping& rhs) { return !(lhs == rhs); } friend bool operator<(const nc_touchpoint_resource_nmos_channel_mapping& lhs, const nc_touchpoint_resource_nmos_channel_mapping& rhs) { return lhs.tied() < rhs.tied(); } }; + + // Root block specification + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Blocks.html + const nc_oid root_block_oid{ 1 }; + const utility::string_t root_block_role{ U("root") }; + + // Device Configuration + const utility::string_t bulk_properties_manager_role{ root_block_role + U(".BulkPropertiesManager") }; } #endif diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 9d17c096..26d6b23b 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -1,7 +1,9 @@ #include "nmos/control_protocol_utils.h" +#include #include #include +#include #include #include "bst/regex.h" #include "cpprest/json_utils.h" @@ -13,629 +15,738 @@ namespace nmos { - namespace details + namespace nc { - bool is_control_class(const nc_class_id& control_class_id, const nc_class_id& class_id_) + namespace details { - nc_class_id class_id{ class_id_ }; - if (control_class_id.size() < class_id.size()) + bool is_control_class(const nc_class_id& control_class_id, const nc_class_id& class_id_) { - // truncate test class_id to relevant class_id - class_id.resize(control_class_id.size()); + nc_class_id class_id{ class_id_ }; + if (control_class_id.size() < class_id.size()) + { + // truncate test class_id to relevant class_id + class_id.resize(control_class_id.size()); + } + return control_class_id == class_id; } - return control_class_id == class_id; - } - // get the runtime property constraints of a specific property_id - web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints) - { - using web::json::value; - - if (!runtime_property_constraints.is_null()) + // get the runtime property constraints of a specific property_id + web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints) { - auto& runtime_prop_constraints = runtime_property_constraints.as_array(); - auto found_constraints = std::find_if(runtime_prop_constraints.begin(), runtime_prop_constraints.end(), [&property_id](const web::json::value& constraints) - { - return property_id == parse_nc_property_id(nmos::fields::nc::property_id(constraints)); - }); + using web::json::value; - if (runtime_prop_constraints.end() != found_constraints) + if (!runtime_property_constraints.is_null()) { - return *found_constraints; + auto& runtime_prop_constraints = runtime_property_constraints.as_array(); + auto found_constraints = std::find_if(runtime_prop_constraints.begin(), runtime_prop_constraints.end(), [&property_id](const web::json::value& constraints) + { + return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::property_id(constraints)); + }); + + if (runtime_prop_constraints.end() != found_constraints) + { + return *found_constraints; + } } + return value::null(); } - return value::null(); - } - - // get the datatype descriptor of a specific type_name - web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) - { - using web::json::value; - if (!type_name.is_null()) + // get the datatype descriptor of a specific type_name + web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { - return get_control_protocol_datatype_descriptor(type_name.as_string()).descriptor; - } - return value::null(); - } + using web::json::value; - // get the datatype property constraints of a specific type_name - web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) - { - using web::json::value; + if (!type_name.is_null()) + { + return get_control_protocol_datatype_descriptor(type_name.as_string()).descriptor; + } + return value::null(); + } - // NcDatatypeDescriptor - const auto& datatype_descriptor = get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor); - if (!datatype_descriptor.is_null()) + // get the datatype property constraints of a specific type_name + web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { - return nmos::fields::nc::constraints(datatype_descriptor); + using web::json::value; + + // NcDatatypeDescriptor + const auto& datatype_descriptor = get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor); + if (!datatype_descriptor.is_null()) + { + return nmos::fields::nc::constraints(datatype_descriptor); + } + return value::null(); } - return value::null(); - } - // constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - void constraints_validation(const web::json::value& data, const web::json::value& constraints) - { - auto parameter_constraints_validation = [&constraints](const web::json::value& value) + // constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + void constraints_validation(const web::json::value& data, const web::json::value& constraints) { - // is numeric constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } + auto parameter_constraints_validation = [&constraints](const web::json::value& value) + { + // is numeric constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - if (!value.is_integer()) { throw control_protocol_exception("value is not an integer"); } + if (!value.is_integer()) { throw control_protocol_exception("value is not an integer"); } - const auto step = nmos::fields::nc::step(constraints).as_double(); - if (step <= 0) { throw control_protocol_exception("step is not a positive integer"); } + const auto step = nmos::fields::nc::step(constraints).as_double(); + if (step <= 0) { throw control_protocol_exception("step is not a positive integer"); } - const auto value_double = value.as_double(); - if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) - { - auto min = nmos::fields::nc::minimum(constraints).as_double(); - if (0 != std::fmod(value_double - min, step)) { throw control_protocol_exception("value is not divisible by step"); } - } - else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) - { - auto max = nmos::fields::nc::maximum(constraints).as_double(); - if (0 != std::fmod(max - value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } - } - else - { - if (0 != std::fmod(value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } - } - } - if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } + const auto value_double = value.as_double(); + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + auto min = nmos::fields::nc::minimum(constraints).as_double(); + if (0 != std::fmod(value_double - min, step)) { throw control_protocol_exception("value is not divisible by step"); } + } + else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + auto max = nmos::fields::nc::maximum(constraints).as_double(); + if (0 != std::fmod(max - value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } + } + else + { + if (0 != std::fmod(value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } + } + } + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { throw control_protocol_exception("value is less than minimum"); } - } - if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } + if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { throw control_protocol_exception("value is less than minimum"); } + } + if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { throw control_protocol_exception("value is greater than maximum"); } - } + if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { throw control_protocol_exception("value is greater than maximum"); } + } - // is string constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } + // is string constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - const size_t max_characters = nmos::fields::nc::max_characters(constraints); - if (!value.is_string() || value.as_string().length() > max_characters) { throw control_protocol_exception("value is longer than maximum characters"); } - } - if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } + const size_t max_characters = nmos::fields::nc::max_characters(constraints); + if (!value.is_string() || value.as_string().length() > max_characters) { throw control_protocol_exception("value is longer than maximum characters"); } + } + if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - if (!value.is_string()) { throw control_protocol_exception("value is not a string"); } - const auto value_string = utility::us2s(value.as_string()); - bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); - if (!bst::regex_match(value_string, pattern)) { throw control_protocol_exception("value dose not match the pattern"); } - } + if (!value.is_string()) { throw control_protocol_exception("value is not a string"); } + const auto value_string = utility::us2s(value.as_string()); + bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); + if (!bst::regex_match(value_string, pattern)) { throw control_protocol_exception("value dose not match the pattern"); } + } - // reaching here, parameter validation successfully - }; + // reaching here, parameter validation successfully + }; - if (data.is_array()) - { - for (const auto& value : data.as_array()) + if (data.is_array()) { - parameter_constraints_validation(value); + for (const auto& value : data.as_array()) + { + parameter_constraints_validation(value); + } + } + else + { + parameter_constraints_validation(data); } } - else - { - parameter_constraints_validation(data); - } - } - // level 0 datatype constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - void datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) - { - auto parameter_constraints_validation = [¶ms](const web::json::value& value_) + // level 0 datatype constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + void datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) { - // no constraints validation required - if (params.datatype_descriptor.is_null()) { return; } + auto parameter_constraints_validation = [¶ms](const web::json::value& value_) + { + // no constraints validation required + if (params.datatype_descriptor.is_null()) { return; } - const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); + const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); - // do NcDatatypeDescriptorPrimitive constraints validation - if (nc_datatype_type::Primitive == datatype_type) - { - // hmm, for the primitive type, it should not have datatype constraints specified via the datatype_descriptor but just in case - const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); - if (datatype_constraints.is_null()) - { - auto primitive_validation = [](const nc_name& name, const web::json::value& value) + // do NcDatatypeDescriptorPrimitive constraints validation + if (nc_datatype_type::Primitive == datatype_type) + { + // hmm, for the primitive type, it should not have datatype constraints specified via the datatype_descriptor but just in case + const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); + if (datatype_constraints.is_null()) { - auto is_int16 = [](int32_t value) + auto primitive_validation = [](const nc_name& name, const web::json::value& value) { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_uint16 = [](uint32_t value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_float32 = [](double value) - { - return value >= (std::numeric_limits::lowest)() - && value <= (std::numeric_limits::max)(); + auto is_int16 = [](int32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_uint16 = [](uint32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_float32 = [](double value) + { + return value >= (std::numeric_limits::lowest)() + && value <= (std::numeric_limits::max)(); + }; + + if (U("NcBoolean") == name) { return value.is_boolean(); } + if (U("NcInt16") == name && value.is_number()) { return is_int16(value.as_number().to_int32()); } + if (U("NcInt32") == name && value.is_number()) { return value.as_number().is_int32(); } + if (U("NcInt64") == name && value.is_number()) { return value.as_number().is_int64(); } + if (U("NcUint16") == name && value.is_number()) { return is_uint16(value.as_number().to_uint32()); } + if (U("NcUint32") == name && value.is_number()) { return value.as_number().is_uint32(); } + if (U("NcUint64") == name && value.is_number()) { return value.as_number().is_uint64(); } + if (U("NcFloat32") == name && value.is_number()) { return is_float32(value.as_number().to_double()); } + if (U("NcFloat64") == name && value.is_number()) { return !value.as_number().is_integral(); } + if (U("NcString") == name) { return value.is_string(); } + + // invalid primitive type + return false; }; - if (U("NcBoolean") == name) { return value.is_boolean(); } - if (U("NcInt16") == name && value.is_number()) { return is_int16(value.as_number().to_int32()); } - if (U("NcInt32") == name && value.is_number()) { return value.as_number().is_int32(); } - if (U("NcInt64") == name && value.is_number()) { return value.as_number().is_int64(); } - if (U("NcUint16") == name && value.is_number()) { return is_uint16(value.as_number().to_uint32()); } - if (U("NcUint32") == name && value.is_number()) { return value.as_number().is_uint32(); } - if (U("NcUint64") == name && value.is_number()) { return value.as_number().is_uint64(); } - if (U("NcFloat32") == name && value.is_number()) { return is_float32(value.as_number().to_double()); } - if (U("NcFloat64") == name && value.is_number()) { return !value.as_number().is_integral(); } - if (U("NcString") == name) { return value.is_string(); } - - // invalid primitive type - return false; - }; - - // do primitive type constraints validation - const auto& name = nmos::fields::nc::name(params.datatype_descriptor); - if (!primitive_validation(name, value_)) + // do primitive type constraints validation + const auto& name = nmos::fields::nc::name(params.datatype_descriptor); + if (!primitive_validation(name, value_)) + { + throw control_protocol_exception("value is not a " + utility::us2s(name) + " type");; + } + } + else + { + constraints_validation(value_, datatype_constraints); + } + + return; + } + + // do NcDatatypeDescriptorTypeDef constraints validation + if (nc_datatype_type::Typedef == datatype_type) { - throw control_protocol_exception("value is not a " + utility::us2s(name) + " type");; + // do the datatype constraints specified via the datatype_descriptor if presented + const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); + if (datatype_constraints.is_null()) + { + // do parent typename constraints validation + const auto& type_name = params.datatype_descriptor.at(nmos::fields::nc::parent_type); // parent type_name + datatype_constraints_validation(value_, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); + } + else + { + constraints_validation(value_, datatype_constraints); + } + + return; } - } - else - { - constraints_validation(value_, datatype_constraints); - } - return; - } + // do NcDatatypeDescriptorEnum constraints validation + if (nc_datatype_type::Enum == datatype_type) + { + const auto& items = nmos::fields::nc::items(params.datatype_descriptor); + if (items.end() == std::find_if(items.begin(), items.end(), [&](const web::json::value& nc_enum_item_descriptor) { return nmos::fields::nc::value(nc_enum_item_descriptor) == value_; })) + { + const auto& name = nmos::fields::nc::name(params.datatype_descriptor); + throw control_protocol_exception("value is not an enum " + utility::us2s(name) + " type"); + } - // do NcDatatypeDescriptorTypeDef constraints validation - if (nc_datatype_type::Typedef == datatype_type) - { - // do the datatype constraints specified via the datatype_descriptor if presented - const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); - if (datatype_constraints.is_null()) - { - // do parent typename constraints validation - const auto& type_name = params.datatype_descriptor.at(nmos::fields::nc::parent_type); // parent type_name - datatype_constraints_validation(value_, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); - } - else - { - constraints_validation(value_, datatype_constraints); - } + return; + } - return; - } + // do NcDatatypeDescriptorStruct constraints validation + if (nc_datatype_type::Struct == datatype_type) + { + const auto& datatype_name = nmos::fields::nc::name(params.datatype_descriptor); + const auto& fields = nmos::fields::nc::fields(params.datatype_descriptor); + // NcFieldDescriptor + for (const web::json::value& nc_field_descriptor : fields) + { + const auto& field_name = nmos::fields::nc::name(nc_field_descriptor); + // is field in strurcture + if (!value_.has_field(field_name)) { throw control_protocol_exception("missing " + utility::us2s(field_name) + " in " + utility::us2s(datatype_name)); } + + // is field nullable + if (!nmos::fields::nc::is_nullable(nc_field_descriptor) && value_.at(field_name).is_null()) { throw control_protocol_exception(utility::us2s(field_name) + " is not nullable"); } + + // if field value is null continue to next field + if (value_.at(field_name).is_null()) continue; + + // is field sequenceable + if (nmos::fields::nc::is_sequence(nc_field_descriptor) != value_.at(field_name).is_array()) { throw control_protocol_exception(utility::us2s(field_name) + " is not sequenceable"); } + + // check constraints of its typeName + const auto& field_type_name = nc_field_descriptor.at(nmos::fields::nc::type_name); + + if (!field_type_name.is_null()) + { + auto value = value_.at(field_name); + + // do typename constraints validation + datatype_constraints_validation(value, { details::get_datatype_descriptor(field_type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); + } + + // check against field constraints if present + const auto& constraints = nmos::fields::nc::constraints(nc_field_descriptor); + if (!constraints.is_null()) + { + // do field constraints validation + const auto& value = value_.at(field_name); + constraints_validation(value, constraints); + } + } + // unsupported datatype_type, no validation is required + return; + } + }; - // do NcDatatypeDescriptorEnum constraints validation - if (nc_datatype_type::Enum == datatype_type) + if (data.is_array()) { - const auto& items = nmos::fields::nc::items(params.datatype_descriptor); - if (items.end() == std::find_if(items.begin(), items.end(), [&](const web::json::value& nc_enum_item_descriptor) { return nmos::fields::nc::value(nc_enum_item_descriptor) == value_; })) + for (const auto& value : data.as_array()) { - const auto& name = nmos::fields::nc::name(params.datatype_descriptor); - throw control_protocol_exception("value is not an enum " + utility::us2s(name) + " type"); + parameter_constraints_validation(value); } - - return; } - - // do NcDatatypeDescriptorStruct constraints validation - if (nc_datatype_type::Struct == datatype_type) + else { - const auto& datatype_name = nmos::fields::nc::name(params.datatype_descriptor); - const auto& fields = nmos::fields::nc::fields(params.datatype_descriptor); - // NcFieldDescriptor - for (const web::json::value& nc_field_descriptor : fields) - { - const auto& field_name = nmos::fields::nc::name(nc_field_descriptor); - // is field in strurcture - if (!value_.has_field(field_name)) { throw control_protocol_exception("missing " + utility::us2s(field_name) + " in " + utility::us2s(datatype_name)); } + parameter_constraints_validation(data); + } + } - // is field nullable - if (!nmos::fields::nc::is_nullable(nc_field_descriptor) && value_.at(field_name).is_null()) { throw control_protocol_exception(utility::us2s(field_name) + " is not nullable"); } + // multiple levels of constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + void constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + { + // do level 2 runtime property constraints validation + if (!runtime_property_constraints.is_null()) { constraints_validation(data, runtime_property_constraints); return; } + + // do level 1 property constraints validation + if (!property_constraints.is_null()) { constraints_validation(data, property_constraints); return; } + + // do level 0 datatype constraints validation + datatype_constraints_validation(data, params); + } - // if field value is null continue to next field - if (value_.at(field_name).is_null()) continue; + // method parameter constraints validation, may throw nmos::control_protocol_exception + void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + { + using web::json::value; - // is field sequenceable - if (nmos::fields::nc::is_sequence(nc_field_descriptor) != value_.at(field_name).is_array()) { throw control_protocol_exception(utility::us2s(field_name) + " is not sequenceable"); } + // do level 1 property constraints & level 0 datatype constraints validation + constraints_validation(data, value::null(), property_constraints, params); + } - // check constraints of its typeName - const auto& field_type_name = nc_field_descriptor.at(nmos::fields::nc::type_name); + web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, web::json::array& role_path_segments) + { + if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(parent_nc_block_resource.data); - if (!field_type_name.is_null()) + const auto role_path_segement = web::json::front(role_path_segments); + role_path_segments.erase(0); + // find the role_path_segment member + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& member) { - auto value = value_.at(field_name); + return role_path_segement.as_string() == nmos::fields::nc::role(member); + }); - // do typename constraints validation - datatype_constraints_validation(value, { details::get_datatype_descriptor(field_type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); + if (members.end() != member_found) + { + if (role_path_segments.size() == 0) + { + // NcBlockMemberDescriptor + return *member_found; } - // check against field constraints if present - const auto& constraints = nmos::fields::nc::constraints(nc_field_descriptor); - if (!constraints.is_null()) + // get the role_path_segement member resource + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) { - // do field constraints validation - const auto& value = value_.at(field_name); - constraints_validation(value, constraints); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(*member_found); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + return get_nc_block_member_descriptor(resources, *found, role_path_segments); + } } } - // unsupported datatype_type, no validation is required - return; - } - }; - - if (data.is_array()) - { - for (const auto& value : data.as_array()) - { - parameter_constraints_validation(value); } + return web::json::value{}; } - else + + web::json::value parse_role_path(const utility::string_t& role_path_) { - parameter_constraints_validation(data); + // tokenize the role_path with the '.' delimiter + std::list role_path_segments; + boost::algorithm::split(role_path_segments, role_path_, [](utility::char_t c) { return '.' == c; }); + + return web::json::value_from_elements(role_path_segments); } } - // multiple levels of constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - void constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + // is the given class_id a NcBlock + bool is_block(const nc_class_id& class_id) { - // do level 2 runtime property constraints validation - if (!runtime_property_constraints.is_null()) { constraints_validation(data, runtime_property_constraints); return; } - - // do level 1 property constraints validation - if (!property_constraints.is_null()) { constraints_validation(data, property_constraints); return; } - - // do level 0 datatype constraints validation - datatype_constraints_validation(data, params); + return details::is_control_class(nc_block_class_id, class_id); } - // method parameter constraints validation, may throw nmos::control_protocol_exception - void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + // is the given class_id a NcWorker + bool is_worker(const nc_class_id& class_id) { - using web::json::value; - - // do level 1 property constraints & level 0 datatype constraints validation - constraints_validation(data, value::null(), property_constraints, params); + return details::is_control_class(nc_worker_class_id, class_id); } - } - // is the given class_id a NcBlock - bool is_nc_block(const nc_class_id& class_id) - { - return details::is_control_class(nc_block_class_id, class_id); - } - - // is the given class_id a NcWorker - bool is_nc_worker(const nc_class_id& class_id) - { - return details::is_control_class(nc_worker_class_id, class_id); - } - - // is the given class_id a NcManager - bool is_nc_manager(const nc_class_id& class_id) - { - return details::is_control_class(nc_manager_class_id, class_id); - } + // is the given class_id a NcManager + bool is_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_manager_class_id, class_id); + } - // is the given class_id a NcDeviceManager - bool is_nc_device_manager(const nc_class_id& class_id) - { - return details::is_control_class(nc_device_manager_class_id, class_id); - } + // is the given class_id a NcDeviceManager + bool is_device_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_device_manager_class_id, class_id); + } - // is the given class_id a NcClassManager - bool is_nc_class_manager(const nc_class_id& class_id) - { - return details::is_control_class(nc_class_manager_class_id, class_id); - } + // is the given class_id a NcClassManager + bool is_class_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_class_manager_class_id, class_id); + } - // construct NcClassId - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) - { - nc_class_id class_id = prefix; - class_id.push_back(authority_key); - class_id.insert(class_id.end(), suffix.begin(), suffix.end()); - return class_id; - } - nc_class_id make_nc_class_id(const nc_class_id& prefix, const std::vector& suffix) - { - return make_nc_class_id(prefix, 0, suffix); - } + // construct NcClassId + nc_class_id make_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) + { + nc_class_id class_id = prefix; + class_id.push_back(authority_key); + class_id.insert(class_id.end(), suffix.begin(), suffix.end()); + return class_id; + } + nc_class_id make_class_id(const nc_class_id& prefix, const std::vector& suffix) + { + return make_class_id(prefix, 0, suffix); + } - // find control class property descriptor (NcPropertyDescriptor) - web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) - { - using web::json::value; + // find control class property descriptor (NcPropertyDescriptor) + web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + using web::json::value; - auto class_id = class_id_; + auto class_id = class_id_; - while (!class_id.empty()) - { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - auto& property_descriptors = control_class.property_descriptors.as_array(); - auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) + while (!class_id.empty()) { - return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); - }); - if (property_descriptors.end() != found) { return *found; } + const auto& control_class = get_control_protocol_class_descriptor(class_id); + const auto& property_descriptors = control_class.property_descriptors.as_array(); + auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) + { + return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); + }); + if (property_descriptors.end() != found) { return *found; } - class_id.pop_back(); - } + class_id.pop_back(); + } - return value::null(); - } + return value::null(); + } - // get block member descriptors - void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors) - { - if (resource.data.has_field(nmos::fields::nc::members)) + // get block member descriptors + void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors) { - const auto& members = nmos::fields::nc::members(resource.data); - - for (const auto& member : members) + if (resource.data.has_field(nmos::fields::nc::members)) { - web::json::push_back(descriptors, member); - } + const auto& members = nmos::fields::nc::members(resource.data); - if (recurse) - { - // get members on all NcBlock(s) for (const auto& member : members) { - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + web::json::push_back(descriptors, member); + } + + if (recurse) + { + // get members on all NcBlock(s) + for (const auto& member : members) { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(member); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { - get_member_descriptors(resources, *found, recurse, descriptors); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + get_member_descriptors(resources, *found, recurse, descriptors); + } } } } } } - } - // find members with given role name or fragment - void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) - { - auto find_members_by_matching_role = [&](const web::json::array& members) + // find members with given role name or fragment + void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) { - using web::json::value; - - auto match = [&](const web::json::value& descriptor) - { - if (match_whole_string) + auto find_members_by_matching_role = [&](const web::json::array& members) { - if (case_sensitive) { return role == nmos::fields::nc::role(descriptor); } - else { return boost::algorithm::to_upper_copy(role) == boost::algorithm::to_upper_copy(nmos::fields::nc::role(descriptor)); } - } - else - { - if (case_sensitive) { return !boost::find_first(nmos::fields::nc::role(descriptor), role).empty(); } - else { return !boost::ifind_first(nmos::fields::nc::role(descriptor), role).empty(); } - } - }; + using web::json::value; - return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); - }; + auto match = [&](const web::json::value& descriptor) + { + if (match_whole_string) + { + if (case_sensitive) { return role == nmos::fields::nc::role(descriptor); } + else { return boost::algorithm::to_upper_copy(role) == boost::algorithm::to_upper_copy(nmos::fields::nc::role(descriptor)); } + } + else + { + if (case_sensitive) { return !boost::find_first(nmos::fields::nc::role(descriptor), role).empty(); } + else { return !boost::ifind_first(nmos::fields::nc::role(descriptor), role).empty(); } + } + }; - if (resource.data.has_field(nmos::fields::nc::members)) - { - const auto& members = nmos::fields::nc::members(resource.data); + return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); + }; - auto members_found = find_members_by_matching_role(members); - for (const auto& member : members_found) + if (resource.data.has_field(nmos::fields::nc::members)) { - web::json::push_back(descriptors, member); - } + const auto& members = nmos::fields::nc::members(resource.data); - if (recurse) - { - // do role match on all NcBlock(s) - for (const auto& member : members) + auto members_found = find_members_by_matching_role(members); + for (const auto& member : members_found) + { + web::json::push_back(descriptors, member); + } + + if (recurse) { - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + // do role match on all NcBlock(s) + for (const auto& member : members) { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(member); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { - find_members_by_role(resources, *found, role, match_whole_string, case_sensitive, recurse, descriptors); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + find_members_by_role(resources, *found, role, match_whole_string, case_sensitive, recurse, descriptors); + } } } } } } - } - // find members with given class id - void find_members_by_class_id(const resources& resources, const nmos::resource& resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) - { - auto find_members_by_matching_class_id = [&](const web::json::array& members) + // find members with given class id + void find_members_by_class_id(const resources& resources, const nmos::resource& resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) { - using web::json::value; - - auto match = [&](const web::json::value& descriptor) - { - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + auto find_members_by_matching_class_id = [&](const web::json::array& members) + { + using web::json::value; - if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } - else { return class_id == class_id_; } - }; + auto match = [&](const web::json::value& descriptor) + { + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); - return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); - }; + if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } + else { return class_id == class_id_; } + }; - if (resource.data.has_field(nmos::fields::nc::members)) - { - auto& members = nmos::fields::nc::members(resource.data); + return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); + }; - auto members_found = find_members_by_matching_class_id(members); - for (const auto& member : members_found) + if (resource.data.has_field(nmos::fields::nc::members)) { - web::json::push_back(descriptors, member); - } + auto& members = nmos::fields::nc::members(resource.data); - if (recurse) - { - // do class_id match on all NcBlock(s) - for (const auto& member : members) + auto members_found = find_members_by_matching_class_id(members); + for (const auto& member : members_found) + { + web::json::push_back(descriptors, member); + } + + if (recurse) { - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + // do class_id match on all NcBlock(s) + for (const auto& member : members) { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(member); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { - find_members_by_class_id(resources, *found, class_id_, include_derived, recurse, descriptors); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + find_members_by_class_id(resources, *found, class_id_, include_derived, recurse, descriptors); + } } } } } } - } - // push a control protocol resource into other control protocol NcBlock resource - void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) - { - // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... + // push a control protocol resource into other control protocol NcBlock resource + void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) + { + // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... - using web::json::value; + using web::json::value; - auto& parent = nc_block_resource.data; - const auto& child = resource.data; + auto& parent = nc_block_resource.data; + const auto& child = resource.data; - if (!is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); + if (!is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); - web::json::push_back(parent[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + web::json::push_back(parent[nmos::fields::nc::members], + nmos::details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); - nc_block_resource.resources.push_back(resource); - } + nc_block_resource.resources.push_back(resource); + } - // modify a control protocol resource, and insert notification event to all subscriptions - bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) - { - // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... + // modify a control protocol resource, and insert notification event to all subscriptions + bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) + { + // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... - auto found = resources.find(id); - if (resources.end() == found || !found->has_data()) return false; + auto found = resources.find(id); + if (resources.end() == found || !found->has_data()) return false; - auto pre = found->data; + auto pre = found->data; - // "If an exception is thrown by some user-provided operation, then the element pointed to by position is erased." - // This seems too surprising, despite the fact that it means that a modification may have been partially completed, - // so capture and rethrow. - // See https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/reference/ord_indices.html#modify - std::exception_ptr modifier_exception; + // "If an exception is thrown by some user-provided operation, then the element pointed to by position is erased." + // This seems too surprising, despite the fact that it means that a modification may have been partially completed, + // so capture and rethrow. + // See https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/reference/ord_indices.html#modify + std::exception_ptr modifier_exception; - auto resource_updated = nmos::strictly_increasing_update(resources); - auto result = resources.modify(found, [&resource_updated, &modifier, &modifier_exception](resource& resource) - { - try + auto resource_updated = nmos::strictly_increasing_update(resources); + auto result = resources.modify(found, [&resource_updated, &modifier, &modifier_exception](resource& resource) + { + try + { + modifier(resource); + } + catch (...) + { + modifier_exception = std::current_exception(); + } + + // set the update timestamp + resource.updated = resource_updated; + }); + + if (result) { - modifier(resource); + auto& modified = *found; + + insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); } - catch (...) + + if (modifier_exception) { - modifier_exception = std::current_exception(); + std::rethrow_exception(modifier_exception); } - // set the update timestamp - resource.updated = resource_updated; - }); + return result; + } - if (result) + // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id + resources::const_iterator find_resource(resources& resources, type type, const id& resource_id) { - auto& modified = *found; - - insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); + return find_resource_if(resources, type, [resource_id](const nmos::resource& resource) + { + auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); + if (!touchpoints.is_null() && touchpoints.is_array()) + { + auto& tps = touchpoints.as_array(); + auto found_tp = std::find_if(tps.begin(), tps.end(), [resource_id](const web::json::value& touchpoint) + { + auto& resource = nmos::fields::nc::resource(touchpoint); + return (resource_id == nmos::fields::nc::id(resource).as_string()); + }); + return (tps.end() != found_tp); + } + return false; + }); } - if (modifier_exception) + // method parameters constraints validation + 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) { - std::rethrow_exception(modifier_exception); + for (const auto& param : nmos::fields::nc::parameters(nc_method_descriptor)) + { + const auto& name = nmos::fields::nc::name(param); + const auto& constraints = nmos::fields::nc::constraints(param); + const auto& type_name = param.at(nmos::fields::nc::type_name); + if (arguments.is_null() || !arguments.has_field(name)) + { + // missing argument parameter + throw control_protocol_exception("missing argument parameter " + utility::us2s(name)); + } + details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::nc::details::get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + } } - return result; - } - - // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id - resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& resource_id) - { - return find_resource_if(resources, type, [resource_id](const nmos::resource& resource) + resources::const_iterator find_resource_by_role_path(const resources& resources, const web::json::array& role_path_) { - auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); - if (!touchpoints.is_null() && touchpoints.is_array()) + auto role_path = role_path_; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) { - auto& tps = touchpoints.as_array(); - auto found_tp = std::find_if(tps.begin(), tps.end(), [resource_id](const web::json::value& touchpoint) + const auto role = nmos::fields::nc::role(resource->data); + + if (role_path.size() && role == web::json::front(role_path).as_string()) { - auto& resource = nmos::fields::nc::resource(touchpoint); - return (resource_id == nmos::fields::nc::id(resource).as_string() - && nmos::ncp_nmos_resource_types::receiver.name == nmos::fields::nc::resource_type(resource)); - }); - return (tps.end() != found_tp); + role_path.erase(0); + + if (role_path.size()) + { + const auto& block_member_descriptor = details::get_nc_block_member_descriptor(resources, *resource, role_path); + if (!block_member_descriptor.is_null()) + { + const auto& oid = nmos::fields::nc::oid(block_member_descriptor); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + return found; + } + } + } + else + { + return resource; + } + } } - return false; - }); - } + return resources.end(); + } - // method parameters constraints validation - 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) - { - for (const auto& param : nmos::fields::nc::parameters(nc_method_descriptor)) + resources::const_iterator find_resource_by_role_path(const resources& resources, const utility::string_t& role_path_) + { + const auto& role_path = details::parse_role_path(role_path_); + + return find_resource_by_role_path(resources, role_path.as_array()); + } + + resources::const_iterator find_touchpoint_resource(const resources& resources, const resource& resource) { - const auto& name = nmos::fields::nc::name(param); - const auto& constraints = nmos::fields::nc::constraints(param); - const auto& type_name = param.at(nmos::fields::nc::type_name); - if (arguments.is_null() || !arguments.has_field(name)) + if (!resource.has_data()) + { + return resources.end(); + } + + const auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); + + if (touchpoints.size() == 0) { - // missing argument parameter - throw control_protocol_exception("missing argument parameter " + utility::us2s(name)); + return resources.end(); } - details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::details::get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + + // Hmmmmm we're only getting the first touchpoint resource. There could be more than one. + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); + return nmos::find_resource(resources, touchpoint_uuid.as_string()); } } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 6d706085..9984c349 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -13,72 +13,86 @@ namespace nmos control_protocol_exception(const std::string& message) : std::runtime_error(message) {} }; - namespace details + namespace nc { - // get the runtime property constraints of a given property_id - web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints_list); + namespace details + { + // get the runtime property constraints of a given property_id + web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints_list); - // get the datatype descriptor of a specific type_name - web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); + // get the datatype descriptor of a specific type_name + web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); - // get the datatype property constraints of a given type_name - web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); + // get the datatype property constraints of a given type_name + web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); - struct datatype_constraints_validation_parameters - { - web::json::value datatype_descriptor; - get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor; - }; - // multiple levels of constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - void constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); - - // method parameter constraints validation, may throw nmos::control_protocol_exception - void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); - } + struct datatype_constraints_validation_parameters + { + web::json::value datatype_descriptor; + get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor; + }; + // multiple levels of constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + void constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); + + // method parameter constraints validation, may throw nmos::control_protocol_exception + void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); + + // convert . delimited string into role path object + web::json::value parse_role_path(const utility::string_t& role_path); + } - // is the given class_id a NcBlock - bool is_nc_block(const nc_class_id& class_id); + // is the given class_id a NcBlock + bool is_block(const nc_class_id& class_id); - // is the given class_id a NcWorker - bool is_nc_worker(const nc_class_id& class_id); + // is the given class_id a NcWorker + bool is_worker(const nc_class_id& class_id); - // is the given class_id a NcManager - bool is_nc_manager(const nc_class_id& class_id); + // is the given class_id a NcManager + bool is_manager(const nc_class_id& class_id); - // is the given class_id a NcDeviceManager - bool is_nc_device_manager(const nc_class_id& class_id); + // is the given class_id a NcDeviceManager + bool is_device_manager(const nc_class_id& class_id); - // is the given class_id a NcClassManager - bool is_nc_class_manager(const nc_class_id& class_id); + // is the given class_id a NcClassManager + bool is_class_manager(const nc_class_id& class_id); - // construct NcClassId - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix); - nc_class_id make_nc_class_id(const nc_class_id& prefix, const std::vector& suffix); // using default authority_key 0 + // construct NcClassId + nc_class_id make_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix); + nc_class_id make_class_id(const nc_class_id& prefix, const std::vector& suffix); // using default authority_key 0 - // find control class property descriptor (NcPropertyDescriptor) - web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor); + // find control class property descriptor (NcPropertyDescriptor) + web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor); - // get block memeber descriptors - void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors); + // get block memeber descriptors + void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors); - // find members with given role name or fragment - void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); + // find members with given role name or fragment + void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); - // find members with given class id - void find_members_by_class_id(const resources& resources, const resource& resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); + // find members with given class id + void find_members_by_class_id(const resources& resources, const resource& resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); - // push control protocol resource into other control protocol NcBlock resource - void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); + // push control protocol resource into other control protocol NcBlock resource + void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); - // modify a control protocol resource, and insert notification event to all subscriptions - bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); + // modify a control protocol resource, and insert notification event to all subscriptions + bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); - // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id - resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); + // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id + resources::const_iterator find_resource(resources& resources, type type, const id& id); - // 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); + // find resource based on role path. + resources::const_iterator find_resource_by_role_path(const resources& resources, const web::json::array& role_path); + + // find resource based on role path. Roles in role path string must be delimited with a '.' + resources::const_iterator find_resource_by_role_path(const resources& resources, const utility::string_t& role_path); + + // 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); + + resources::const_iterator find_touchpoint_resource(const resources& resources, const resource& resource); + } } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index cf1f35a0..71257a08 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -268,7 +268,7 @@ namespace nmos try { // do method arguments constraints validation - method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); + nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); // execute the relevant control method handler, then accumulating up their response to reponses // wrap the NcMethodResuls here diff --git a/Development/nmos/is14_schemas/is14_schemas.h b/Development/nmos/is14_schemas/is14_schemas.h new file mode 100644 index 00000000..7a261fc0 --- /dev/null +++ b/Development/nmos/is14_schemas/is14_schemas.h @@ -0,0 +1,35 @@ +#ifndef NMOS_IS14_SCHEMAS_H +#define NMOS_IS14_SCHEMAS_H + +// Extern declarations for auto-generated constants +// could be auto-generated, but isn't currently! +namespace nmos +{ + namespace is14_schemas + { + namespace v1_0_x + { + extern const char* base; + extern const char* bulkProperties_get_response; + extern const char* bulkProperties_set_request; + extern const char* bulkProperties_set_response; + extern const char* bulkProperties_validate_request; + extern const char* bulkProperties_validate_response; + extern const char* descriptor_get; + extern const char* method_patch_request; + extern const char* method_patch_response; + extern const char* methods_base; + extern const char* ms05_error; + extern const char* properties_base; + extern const char* property; + extern const char* property_descriptor; + extern const char* property_value_get; + extern const char* property_value_put_request; + extern const char* property_value_put_response; + extern const char* rolePath; + extern const char* rolePaths_base; + } + } +} + +#endif diff --git a/Development/nmos/is14_versions.h b/Development/nmos/is14_versions.h new file mode 100644 index 00000000..a5fc9d2c --- /dev/null +++ b/Development/nmos/is14_versions.h @@ -0,0 +1,26 @@ +#ifndef NMOS_IS14_VERSIONS_H +#define NMOS_IS14_VERSIONS_H + +#include +#include +#include "nmos/api_version.h" +#include "nmos/settings.h" + +namespace nmos +{ + namespace is14_versions + { + const api_version v1_0{ 1, 0 }; + + const std::set all{ nmos::is14_versions::v1_0 }; + + inline std::set from_settings(const nmos::settings& settings) + { + return settings.has_field(nmos::fields::is14_versions) + ? boost::copy_range>(nmos::fields::is14_versions(settings) | boost::adaptors::transformed([](const web::json::value& v) { return nmos::parse_api_version(v.as_string()); })) + : nmos::is14_versions::all; + } + } +} + +#endif diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 7374c5a9..b4d3b78d 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -336,6 +336,15 @@ namespace nmos const web::json::field_as_string payload_status_message{ U("payloadStatusMessage") }; const web::json::field_as_bool signal_protection_status{ U("signalProtectionStatus") }; const web::json::field_as_bool active{ U("active") }; + const web::json::field_as_array values{ U("values") }; + const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; + const web::json::field_as_value status_message{ U("statusMessage") }; + const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkValuesHolder + const web::json::field_as_bool is_rebuildable{ U("isRebuildable") }; + const web::json::field_as_integer notice_type{ U("noticeType") }; + const web::json::field_as_string notice_message{ U("noticeMessage") }; + const web::json::field_as_array notices{ U("notices") }; + const web::json::field_as_integer restore_mode{ U("restoreMode") }; } // NMOS Parameter Registers diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index d24f2a5d..81bff63b 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -12,6 +12,8 @@ #include "nmos/is10_schemas/is10_schemas.h" #include "nmos/is12_versions.h" #include "nmos/is12_schemas/is12_schemas.h" +#include "nmos/is14_versions.h" +#include "nmos/is14_schemas/is14_schemas.h" #include "nmos/type.h" namespace nmos @@ -170,6 +172,26 @@ namespace nmos const web::uri controlprotocolapi_subscription_message_schema_uri = make_schema_uri(tag, _XPLATSTR("subscription-message.json")); } } + + namespace is14_schemas + { + web::uri make_schema_uri(const utility::string_t& tag, const utility::string_t& ref = {}) + { + return{ _XPLATSTR("https://github.com/AMWA-TV/is-14/raw/") + tag + _XPLATSTR("/APIs/schemas/") + ref }; + } + + // See https://github.com/AMWA-TV/is-14/tree/v1.0-dev/APIs/schemas/ + namespace v1_0 + { + using namespace nmos::is14_schemas::v1_0_x; + const utility::string_t tag(_XPLATSTR("v1.0.x")); + + const web::uri configurationapi_bulkProperties_set_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-set-request.json")); + const web::uri configurationapi_bulkProperties_validate_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-validate-request.json")); + const web::uri configurationapi_method_patch_request_schema_uri = make_schema_uri(tag, _XPLATSTR("method-patch-request.json")); + const web::uri configurationapi_property_value_put_request_schema_uri = make_schema_uri(tag, _XPLATSTR("property-value-put-request.json")); + } + } } namespace nmos @@ -391,6 +413,20 @@ namespace nmos }; } + static std::map make_is14_schemas() + { + using namespace nmos::is14_schemas; + + return + { + // v1.0 + { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-set-request.json")), make_schema(v1_0::bulkProperties_set_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-validate-request.json")), make_schema(v1_0::bulkProperties_validate_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("method-patch-request.json")), make_schema(v1_0::method_patch_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("property-value-put-request.json")), make_schema(v1_0::property_value_put_request) } + }; + } + inline void merge(std::map& to, std::map&& from) { to.insert(from.begin(), from.end()); // std::map::merge in C++17 @@ -404,6 +440,7 @@ namespace nmos merge(result, make_is09_schemas()); merge(result, make_is10_schemas()); merge(result, make_is12_schemas()); + merge(result, make_is14_schemas()); return result; } @@ -510,6 +547,26 @@ namespace nmos return is12_schemas::v1_0::controlprotocolapi_subscription_message_schema_uri; } + web::uri make_configurationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configurationapi_bulkProperties_set_request_schema_uri; + } + + web::uri make_configurationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configurationapi_bulkProperties_validate_request_schema_uri; + } + + web::uri make_configurationapi_method_patch_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configurationapi_method_patch_request_schema_uri; + } + + web::uri make_configurationapi_property_value_put_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configurationapi_property_value_put_request_schema_uri; + } + // load the json schema for the specified base URI web::json::value load_json_schema(const web::uri& id) { diff --git a/Development/nmos/json_schema.h b/Development/nmos/json_schema.h index 57cb0996..d1a06be6 100644 --- a/Development/nmos/json_schema.h +++ b/Development/nmos/json_schema.h @@ -40,6 +40,11 @@ namespace nmos web::uri make_controlprotocolapi_command_message_schema_uri(const nmos::api_version& version); web::uri make_controlprotocolapi_subscription_message_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_method_patch_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_property_value_put_request_schema_uri(const nmos::api_version& version); + // load the json schema for the specified base URI web::json::value load_json_schema(const web::uri& id); } diff --git a/Development/nmos/node_resources.cpp b/Development/nmos/node_resources.cpp index 534b6b2b..a97bffc8 100644 --- a/Development/nmos/node_resources.cpp +++ b/Development/nmos/node_resources.cpp @@ -17,6 +17,7 @@ #include "nmos/is07_versions.h" #include "nmos/is08_versions.h" #include "nmos/is12_versions.h" +#include "nmos/is14_versions.h" #include "nmos/media_type.h" #include "nmos/resource.h" #include "nmos/sdp_utils.h" // for nmos::make_components @@ -152,6 +153,27 @@ namespace nmos } } + if (0 <= nmos::fields::configuration_port(settings)) + { + for (const auto& version : nmos::is14_versions::from_settings(settings)) + { + auto configuration_uri = web::uri_builder() + .set_scheme(nmos::http_scheme(settings)) + .set_port(nmos::fields::connection_port(settings)) + .set_path(U("/x-nmos/configuration/") + make_api_version(version)); + auto type = U("urn:x-nmos:control:configuration/") + make_api_version(version); + + for (const auto& host : hosts) + { + web::json::push_back(data[U("controls")], value_of({ + { U("href"), configuration_uri.set_host(host).to_uri().to_string() }, + { U("type"), type }, + { U("authorization"), nmos::experimental::fields::server_authorization(settings) } + })); + } + } + } + return{ is04_versions::v1_3, types::device, std::move(data), false }; } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 00258389..eba159d3 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -3,6 +3,7 @@ #include "cpprest/ws_utils.h" #include "nmos/api_utils.h" #include "nmos/channelmapping_activation.h" +#include "nmos/configuration_api.h" #include "nmos/control_protocol_ws_api.h" #include "nmos/events_api.h" #include "nmos/events_ws_api.h" @@ -74,6 +75,10 @@ namespace nmos node_server.api_routers[{ {}, nmos::fields::channelmapping_port(node_model.settings) }].mount({}, nmos::make_channelmapping_api(node_model, node_implementation.validate_map, validate_authorization ? validate_authorization(nmos::experimental::scopes::channelmapping) : nullptr, gate)); + // Configure the Configuration API + + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.filter_property_value_holders, node_implementation.modify_rebuildable_block, node_implementation.control_protocol_property_changed, gate)); + const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; events_ws_api.first = nmos::make_events_ws_api(node_model, events_ws_api.second, node_implementation.ws_validate_authorization, gate); diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 25a15d4b..b4e7e910 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -7,6 +7,7 @@ #include "nmos/channelmapping_activation.h" #include "nmos/connection_api.h" #include "nmos/connection_activation.h" +#include "nmos/configuration_handlers.h" #include "nmos/control_protocol_handlers.h" #include "nmos/node_behaviour.h" #include "nmos/node_system_behaviour.h" @@ -27,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -50,6 +51,8 @@ namespace nmos , get_control_protocol_datatype_descriptor(std::move(get_control_protocol_datatype_descriptor)) , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) + , filter_property_value_holders(std::move(filter_property_value_holders)) + , modify_rebuildable_block(std::move(modify_rebuildable_block)) {} // use the default constructor and chaining member functions for fluent initialization @@ -82,6 +85,8 @@ namespace nmos node_implementation& on_get_control_datatype_descriptor(nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { this->get_control_protocol_datatype_descriptor = std::move(get_control_protocol_datatype_descriptor); return *this; } node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } + node_implementation& on_filter_property_value_holders(nmos::filter_property_value_holders_handler filter_property_value_holders) { this->filter_property_value_holders = std::move(filter_property_value_holders); return *this; } + node_implementation& on_modify_rebuildable_block(nmos::modify_rebuildable_block_handler modify_rebuildable_block) { this->modify_rebuildable_block = std::move(modify_rebuildable_block); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -124,6 +129,10 @@ namespace nmos nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor; nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor; nmos::control_protocol_property_changed_handler control_protocol_property_changed; + + // Device Configuration handlers + nmos::filter_property_value_holders_handler filter_property_value_holders; + nmos::modify_rebuildable_block_handler modify_rebuildable_block; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API diff --git a/Development/nmos/scope.h b/Development/nmos/scope.h index 25d65004..ee623e55 100644 --- a/Development/nmos/scope.h +++ b/Development/nmos/scope.h @@ -26,6 +26,8 @@ namespace nmos const scope channelmapping{ U("channelmapping") }; // IS-12 const scope ncp{ U("ncp") }; + // IS-14 + const scope configuration{ U("configuration") }; } inline utility::string_t make_scope(const scope& scope) @@ -43,6 +45,7 @@ namespace nmos if (scopes::events.name == scope) { return scopes::events; } if (scopes::channelmapping.name == scope) { return scopes::channelmapping; } if (scopes::ncp.name == scope) { return scopes::ncp; } + if (scopes::configuration.name == scope) { return scopes::configuration; } return{}; } } diff --git a/Development/nmos/settings.cpp b/Development/nmos/settings.cpp index 5608fbca..94251cb4 100644 --- a/Development/nmos/settings.cpp +++ b/Development/nmos/settings.cpp @@ -86,6 +86,7 @@ namespace nmos web::json::insert(settings, std::make_pair(nmos::experimental::fields::authorization_redirect_port, http_port)); web::json::insert(settings, std::make_pair(nmos::experimental::fields::jwks_uri_port, http_port)); if (!registry) web::json::insert(settings, std::make_pair(nmos::fields::control_protocol_ws_port, ncp_ws_port)); + if (!registry) web::json::insert(settings, std::make_pair(nmos::fields::configuration_port, http_port)); } } } diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index c0981f8b..4f1a0932 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -107,6 +107,9 @@ namespace nmos // is12_versions [node]: used to specify the enabled API versions for a version-locked configuration const web::json::field_as_array is12_versions{ U("is12_versions") }; // when omitted, nmos::is12_versions::all is used + // is14_versions [node]: used to specify the enabled API versions for a version-locked configuration + const web::json::field_as_array is14_versions{ U("is14_versions") }; // when omitted, nmos::is14_versions::all is used + // pri [registry, node]: used for the 'pri' TXT record; specifying nmos::service_priorities::no_priority (maximum value) disables advertisement completely const web::json::field_as_integer_or pri{ U("pri"), 100 }; // default to highest_development_priority @@ -151,6 +154,7 @@ namespace nmos const web::json::field_as_integer_or system_port{ U("system_port"), 10641 }; // control_protocol_ws_port [node]: used to construct request URLs for the Control Protocol websocket, or negative to disable the control protocol features const web::json::field_as_integer_or control_protocol_ws_port{ U("control_protocol_ws_port"), 3218 }; + const web::json::field_as_integer_or configuration_port{ U("configuration_port"), 3219 }; // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) const web::json::field_as_integer_or listen_backlog{ U("listen_backlog"), 0 }; diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp new file mode 100644 index 00000000..b9c8a4b6 --- /dev/null +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -0,0 +1,93 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_resources.h" +#include "nmos/configuration_methods.h" +#include "nmos/configuration_utils.h" + +#include "bst/test/test.h" + + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetPropertiesByPath) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor = nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + 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")); + nmos::make_rebuildable(receivers); + // 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::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + // make monitor1 rebuildable + nmos::make_rebuildable(monitor1); + + auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + { + const auto target_role_path = value_of({ U("root") }); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + + BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); + + const auto& bulk_values_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + + BST_REQUIRE_EQUAL(5, object_properties_holders.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers") }); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + + BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); + + const auto& bulk_values_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + + BST_REQUIRE_EQUAL(3, object_properties_holders.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + + BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); + + const auto& bulk_values_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + + BST_REQUIRE_EQUAL(1, object_properties_holders.size()); + } +} diff --git a/Development/nmos/test/configuration_resources_test.cpp b/Development/nmos/test/configuration_resources_test.cpp new file mode 100644 index 00000000..b1950b20 --- /dev/null +++ b/Development/nmos/test/configuration_resources_test.cpp @@ -0,0 +1,70 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_typedefs.h" +#include "nmos/configuration_resources.h" + +#include "bst/test/test.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testMakeObjectPropertiesSetValidation) +{ + using web::json::value_of; + using web::json::value; + + auto role_path = web::json::value_of({ U("root"), U("path1") }).as_array(); + auto status = nmos::nc_restore_validation_status::ok; + auto notices = value::array().as_array(); + auto status_message = U("status message"); + + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status, notices, status_message); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(notices, nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(value::string(status_message), nmos::fields::nc::status_message(object_properties_set_validation)); + } + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status, notices); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(notices, nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::null(), nmos::fields::nc::status_message(object_properties_set_validation)); + } + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::array().as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::null(), nmos::fields::nc::status_message(object_properties_set_validation)); + } + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status, status_message); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::array().as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(value::string(status_message), nmos::fields::nc::status_message(object_properties_set_validation)); + } +} diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp new file mode 100644 index 00000000..5bd4b81f --- /dev/null +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -0,0 +1,995 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_resources.h" +#include "nmos/configuration_utils.h" + +#include "bst/test/test.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testIsRolePathRoot) +{ + { + auto role_path = web::json::value_of({ U("root"), U("path1") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1") }).as_array(); + + BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); + } + { + auto role_path = web::json::value_of({ U("root"), U("path1"), U("path2"), U("path3")}).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); + + BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); + } + { + auto role_path = web::json::value_of({ U("root"), U("path1") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); + + BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); + } + { + auto role_path = web::json::value_of({ U("root"), U("path3"), U("path4") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); + + BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); + } + { + auto role_path = web::json::value_of({ U("path3"), U("path4") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); + + BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testIsBlockModified) +{ + using web::json::value_of; + using web::json::value; + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + auto oid = nmos::root_block_oid; + // root, receivers + auto receivers = nmos::make_block(++oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + auto receiver_block_oid = oid; + // 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::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor_1_oid = oid; + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor_2_oid = oid; + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + + // Create Object Properties Holder + auto role_path = value::array(); + push_back(role_path, U("root")); + push_back(role_path, U("receivers")); + + // Members unchanged + { + auto property_value_holders = value::array(); + + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + + BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed number of members + { + auto property_value_holders = value::array(); + + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto block_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); + push_back(members, block_descriptor); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed oids + { + auto property_value_holders = value::array(); + + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed roles + { + auto property_value_holders = value::array(); + + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed class id + { + auto property_value_holders = value::array(); + + auto members = value::array(); + const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed owner oid + { + auto property_value_holders = value::array(); + + auto members = value::array(); + const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); + push_back(members, block_member_descriptor); + } + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); + push_back(members, block_member_descriptor); + } + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed constant oid + { + auto property_value_holders = value::array(); + + auto members = value::array(); + const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetObjectPropertiesHolder) +{ + using web::json::value_of; + using web::json::value; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(1, object_property_holder.size()); + BST_CHECK_EQUAL(target_role_path.as_array(), nmos::fields::nc::path(*object_property_holder.begin())); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(1, object_property_holder.size()); + BST_CHECK_EQUAL(target_role_path.as_array(), nmos::fields::nc::path(*object_property_holder.begin())); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(0, object_property_holder.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("does_not_exist") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(0, object_property_holder.size()); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetChildObjectPropertiesHolders) +{ + using web::json::value_of; + using web::json::value; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + + { + const auto target_role_path = value_of({ U("root"), U("receivers") }); + + const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); + + BST_REQUIRE_EQUAL(2, child_object_properties_holders.size()); + + const auto& object_properties_holder1 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon1") }).as_array()); + BST_CHECK_EQUAL(1, object_properties_holder1.size()); + + const auto& object_properties_holder2 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon2") }).as_array()); + BST_CHECK_EQUAL(1, object_properties_holder2.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1")}); + + const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); + + BST_REQUIRE_EQUAL(1, child_object_properties_holders.size()); + + const auto& object_properties_holder1 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon1") }).as_array()); + BST_CHECK_EQUAL(1, object_properties_holder1.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("does_not_exist") }); + + const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); + + BST_REQUIRE_EQUAL(0, child_object_properties_holders.size()); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetRolePath) +{ + // Create Fake Device Model + using web::json::value_of; + using web::json::value; + + nmos::experimental::control_protocol_state control_protocol_state; + nmos::resources resources; + + // root + 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); + nmos::nc_oid 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")); + nmos::make_rebuildable(receivers); + // 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::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + auto expected_role_paths = value::array(); + push_back(expected_role_paths, value_of({ U("root") })); + push_back(expected_role_paths, value_of({ U("root"), U("ClassManager")})); + push_back(expected_role_paths, value_of({ U("root"), U("receivers") })); + push_back(expected_role_paths, value_of({ U("root"), U("receivers"), U("mon1") })); + push_back(expected_role_paths, value_of({ U("root"), U("receivers"), U("mon2") })); + + for (const auto& expected_role_path : expected_role_paths.as_array()) + { + const auto& resource = nmos::nc::find_resource_by_role_path(resources, expected_role_path.as_array()); + const auto actual_role_path = nmos::get_role_path(resources, *resource); + BST_CHECK_EQUAL(expected_role_path.as_array(), actual_role_path); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testApplyBackupDataSet) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + 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")); + nmos::make_rebuildable(receivers); + // 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::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + // make monitor1 rebuildable + nmos::make_rebuildable(monitor1); + + auto monitor_1_oid = oid; + auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + bool filter_property_value_holders_called = false; + bool modify_rebuildable_block_called = false; + + // callback stubs + nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + filter_property_value_holders_called = true; + auto modifiable_property_value_holders = value::array(); + + for (const auto& property_value : property_values) + { + web::json::push_back(modifiable_property_value_holders, property_value); + } + return modifiable_property_value_holders.as_array(); + }; + nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + modify_rebuildable_block_called = true; + auto out = value::array(); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, U("OK")); + web::json::push_back(out, object_properties_set_validation); + return out; + }; + + { + // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode + // + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + const auto target_role_path = value_of({ U("root"), U("receivers")}); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + auto object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + + // not expecting callbacks to be invoked as no read only properties, or rebuildable blocks modified + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Check filter_property_value_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); + // This is a read only property + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + // must be a more efficient way of initializing these role paths + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + bool validate = true; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + const auto object_properties_set_validation = output.as_array().at(0); + + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Check error generated when attempting to change a read only property of non-rebuidable object in Rebuild mode + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); + // This is a read only property + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + // must be a more efficient way of initializing these role paths + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + bool validate = true; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + const auto object_properties_set_validation = output.as_array().at(0); + + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // expectation a single notice for the read only property that couldn't be changed + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto& notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Check an error is caused by trying to modify a read only property in Modify mode + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Change a read only property in Rebuild mode + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); + // This is a read only property + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), true, value("change this value"))); + // This is a writable property + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false)); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + // must be a more efficient way of initializing these role paths + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; + bool validate = true; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + const auto object_properties_set_validation = output.as_array().at(0); + + // expect overall status for object to be OK as although the read only property change should fail + // the writable property should succeed + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // expectation a single notice for the read only property that couldn't be changed + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto& notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Check modify_rebuildable_block_handler is called when trying to modify a rebuildable block + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers")}); + auto property_value_holders = value::array(); + auto members = value::array(); + + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + const auto object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(modify_rebuildable_block_called); + } + { + // Check that role paths outside of the scope of the target role path are errored + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("other_receivers") }); + auto property_value_holders = value::array(); + auto members = value::array(); + + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + const auto object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::not_found, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Mixture of filter_property_value_holders_handler and errors in Rebuild mode + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); + // This is a read only property + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value"))); //read only + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcString"), false, false)); // error in data type + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + // must be a more efficient way of initializing these role paths + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + bool validate = true; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + const auto object_properties_set_validation = output.as_array().at(0); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(2, 1), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("enabled"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Incorrect property name in property value holders + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + // This is a read only property + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("wrong_property_name"), U("NcString"), false, value("change this value"))); //read only + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + // must be a more efficient way of initializing these role paths + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + bool validate = true; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + const auto object_properties_set_validation = output.as_array().at(0); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("wrong_property_name"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Incorrect property type in property value holders + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + // This is a read only property + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("wrong_data_type"), false, value("change this value"))); //read only + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + // must be a more efficient way of initializing these role paths + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + bool validate = true; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + const auto object_properties_set_validation = output.as_array().at(0); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + // ensure an error if trying to invoke rebuildable block when in Modify mode +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + 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); + const 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")); + nmos::make_rebuildable(receivers); + // 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::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::make_rebuildable(monitor1); + auto monitor_1_oid = oid; + const auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + // undefined callback stubs + nmos::filter_property_value_holders_handler filter_property_value_holders; + nmos::modify_rebuildable_block_handler modify_rebuildable_block; + + { + // Check that Modify mode is unaffected by undefined Rebuild mode callbacks + // + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + const auto object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + { + // Check that Rebuild mode is unaffected by undefined Rebuild mode callbacks when no objects are being rebuilt + // + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + const auto object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + { + // Check undefined filter_property_value_holders_handler causes an unsupported mode error when attempting to modify a read only property in Rebuild mode + // + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); + // This is a read only property + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + // must be a more efficient way of initializing these role paths + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + bool validate = true; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + const auto object_properties_set_validation = output.as_array().at(0); + + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } + { + // Check undefined modify_rebuildable_block_handler causes an unsupported error when attempting to modify a rebuildable block + // + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + auto property_value_holders = value::array(); + auto members = value::array(); + + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + const auto object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } +} \ No newline at end of file diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 408e4fd5..bf80ad6e 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -630,42 +630,42 @@ BST_TEST_CASE(testNcDatatypeDescriptorPrimitive) BST_TEST_CASE(testNcClassId) { - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1, 2 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1, 2, 0 })); - BST_REQUIRE(nmos::is_nc_block(nmos::nc_block_class_id)); - BST_REQUIRE(nmos::is_nc_block(nmos::make_nc_class_id(nmos::nc_block_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1, 1, 1 })); - BST_REQUIRE(nmos::is_nc_worker(nmos::nc_worker_class_id)); - BST_REQUIRE(nmos::is_nc_worker(nmos::make_nc_class_id(nmos::nc_worker_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1, 1, 1 })); - BST_REQUIRE(nmos::is_nc_manager(nmos::nc_manager_class_id)); - BST_REQUIRE(nmos::is_nc_manager(nmos::make_nc_class_id(nmos::nc_manager_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 3, 2 })); - BST_REQUIRE(nmos::is_nc_device_manager(nmos::nc_device_manager_class_id)); - BST_REQUIRE(nmos::is_nc_device_manager(nmos::make_nc_class_id(nmos::nc_device_manager_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 3, 1 })); - BST_REQUIRE(nmos::is_nc_class_manager(nmos::nc_class_manager_class_id)); - BST_REQUIRE(nmos::is_nc_class_manager(nmos::make_nc_class_id(nmos::nc_class_manager_class_id, { 1 }))); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ 1, 2 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ 1, 2, 0 })); + BST_REQUIRE(nmos::nc::is_block(nmos::nc_block_class_id)); + BST_REQUIRE(nmos::nc::is_block(nmos::nc::make_class_id(nmos::nc_block_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ 1, 1, 1 })); + BST_REQUIRE(nmos::nc::is_worker(nmos::nc_worker_class_id)); + BST_REQUIRE(nmos::nc::is_worker(nmos::nc::make_class_id(nmos::nc_worker_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ 1, 1, 1 })); + BST_REQUIRE(nmos::nc::is_manager(nmos::nc_manager_class_id)); + BST_REQUIRE(nmos::nc::is_manager(nmos::nc::make_class_id(nmos::nc_manager_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1, 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1, 3, 2 })); + BST_REQUIRE(nmos::nc::is_device_manager(nmos::nc_device_manager_class_id)); + BST_REQUIRE(nmos::nc::is_device_manager(nmos::nc::make_class_id(nmos::nc_device_manager_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1, 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1, 3, 1 })); + BST_REQUIRE(nmos::nc::is_class_manager(nmos::nc_class_manager_class_id)); + BST_REQUIRE(nmos::nc::is_class_manager(nmos::nc::make_class_id(nmos::nc_class_manager_class_id, { 1 }))); } BST_TEST_CASE(testFindProperty) @@ -676,27 +676,27 @@ BST_TEST_CASE(testFindProperty) const auto invalid_property_id = nmos::nc_property_id(1000, 1000); const auto invalid_class_id = nmos::nc_class_id({ 1000, 1000 }); - nmos::experimental::control_protocol_state control_protocol_state(nullptr); + 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); { // valid - find members property in NcBlock - auto property = nmos::find_property_descriptor(nc_block_members_property_id, nc_block_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(nc_block_members_property_id, nc_block_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(!property.is_null()); } { // invalid - find members property in NcWorker - auto property = nmos::find_property_descriptor(nc_block_members_property_id, nc_worker_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(nc_block_members_property_id, nc_worker_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(property.is_null()); } { // invalid - find unknown propertry in NcBlock - auto property = nmos::find_property_descriptor(invalid_property_id, nc_block_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(invalid_property_id, nc_block_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(property.is_null()); } { // invalid - find unknown property in unknown class - auto property = nmos::find_property_descriptor(invalid_property_id, invalid_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(invalid_property_id, invalid_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(property.is_null()); } } @@ -783,7 +783,7 @@ BST_TEST_CASE(testConstraints) const auto struct_datatype = nmos::details::make_nc_datatype_descriptor_struct(U("struct datatype"), U("structDatatype"), fields, value::null()); // no datatype constraints for struct datatype // setup datatypes in control_protocol_state - nmos::experimental::control_protocol_state control_protocol_state(nullptr); + nmos::experimental::control_protocol_state control_protocol_state; control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_int16_datatype }); control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_int32_datatype }); control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_int64_datatype }); @@ -799,130 +799,130 @@ BST_TEST_CASE(testConstraints) control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_string_seq_datatype }); // test get_runtime_property_constraints - BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_string_id, runtime_property_constraints), runtime_property_string_constraints); - BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_int32_id, runtime_property_constraints), runtime_property_int32_constraints); - BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(unknown_property_id, runtime_property_constraints), value::null()); + BST_REQUIRE_EQUAL(nmos::nc::details::get_runtime_property_constraints(property_string_id, runtime_property_constraints), runtime_property_string_constraints); + BST_REQUIRE_EQUAL(nmos::nc::details::get_runtime_property_constraints(property_int32_id, runtime_property_constraints), runtime_property_int32_constraints); + BST_REQUIRE_EQUAL(nmos::nc::details::get_runtime_property_constraints(unknown_property_id, runtime_property_constraints), value::null()); // string property constraints validation // runtime property constraints validation - const nmos::details::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // property constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // datatype constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - const nmos::details::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); // number property constraints validation // runtime property constraints validation - const nmos::details::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // property constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // datatype constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // int16 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); // int32 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); // int64 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); // uint16 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); // uint32 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); // uint64 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); // float32 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(0.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(-1000.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(0.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(-1000.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); // float64 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); // enum property datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), nmos::control_protocol_exception); // invalid data vs primitive datatype constraints - const nmos::details::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); - const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // struct property datatype constraints validation const auto good_struct1 = value_of({ @@ -1833,29 +1833,29 @@ BST_TEST_CASE(testConstraints) { U("sequenceStructPropertyNullable"), value::null() } }); - const nmos::details::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(good_struct1, value::null(), value::null(), struct_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(good_struct2, value::null(), value::null(), struct_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(good_struct1, value::null(), value::null(), struct_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(good_struct2, value::null(), value::null(), struct_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); } diff --git a/Development/nmos/test/control_protocol_utils_test.cpp b/Development/nmos/test/control_protocol_utils_test.cpp new file mode 100644 index 00000000..bccda1ec --- /dev/null +++ b/Development/nmos/test/control_protocol_utils_test.cpp @@ -0,0 +1,70 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_utils.h" + +#include "nmos/is04_versions.h" + +#include "bst/test/test.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testFindTouchpointResources) +{ + using web::json::value_of; + using web::json::value; + + const auto touchpoint1_id = U("1000"); + auto touchpoint1_data = value_of({ + { nmos::fields::id, touchpoint1_id }, + { nmos::fields::version, nmos::make_version() }, + { nmos::fields::label, U("touchpoint1") }, + { nmos::fields::description, U("touchpoint1") }, + { nmos::fields::tags, value::null() } + }); + nmos::resource touchpoint1 = { nmos::is04_versions::v1_3, nmos::types::node, std::move(touchpoint1_data), false }; + + const auto touchpoint2_id = U("1001"); + auto touchpoint2_data = value_of({ + { nmos::fields::id, touchpoint2_id }, + { nmos::fields::version, nmos::make_version() }, + { nmos::fields::label, U("touchpoint2") }, + { nmos::fields::description, U("touchpoint2") }, + { nmos::fields::tags, value::null() } + }); + nmos::resource touchpoint2 = { nmos::is04_versions::v1_3, nmos::types::node, std::move(touchpoint2_data), false }; + + const auto non_existant_id = U("1002"); + + // Create Device Model + auto oid = nmos::root_block_oid; + auto monitor1 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint1_id})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint2_id})} })); + auto monitor3 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon3"), U("monitor 3"), U("monitor 3"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, non_existant_id})} })); + + nmos::resources resources; + // Insert dummy NMOS resources + insert_resource(resources, std::move(touchpoint1)); + insert_resource(resources, std::move(touchpoint2)); + + { + const auto& touchpoint = nmos::nc::find_touchpoint_resource(resources, monitor1); + + BST_CHECK_EQUAL(touchpoint1_id, nmos::fields::id(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint1"), nmos::fields::label(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint1"), nmos::fields::description(touchpoint->data)); + } + { + const auto& touchpoint = nmos::nc::find_touchpoint_resource(resources, monitor2); + + BST_CHECK_EQUAL(touchpoint2_id, nmos::fields::id(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint2"), nmos::fields::label(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint2"), nmos::fields::description(touchpoint->data)); + } + { + const auto& touchpoint = nmos::nc::find_touchpoint_resource(resources, monitor3); + + BST_CHECK_EQUAL(touchpoint, resources.end()); + } +} diff --git a/Development/nmos/type.h b/Development/nmos/type.h index 8da37f68..eef5ca39 100644 --- a/Development/nmos/type.h +++ b/Development/nmos/type.h @@ -49,6 +49,7 @@ namespace nmos const type nc_receiver_monitor{ U("nc_receiver_monitor") }; const type nc_receiver_monitor_protected{ U("nc_receiver_monitor_protected") }; const type nc_ident_beacon{ U("nc_ident_beacon") }; + const type nc_bulk_properties_manager{ U("nc_bulk_properties_manager") }; } } diff --git a/Development/third_party/is-14/README.md b/Development/third_party/is-14/README.md new file mode 100644 index 00000000..a6d9c931 --- /dev/null +++ b/Development/third_party/is-14/README.md @@ -0,0 +1,9 @@ +# AMWA IS-14 NMOS Device Configuration Specification + +This directory contains files from the [AMWA NMOS Device Configuration Specification](https://github.com/AMWA-TV/is-14), in particular tagged versions of the JSON schemas used by the API specifications. + +Original source code: + +- (c) AMWA 2024 +- Licensed under the Apache License, Version 2.0; http://www.apache.org/licenses/LICENSE-2.0 + diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/base.json new file mode 100644 index 00000000..d8c791be --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/base.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API base resource", + "title": "Configuration API base resource", + "items": { + "type": "string", + "enum": [ + "rolePaths/" + ] + }, + "minItems": 1, + "maxItems": 1, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json new file mode 100644 index 00000000..9ddc05ea --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Returns a NcMethodResultBulkValuesHolder from a bulkProperties GET", + "title": "NcMethodResultBulkValuesHolder" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json new file mode 100644 index 00000000..4603b227 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "PUT request body for invoking SetPropertiesByPath method on NcBulkPropertiesManager", + "title": "Bulk properties Set request", + "required": [ + "arguments" + ], + "properties": { + "arguments": { + "type": "object", + "description": "Method arguments. The rolePath is offered in the URL and is not part of these arguments", + "properties": { + "dataSet": { + "type": "object", + "description": "NcBulkValuesHolder datatype" + }, + "recurse": { + "type": "boolean" + } + } + } + } +} \ No newline at end of file diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json new file mode 100644 index 00000000..6b5885c0 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Returns a NcMethodResultObjectPropertiesSetValidation from a bulkProperties PUT", + "title": "NcMethodResultObjectPropertiesSetValidation" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json new file mode 100644 index 00000000..7796c1ce --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "OPTIONS request body for invoking ValidateSetPropertiesByPath method on NcBulkPropertiesManager", + "title": "Bulk properties Validate request", + "required": [ + "arguments" + ], + "properties": { + "arguments": { + "type": "object", + "description": "Method arguments. The rolePath is offered in the URL and is not part of these arguments", + "properties": { + "dataSet": { + "type": "object", + "description": "NcBulkValuesHolder datatype" + }, + "recurse": { + "type": "boolean" + } + } + } + } +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json new file mode 100644 index 00000000..0a077c2f --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Returns a NcMethodResultObjectPropertiesSetValidation from a bulkProperties OPTIONS", + "title": "NcMethodResultObjectPropertiesSetValidation" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/descriptor-get.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/descriptor-get.json new file mode 100644 index 00000000..28f76a40 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/descriptor-get.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResultClassDescriptor", + "title": "NcMethodResultClassDescriptor" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json new file mode 100644 index 00000000..42e25351 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "PATCH request body for invoking a method", + "title": "Invoke method body", + "required": [ + "arguments" + ], + "properties": { + "arguments": { + "type": "object", + "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. For methods which do not have arguments defined the object MUST be an empty object." + } + } +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-response.json new file mode 100644 index 00000000..c74183f9 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResult", + "title": "NcMethodResult" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json new file mode 100644 index 00000000..6b9222b5 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}/methods base", + "title": "Configuration API /rolePaths/{rolePath}/methods base", + "items": { + "type": "string", + "pattern": "^[0-9]+m[0-9]+" + }, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json new file mode 100644 index 00000000..4eea45a3 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResultError", + "title": "NcMethodResultError" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json new file mode 100644 index 00000000..72c3f548 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}/properties base", + "title": "Configuration API /rolePaths/{rolePath}/properties base", + "items": { + "type": "string", + "pattern": "^[0-9]+p[0-9]+" + }, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-descriptor.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-descriptor.json new file mode 100644 index 00000000..e564e0fd --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-descriptor.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResultDatatypeDescriptor", + "title": "NcMethodResultDatatypeDescriptor" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-get.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-get.json new file mode 100644 index 00000000..0f018830 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-get.json @@ -0,0 +1,5 @@ +{ + "type": "object", + "description": "NcMethodResultPropertyValue with the contents of the property", + "title": "NcMethodResultPropertyValue" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-request.json new file mode 100644 index 00000000..fdbd07d4 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-request.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "PUT request body for modyfing a property", + "title": "Modify property body", + "required": [ + "value" + ], + "properties": { + "value": { + "description": "New property value. The actual type is determined by the property's MS-05-02 datatype.", + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "integer" + }, + { + "type": "object" + }, + { + "type": "array" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + } +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-response.json new file mode 100644 index 00000000..c74183f9 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResult", + "title": "NcMethodResult" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json new file mode 100644 index 00000000..0a53e510 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}/properties/{propertyId}", + "title": "Configuration API /rolePaths/{rolePath}/properties/{propertyId}", + "items": { + "type": "string", + "enum": [ + "descriptor/", + "value/" + ] + }, + "minItems": 2, + "maxItems": 2, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json new file mode 100644 index 00000000..03cf62b4 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}", + "title": "Configuration API /rolePaths/{rolePath}", + "items": { + "type": "string", + "enum": [ + "bulkProperties/", + "descriptor/", + "methods/", + "properties/" + ] + }, + "minItems": 4, + "maxItems": 4, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePaths-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePaths-base.json new file mode 100644 index 00000000..de6d1c0b --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePaths-base.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths base", + "title": "Configuration API /rolePaths base", + "items": { + "type": "string" + }, + "uniqueItems": true +}