diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index b49f7873..12ce63b2 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,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/channels.cpp nmos/client_utils.cpp nmos/components.cpp + nmos/configuration_api.cpp nmos/connection_activation.cpp nmos/connection_api.cpp nmos/connection_events_activation.cpp @@ -1014,6 +1099,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/colorspace.h nmos/components.h nmos/copyable_atomic.h + nmos/configuration_api.h nmos/connection_activation.h nmos/connection_api.h nmos/connection_events_activation.h @@ -1048,6 +1134,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 +1285,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/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index e4b420fa..51a7dec1 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.get_properties_by_path, node_implementation.validate_set_properties_by_path, node_implementation.set_properties_by_path); 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..12d4bd70 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1206,6 +1206,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")); @@ -1263,7 +1266,7 @@ 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, 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_touchpoint_resource_types::receiver, receiver_id}) } })); // add receiver-monitor to root-block nmos::push_back(root_block, receiver_monitor); @@ -1284,6 +1287,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::push_back(root_block, class_manager); // add device-manager to root-block nmos::push_back(root_block, device_manager); + // add bulk-properties-manager to root-block + nmos::push_back(root_block, bulk_properties_manager); // insert control protocol resources to model insert_root_after(delay_millis, root_block, gate); @@ -1714,6 +1719,42 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control }; } +// Example Device Configuration callback for creating a back-up dataset +nmos::get_properties_by_path_handler make_node_implementation_get_properties_by_path_handler(const nmos::resources& resources, slog::base_gate& gate) +{ + return [&resources, &gate](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, const nmos::resource& resource, bool recurse) + { + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do get_properties_by_path"; + + // Implement backup of device model here + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }); + }; +} + +// Example Device Configuration callback for validating a back-up dataset +nmos::validate_set_properties_by_path_handler make_node_implementation_validate_set_properties_by_path_handler(const nmos::resources& resources, slog::base_gate& gate) +{ + return [&resources, &gate](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode) + { + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do validate_set_properties_by_path"; + + // Can this backup be restored? + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }); + }; +} + +// Example Device Configuration callback for restoring a back-up dataset +nmos::set_properties_by_path_handler make_node_implementation_set_properties_by_path_handler(nmos::resources& resources, slog::base_gate& gate) +{ + return [&resources, &gate](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, const nmos::resource& resource, const web::json::value& data_set, bool recurse, const web::json::value& restore_mode) + { + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do set_properties_by_path"; + + // Implement restore of device model here + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }); + }; +} + namespace impl { nmos::interlace_mode get_interlace_mode(const nmos::settings& settings) @@ -1868,5 +1909,8 @@ 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_get_properties_by_path(make_node_implementation_get_properties_by_path_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required + .on_validate_set_properties_by_path(make_node_implementation_validate_set_properties_by_path_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required + .on_set_properties_by_path(make_node_implementation_set_properties_by_path_handler(model.control_protocol_resources, gate)); // may be omitted if IS-14 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..26fa3b71 --- /dev/null +++ b/Development/nmos/configuration_api.cpp @@ -0,0 +1,864 @@ +#include "nmos/configuration_api.h" + +#include +#include +#include +#include "cpprest/json_validator.h" +#include "nmos/api_utils.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, 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, 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, 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::is_nc_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); + } + } + } + } + } + + web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, std::list& 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); + + const auto role_path_segement = role_path_segments.front(); + role_path_segments.pop_front(); + // find the role_path_segment member + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& member) + { + return role_path_segement == nmos::fields::nc::role(member); + }); + + if (members.end() != member_found) + { + if (role_path_segments.empty()) + { + // NcBlockMemberDescriptor + return *member_found; + } + + // get the role_path_segement member resource + if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) + { + // 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); + } + } + } + } + return web::json::value{}; + } + + resources::const_iterator find_resource(const resources& resources, std::list& role_path_segments) + { + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + const auto role = nmos::fields::nc::role(resource->data); + if (role_path_segments.size() && role == role_path_segments.front()) + { + role_path_segments.pop_front(); + + if (role_path_segments.size()) + { + const auto& block_member_descriptor = details::get_nc_block_member_descriptor(resources, *resource, role_path_segments); + 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 resources.end(); + } + + resources::const_iterator find_resource(const resources& resources, const utility::string_t& role_path) + { + // 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 find_resource(resources, role_path_segments); + } + + 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, 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); + }); + + 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); + }); + + 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 = details::find_resource(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); + }); + + 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 = details::find_resource(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); + }); + + 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 = details::find_resource(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); + }); + + 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 = details::find_resource(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); + }); + + 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 = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = 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); + }); + + 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 = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = 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); + }); + + 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 = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = 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::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(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); + }); + + 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 = details::find_resource(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 + 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; + }); + }); + + 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 = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = 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); + } + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return true; + }); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_method_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 = details::find_resource(resources, role_path); + const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + + if (resources.end() != resource && resources.end() != bulk_properties_manager) + { + auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_get_properties_by_path_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 + { + bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + + method_result = control_method_handler(resources, *resource, value_of({ { nmos::fields::nc::recurse, recurse } }), 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("parameter 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 + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("get_properties_by_path unsupported by bulk properties manager.")); + + code = status_codes::NotFound; + } + set_reply(res, code, method_result); + } + else + { + if (resources.end() == bulk_properties_manager) + { + // no bulk properties manager + set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); + } + 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); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_method_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); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = details::find_resource(resources, role_path); + const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + if (resources.end() != resource && resources.end() != bulk_properties_manager) + { + return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); + + auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_validate_set_properties_by_path_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 + { + method_result = control_method_handler(resources, *resource, nmos::fields::nc::arguments(body), 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("parameter 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 + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("validate_set_properties_by_path unsupported by bulk properties manager.")); + + code = status_codes::NotFound; + } + set_reply(res, code, method_result); + + return true; + }); + } + else + { + if (resources.end() == bulk_properties_manager) + { + // no bulk properties manager + set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); + } + 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); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_method_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); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = details::find_resource(resources, role_path); + const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + if (resources.end() != resource && resources.end() != bulk_properties_manager) + { + return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); + + auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_set_properties_by_path_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 + { + method_result = control_method_handler(resources, *resource, nmos::fields::nc::arguments(body), 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("parameter 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 + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("set_properties_by_path unsupported by bulk properties manager.")); + + code = status_codes::NotFound; + } + set_reply(res, code, method_result); + + return true; + }); + } + else + { + if (resources.end() == bulk_properties_manager) + { + // no bulk properties manager + set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); + } + 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); + }); + + return configuration_api; + } +} diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h new file mode 100644 index 00000000..9b1a7329 --- /dev/null +++ b/Development/nmos/configuration_api.h @@ -0,0 +1,21 @@ +#ifndef NMOS_CONFIGURATION_API_H +#define NMOS_CONFIGURATION_API_H + +#include "cpprest/api_router.h" +#include "nmos/control_protocol_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, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); +} + +#endif diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 0dcd8478..94c2c2b3 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -36,6 +36,12 @@ namespace nmos // this callback should not throw exceptions, as the relevant property will already has been changed and those changes will not be rolled back typedef std::function control_protocol_property_changed_handler; + // Device Configuration handlers + // these callbacks should not throw exceptions + typedef std::function get_properties_by_path_handler; + typedef std::function validate_set_properties_by_path_handler; + typedef std::function set_properties_by_path_handler; + namespace experimental { // control method handler definition diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 9894cd5e..a83c6db5 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... @@ -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... @@ -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... @@ -540,7 +540,7 @@ 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) { 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..9d866ea1 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -817,6 +817,16 @@ namespace nmos return data; } + // 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; + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) { @@ -1227,6 +1237,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 +1357,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 +2114,123 @@ 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"), 300)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), 400)); + + 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..270dcc51 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -191,6 +191,9 @@ 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); } // command message response @@ -281,6 +284,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 +437,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..485379b0 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -32,7 +32,7 @@ namespace nmos { 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 +98,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..7e29fa13 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -49,6 +49,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..09ee8f6c 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -1,5 +1,6 @@ #include "nmos/control_protocol_state.h" +#include "cpprest/http_utils.h" #include "nmos/control_protocol_methods.h" #include "nmos/control_protocol_resource.h" @@ -89,9 +90,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 +104,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 +132,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 +167,98 @@ 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(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, get_properties_by_path_handler get_properties_by_path) + { + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_properties_by_path](nmos::resources& resources, const nmos::resource& 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); + bool recurse = nmos::fields::nc::recurse(arguments); + + // Delegate to user defined handler + + auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); + if (get_properties_by_path) + { + result = get_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, recurse); + + 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_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, validate_set_properties_by_path_handler validate_set_properties_by_path) + { + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_set_properties_by_path](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 (validate_set_properties_by_path) + { + result = validate_set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); + + 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, set_properties_by_path_handler set_properties_by_path) + { + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, set_properties_by_path](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 (set_properties_by_path) + { + result = set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); + + 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, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path) { - using web::json::value; - auto to_vector = [](const web::json::value& data) { if (!data.is_null()) @@ -314,7 +389,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), get_properties_by_path)}, + { 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), validate_set_properties_by_path) }, + { 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), set_properties_by_path) } + }), + to_vector(make_nc_bulk_properties_manager_events())) } }; // setup the standard datatypes @@ -393,7 +478,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..ee0904e8 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -58,8 +58,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, get_properties_by_path_handler get_properties_by_path = nullptr, validate_set_properties_by_path_handler validate_set_properties_by_path = nullptr, set_properties_by_path_handler set_properties_by_path = 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..40301f0d 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -124,6 +124,20 @@ namespace nmos }; } + // NcRestoreValidationStatus + namespace nc_restore_validation_status + { + enum staus + { + ok = 200, // Restore was successful + excluded = 204, // Excluded from restore due to data provided in the request + invalid_data = 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 + missing_dependency = 424, // Restore failed because of missing dependency information in the relevant backup data set + device_error = 500 // Restore failed due to an internal device error preventing the restore from happening + }; + } + // NcElementId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid struct nc_element_id @@ -171,6 +185,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 +251,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 +289,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 +372,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 +400,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..1acff9d6 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -613,8 +613,7 @@ namespace nmos 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() - && nmos::ncp_nmos_resource_types::receiver.name == nmos::fields::nc::resource_type(resource)); + return (resource_id == nmos::fields::nc::id(resource).as_string()); }); return (tps.end() != found_tp); } 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..47caed18 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_string 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 2d6d8d23..623e7e61 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..1e9a97da 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.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..2dc96e4d 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -27,7 +27,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::get_properties_by_path_handler get_properties_by_path, nmos::validate_set_properties_by_path_handler validate_set_properties_by_path, nmos::set_properties_by_path_handler set_properties_by_path) : 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 +50,9 @@ 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)) + , get_properties_by_path(std::move(get_properties_by_path)) + , validate_set_properties_by_path(std::move(validate_set_properties_by_path)) + , set_properties_by_path(std::move(set_properties_by_path)) {} // use the default constructor and chaining member functions for fluent initialization @@ -82,6 +85,9 @@ 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_get_properties_by_path(nmos::get_properties_by_path_handler get_properties_by_path) { this->get_properties_by_path = std::move(get_properties_by_path); return *this; } + node_implementation& on_validate_set_properties_by_path(nmos::validate_set_properties_by_path_handler validate_set_properties_by_path) { this->validate_set_properties_by_path = std::move(validate_set_properties_by_path); return *this; } + node_implementation& on_set_properties_by_path(nmos::set_properties_by_path_handler set_properties_by_path) { this->set_properties_by_path = std::move(set_properties_by_path); 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 +130,11 @@ 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 method handlers + nmos::get_properties_by_path_handler get_properties_by_path; + nmos::validate_set_properties_by_path_handler validate_set_properties_by_path; + nmos::set_properties_by_path_handler set_properties_by_path; }; // 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/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 408e4fd5..692eba76 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -676,7 +676,7 @@ 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); { @@ -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 }); 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 +}