diff options
43 files changed, 1245 insertions, 263 deletions
diff --git a/src/components/application_manager/include/application_manager/application.h b/src/components/application_manager/include/application_manager/application.h index 844b08071b..da5860f06b 100644 --- a/src/components/application_manager/include/application_manager/application.h +++ b/src/components/application_manager/include/application_manager/application.h @@ -51,6 +51,7 @@ #include "protocol_handler/protocol_handler.h" #include "smart_objects/smart_object.h" #include "utils/macro.h" +#include "utils/semantic_version.h" namespace application_manager { @@ -115,6 +116,7 @@ class InitialApplicationData { virtual const smart_objects::SmartObject* ngn_media_screen_name() const = 0; virtual const mobile_api::Language::eType& language() const = 0; virtual const mobile_api::Language::eType& ui_language() const = 0; + virtual const utils::SemanticVersion& msg_version() const = 0; virtual void set_app_types(const smart_objects::SmartObject& app_types) = 0; virtual void set_vr_synonyms( const smart_objects::SmartObject& vr_synonyms) = 0; @@ -125,6 +127,7 @@ class InitialApplicationData { virtual void set_language(const mobile_api::Language::eType& language) = 0; virtual void set_ui_language( const mobile_api::Language::eType& ui_language) = 0; + virtual void set_msg_version(const utils::SemanticVersion& version) = 0; }; /* diff --git a/src/components/application_manager/include/application_manager/application_data_impl.h b/src/components/application_manager/include/application_manager/application_data_impl.h index 3f60040db9..05091dbfbd 100644 --- a/src/components/application_manager/include/application_manager/application_data_impl.h +++ b/src/components/application_manager/include/application_manager/application_data_impl.h @@ -35,6 +35,7 @@ #include <string> #include "utils/lock.h" +#include "utils/semantic_version.h" #include "smart_objects/smart_object.h" #include "application_manager/application.h" #include "interfaces/MOBILE_API.h" @@ -55,6 +56,7 @@ class InitialApplicationDataImpl : public virtual Application { const smart_objects::SmartObject* ngn_media_screen_name() const; const mobile_api::Language::eType& language() const; const mobile_api::Language::eType& ui_language() const; + const utils::SemanticVersion& msg_version() const; void set_app_types(const smart_objects::SmartObject& app_types); void set_vr_synonyms(const smart_objects::SmartObject& vr_synonyms); @@ -63,6 +65,7 @@ class InitialApplicationDataImpl : public virtual Application { void set_ngn_media_screen_name(const smart_objects::SmartObject& ngn_name); void set_language(const mobile_api::Language::eType& language); void set_ui_language(const mobile_api::Language::eType& ui_language); + void set_msg_version(const utils::SemanticVersion& version); void set_perform_interaction_layout( mobile_api::LayoutMode::eType layout) OVERRIDE; @@ -77,6 +80,7 @@ class InitialApplicationDataImpl : public virtual Application { mobile_api::Language::eType language_; mobile_api::Language::eType ui_language_; mobile_apis::LayoutMode::eType perform_interaction_layout_; + utils::SemanticVersion msg_version_; private: DISALLOW_COPY_AND_ASSIGN(InitialApplicationDataImpl); diff --git a/src/components/application_manager/include/application_manager/rpc_handler_impl.h b/src/components/application_manager/include/application_manager/rpc_handler_impl.h index 44954d41f7..8f1d454c3b 100644 --- a/src/components/application_manager/include/application_manager/rpc_handler_impl.h +++ b/src/components/application_manager/include/application_manager/rpc_handler_impl.h @@ -57,6 +57,7 @@ #include "interfaces/v4_protocol_v1_2_no_extra_schema.h" #include "utils/threads/message_loop_thread.h" +#include "utils/semantic_version.h" namespace application_manager { namespace rpc_handler { @@ -142,6 +143,16 @@ class RPCHandlerImpl : public RPCHandler, void SetTelemetryObserver(AMTelemetryObserver* observer) OVERRIDE; #endif // TELEMETRY_MONITOR + /** + * @brief Extracts and validates the syncMsgVersion included in + * a RegisterAppInterfaceRequest + * + * @param output - SmartObject Message received from mobile + * @param messageVersion - message version to be updated + */ + void GetMessageVersion(NsSmartDeviceLink::NsSmartObjects::SmartObject& output, + utils::SemanticVersion& message_version); + private: void ProcessMessageFromMobile(const std::shared_ptr<Message> message); void ProcessMessageFromHMI(const std::shared_ptr<Message> message); diff --git a/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/register_app_interface_request.cc b/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/register_app_interface_request.cc index 78a92d360a..164c854f18 100644 --- a/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/register_app_interface_request.cc +++ b/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/src/commands/mobile/register_app_interface_request.cc @@ -293,12 +293,45 @@ void RegisterAppInterfaceRequest::Run() { return; } + uint16_t major = + msg_params[strings::sync_msg_version][strings::major_version].asUInt(); + uint16_t minor = + msg_params[strings::sync_msg_version][strings::minor_version].asUInt(); + uint16_t patch = 0; + // Check if patch exists since it is not mandatory. + if (msg_params[strings::sync_msg_version].keyExists(strings::patch_version)) { + patch = + msg_params[strings::sync_msg_version][strings::patch_version].asUInt(); + } + + utils::SemanticVersion mobile_version(major, minor, patch); + utils::SemanticVersion min_module_version( + minimum_major_version, minimum_minor_version, minimum_patch_version); + + if (mobile_version < min_module_version) { + LOG4CXX_WARN(logger_, + "Application RPC Version does not meet minimum requirement"); + SendResponse(false, mobile_apis::Result::REJECTED); + } + application = application_manager_.RegisterApplication(message_); if (!application) { LOG4CXX_ERROR(logger_, "Application hasn't been registered!"); return; } + + // Version negotiation + utils::SemanticVersion module_version( + major_version, minor_version, patch_version); + if (mobile_version < module_version) { + // Use mobile RPC version as negotiated version + application->set_msg_version(mobile_version); + } else { + // Use module version as negotiated version + application->set_msg_version(module_version); + } + // For resuming application need to restore hmi_app_id from resumeCtrl resumption::ResumeCtrl& resumer = application_manager_.resume_controller(); const std::string& device_mac = application->mac_address(); @@ -585,12 +618,14 @@ void RegisterAppInterfaceRequest::SendRegisterAppInterfaceResponseToMobile( return; } + utils::SemanticVersion negotiated_version = application->msg_version(); + response_params[strings::sync_msg_version][strings::major_version] = - major_version; // From generated file interfaces/generated_msg_version.h + negotiated_version.major_version_; response_params[strings::sync_msg_version][strings::minor_version] = - minor_version; // From generated file interfaces/generated_msg_version.h + negotiated_version.minor_version_; response_params[strings::sync_msg_version][strings::patch_version] = - patch_version; // From generated file interfaces/generated_msg_version.h + negotiated_version.patch_version_; const smart_objects::SmartObject& msg_params = (*message_)[strings::msg_params]; diff --git a/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/test/commands/mobile/register_app_interface_request_test.cc b/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/test/commands/mobile/register_app_interface_request_test.cc index dbaee6b2ad..2518e9c7fd 100644 --- a/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/test/commands/mobile/register_app_interface_request_test.cc +++ b/src/components/application_manager/rpc_plugins/sdl_rpc_plugin/test/commands/mobile/register_app_interface_request_test.cc @@ -55,6 +55,7 @@ #include "utils/custom_string.h" #include "utils/lock.h" #include "utils/macro.h" +#include "utils/semantic_version.h" namespace test { namespace components { @@ -82,6 +83,7 @@ const std::string kMacAddress = "test_mac_address"; const std::string kAppId = "test_app_id"; const std::string kDummyString = "test_string"; const std::vector<uint32_t> kDummyDiagModes; +const utils::SemanticVersion mock_semantic_version(1, 0, 0); } // namespace class RegisterAppInterfaceRequestTest @@ -115,6 +117,12 @@ class RegisterAppInterfaceRequestTest kHmiLanguage; (*msg_)[am::strings::msg_params] [am::strings::hmi_display_language_desired] = kHmiLanguage; + (*msg_)[am::strings::msg_params][am::strings::sync_msg_version] + [am::strings::major_version] = 4; + (*msg_)[am::strings::msg_params][am::strings::sync_msg_version] + [am::strings::minor_version] = 0; + (*msg_)[am::strings::msg_params][am::strings::sync_msg_version] + [am::strings::patch_version] = 0; } MockAppPtr CreateBasicMockedApp() { @@ -125,6 +133,8 @@ class RegisterAppInterfaceRequestTest ON_CALL(*mock_app, language()).WillByDefault(ReturnRef(kMobileLanguage)); ON_CALL(*mock_app, ui_language()).WillByDefault(ReturnRef(kMobileLanguage)); ON_CALL(*mock_app, policy_app_id()).WillByDefault(Return(kAppId)); + ON_CALL(*mock_app, msg_version()) + .WillByDefault(ReturnRef(mock_semantic_version)); return mock_app; } @@ -281,6 +291,7 @@ TEST_F(RegisterAppInterfaceRequestTest, Run_MinimalData_SUCCESS) { .WillByDefault(Return(notify_upd_manager)); EXPECT_CALL(app_mngr_, RegisterApplication(msg_)).WillOnce(Return(mock_app)); + EXPECT_CALL(mock_rpc_service_, ManageHMICommand(HMIResultCodeIs( hmi_apis::FunctionID::BasicCommunication_OnAppRegistered))) diff --git a/src/components/application_manager/src/application_data_impl.cc b/src/components/application_manager/src/application_data_impl.cc index 3cfbeb2602..ea7893ddc5 100644 --- a/src/components/application_manager/src/application_data_impl.cc +++ b/src/components/application_manager/src/application_data_impl.cc @@ -103,6 +103,10 @@ const mobile_api::Language::eType& InitialApplicationDataImpl::ui_language() return ui_language_; } +const utils::SemanticVersion& InitialApplicationDataImpl::msg_version() const { + return msg_version_; +} + void InitialApplicationDataImpl::set_app_types( const smart_objects::SmartObject& app_types) { if (app_types_) { @@ -153,6 +157,11 @@ void InitialApplicationDataImpl::set_ui_language( ui_language_ = ui_language; } +void InitialApplicationDataImpl::set_msg_version( + const utils::SemanticVersion& version) { + msg_version_ = version; +} + void InitialApplicationDataImpl::set_perform_interaction_layout( mobile_apis::LayoutMode::eType layout) { perform_interaction_layout_ = layout; diff --git a/src/components/application_manager/src/rpc_handler_impl.cc b/src/components/application_manager/src/rpc_handler_impl.cc index b374147968..bf1ff78d36 100644 --- a/src/components/application_manager/src/rpc_handler_impl.cc +++ b/src/components/application_manager/src/rpc_handler_impl.cc @@ -194,6 +194,36 @@ void RPCHandlerImpl::SetTelemetryObserver(AMTelemetryObserver* observer) { #endif // TELEMETRY_MONITOR +void RPCHandlerImpl::GetMessageVersion( + NsSmartDeviceLink::NsSmartObjects::SmartObject& output, + utils::SemanticVersion& message_version) { + if (output.keyExists( + NsSmartDeviceLink::NsJSONHandler::strings::S_MSG_PARAMS) && + output[NsSmartDeviceLink::NsJSONHandler::strings::S_MSG_PARAMS].keyExists( + strings::sync_msg_version)) { + // SyncMsgVersion exists, check if it is valid. + auto sync_msg_version = + output[NsSmartDeviceLink::NsJSONHandler::strings::S_MSG_PARAMS] + [strings::sync_msg_version]; + uint16_t major = 0; + uint16_t minor = 0; + uint16_t patch = 0; + if (sync_msg_version.keyExists(strings::major_version)) { + major = sync_msg_version[strings::major_version].asUInt(); + } + if (sync_msg_version.keyExists(strings::minor_version)) { + minor = sync_msg_version[strings::minor_version].asUInt(); + } + if (sync_msg_version.keyExists(strings::patch_version)) { + patch = sync_msg_version[strings::patch_version].asUInt(); + } + utils::SemanticVersion temp_version(major, minor, patch); + if (temp_version.isValid()) { + message_version = temp_version; + } + } +} + bool RPCHandlerImpl::ConvertMessageToSO( const Message& message, NsSmartDeviceLink::NsSmartObjects::SmartObject& output) { @@ -218,9 +248,24 @@ bool RPCHandlerImpl::ConvertMessageToSO( rpc::ValidationReport report("RPC"); + // Attach RPC version to SmartObject if it does not exist yet. + auto app_ptr = app_manager_.application(message.connection_key()); + utils::SemanticVersion msg_version(0, 0, 0); + if (app_ptr && + (output[NsSmartDeviceLink::NsJSONHandler::strings::S_PARAMS] + .keyExists(NsSmartDeviceLink::NsJSONHandler::strings:: + S_RPC_MSG_VERSION) == false)) { + msg_version = app_ptr->msg_version(); + } else if (mobile_apis::FunctionID::RegisterAppInterfaceID == + static_cast<mobile_apis::FunctionID::eType>( + output[strings::params][strings::function_id].asInt())) { + GetMessageVersion(output, msg_version); + } + if (!conversion_result || - !mobile_so_factory().attachSchema(output, true) || - ((output.validate(&report) != smart_objects::Errors::OK))) { + !mobile_so_factory().attachSchema(output, true, msg_version) || + ((output.validate(&report, msg_version) != + smart_objects::Errors::OK))) { LOG4CXX_WARN(logger_, "Failed to parse string to smart object :" << message.json_message()); diff --git a/src/components/application_manager/test/include/application_manager/mock_application.h b/src/components/application_manager/test/include/application_manager/mock_application.h index 47a39d742c..602d228147 100644 --- a/src/components/application_manager/test/include/application_manager/mock_application.h +++ b/src/components/application_manager/test/include/application_manager/mock_application.h @@ -38,6 +38,7 @@ #include "smart_objects/smart_object.h" #include "utils/custom_string.h" #include "application_manager/usage_statistics.h" +#include "utils/semantic_version.h" namespace test { namespace components { @@ -191,6 +192,7 @@ class MockApplication : public ::application_manager::Application { const smart_objects::SmartObject*()); MOCK_CONST_METHOD0(language, const mobile_apis::Language::eType&()); MOCK_CONST_METHOD0(ui_language, const mobile_apis::Language::eType&()); + MOCK_CONST_METHOD0(msg_version, const utils::SemanticVersion&()); MOCK_METHOD1(set_app_types, void(const smart_objects::SmartObject& app_types)); MOCK_METHOD1(set_vr_synonyms, @@ -203,6 +205,7 @@ class MockApplication : public ::application_manager::Application { void(const mobile_apis::Language::eType& language)); MOCK_METHOD1(set_ui_language, void(const mobile_apis::Language::eType& ui_language)); + MOCK_METHOD1(set_msg_version, void(const utils::SemanticVersion& version)); // DynamicApplicationData methods MOCK_CONST_METHOD0(help_prompt, const smart_objects::SmartObject*()); MOCK_CONST_METHOD0(timeout_prompt, const smart_objects::SmartObject*()); diff --git a/src/components/formatters/include/formatters/CSmartFactory.h b/src/components/formatters/include/formatters/CSmartFactory.h index 9fed89253b..a459f179c0 100644 --- a/src/components/formatters/include/formatters/CSmartFactory.h +++ b/src/components/formatters/include/formatters/CSmartFactory.h @@ -82,6 +82,10 @@ extern const std::string S_PROTOCOL_TYPE; extern const std::string S_CORRELATION_ID; /** + * @brief String constant for RPC_MSG_VERSION. + */ +extern const std::string S_RPC_MSG_VERSION; +/** * @brief String constant for "code" param name. */ extern const std::string kCode; @@ -149,8 +153,10 @@ class CSmartFactory { * * @return True if operation was successful or false otherwise. */ - bool attachSchema(NsSmartDeviceLink::NsSmartObjects::SmartObject& object, - const bool RemoveFakeParameters); + bool attachSchema( + NsSmartDeviceLink::NsSmartObjects::SmartObject& object, + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion = utils::SemanticVersion()); /** * @brief Attach schema to the struct SmartObject. @@ -269,7 +275,8 @@ CSmartFactory<FunctionIdEnum, MessageTypeEnum, StructIdEnum>::CSmartFactory( template <class FunctionIdEnum, class MessageTypeEnum, class StructIdEnum> bool CSmartFactory<FunctionIdEnum, MessageTypeEnum, StructIdEnum>::attachSchema( NsSmartDeviceLink::NsSmartObjects::SmartObject& object, - const bool RemoveFakeParameters) { + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion) { if (false == object.keyExists(strings::S_PARAMS)) return false; if (false == object[strings::S_PARAMS].keyExists(strings::S_MESSAGE_TYPE)) @@ -295,7 +302,9 @@ bool CSmartFactory<FunctionIdEnum, MessageTypeEnum, StructIdEnum>::attachSchema( } object.setSchema(schemaIterator->second); - schemaIterator->second.applySchema(object, RemoveFakeParameters); + + schemaIterator->second.applySchema( + object, RemoveFakeParameters, MessageVersion); return true; } diff --git a/src/components/formatters/src/CSmartFactory.cc b/src/components/formatters/src/CSmartFactory.cc index 6b0808fbfa..6efd895500 100644 --- a/src/components/formatters/src/CSmartFactory.cc +++ b/src/components/formatters/src/CSmartFactory.cc @@ -47,6 +47,8 @@ const std::string NsSmartDeviceLink::NsJSONHandler::strings::S_PROTOCOL_TYPE( "protocol_type"); const std::string NsSmartDeviceLink::NsJSONHandler::strings::S_CORRELATION_ID( "correlation_id"); +const std::string NsSmartDeviceLink::NsJSONHandler::strings::S_RPC_MSG_VERSION( + "rpc_msg_version"); const std::string NsSmartDeviceLink::NsJSONHandler::strings::kCode("code"); const std::string NsSmartDeviceLink::NsJSONHandler::strings::kMessage( "message"); diff --git a/src/components/include/utils/semantic_version.h b/src/components/include/utils/semantic_version.h new file mode 100644 index 0000000000..7b9ec0eb96 --- /dev/null +++ b/src/components/include/utils/semantic_version.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018, Livio + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SRC_COMPONENTS_INCLUDE_UTILS_SEMANTIC_VERSION_H_ +#define SRC_COMPONENTS_INCLUDE_UTILS_SEMANTIC_VERSION_H_ + +namespace utils { + +struct SemanticVersion { + SemanticVersion(uint16_t major = 0, uint16_t minor = 0, uint16_t patch = 0) { + major_version_ = major; + minor_version_ = minor; + patch_version_ = patch; + } + + SemanticVersion(const SemanticVersion& other) { + major_version_ = other.major_version_; + minor_version_ = other.minor_version_; + patch_version_ = other.patch_version_; + } + + SemanticVersion(const std::string& versionString) + : major_version_(0), minor_version_(0), patch_version_(0) { + unsigned int major_int, minor_int, patch_int; + int readElements = sscanf( + versionString.c_str(), "%u.%u.%u", &major_int, &minor_int, &patch_int); + if (readElements != 3) { + // LOG4CXX_WARN(logger_, + // "Error while parsing version string: " << versionString); + } else { + major_version_ = static_cast<uint8_t>(major_int); + minor_version_ = static_cast<uint8_t>(minor_int); + patch_version_ = static_cast<uint8_t>(patch_int); + } + } + + static inline int16_t cmp(const SemanticVersion& version1, + const SemanticVersion& version2) { + int16_t diff = + static_cast<int16_t>(version1.major_version_ - version2.major_version_); + if (diff == 0) { + diff = static_cast<int16_t>(version1.minor_version_ - + version2.minor_version_); + if (diff == 0) { + diff = static_cast<int16_t>(version1.patch_version_ - + version2.patch_version_); + } + } + return diff; + } + + bool operator==(const SemanticVersion& other) const { + return SemanticVersion::cmp(*this, other) == 0; + } + bool operator<(const SemanticVersion& other) const { + return SemanticVersion::cmp(*this, other) < 0; + } + bool operator>(const SemanticVersion& other) const { + return SemanticVersion::cmp(*this, other) > 0; + } + bool operator<=(const SemanticVersion& other) const { + return SemanticVersion::cmp(*this, other) <= 0; + } + bool operator>=(const SemanticVersion& other) const { + return SemanticVersion::cmp(*this, other) >= 0; + } + static inline SemanticVersion* min(SemanticVersion& version1, + SemanticVersion& version2) { + return (version1 < version2) ? &version1 : &version2; + } + + const std::string toString() const { + std::string result = ""; + result += std::to_string(major_version_); + result += "."; + result += std::to_string(minor_version_); + result += "."; + result += std::to_string(patch_version_); + return result; + } + + bool isValid() const { + return major_version_ > 0 || minor_version_ > 0 || patch_version_ > 0; + } + + uint16_t major_version_ = 0; + uint16_t minor_version_ = 0; + uint16_t patch_version_ = 0; +}; +} + +#endif // SRC_COMPONENTS_INCLUDE_UTILS_CALLABLE_H
\ No newline at end of file diff --git a/src/components/interfaces/MOBILE_API.xml b/src/components/interfaces/MOBILE_API.xml index 45a484a6a4..4861fe7ba5 100644 --- a/src/components/interfaces/MOBILE_API.xml +++ b/src/components/interfaces/MOBILE_API.xml @@ -1,7 +1,7 @@ <?xml version="1.0" standalone="no"?> <?xml-stylesheet type="text/xml" href="protocol2html.xsl"?> -<interface name="SmartDeviceLink RAPI" version="4.5.0" date="2017-09-22"> +<interface name="SmartDeviceLink RAPI" version="4.5.0" minVersion="1.0.0" date="2017-09-22"> <enum name="Result" internal_scope="base"> <element name="SUCCESS"> <description>The request succeeded</description> diff --git a/src/components/protocol_handler/include/protocol_handler/handshake_handler.h b/src/components/protocol_handler/include/protocol_handler/handshake_handler.h index 8b7f28d50e..be81493e7a 100644 --- a/src/components/protocol_handler/include/protocol_handler/handshake_handler.h +++ b/src/components/protocol_handler/include/protocol_handler/handshake_handler.h @@ -40,6 +40,8 @@ #include "protocol_handler/session_observer.h" #include "security_manager/security_manager_listener.h" +#include "utils/semantic_version.h" + namespace protocol_handler { class ProtocolHandlerImpl; @@ -60,12 +62,12 @@ class HandshakeHandler : public security_manager::SecurityManagerListener { ServiceType service_type, const std::vector<int>& force_protected_service, const bool is_new_service, - ProtocolPacket::ProtocolVersion& full_version, + utils::SemanticVersion& full_version, std::shared_ptr<BsonObject> payload); HandshakeHandler(ProtocolHandlerImpl& protocol_handler, SessionObserver& session_observer, - ProtocolPacket::ProtocolVersion& full_version, + utils::SemanticVersion& full_version, const SessionContext& context, const uint8_t protocol_version, std::shared_ptr<BsonObject> payload); @@ -124,7 +126,7 @@ class HandshakeHandler : public security_manager::SecurityManagerListener { ProtocolHandlerImpl& protocol_handler_; SessionObserver& session_observer_; SessionContext context_; - ProtocolPacket::ProtocolVersion full_version_; + utils::SemanticVersion full_version_; const uint8_t protocol_version_; std::shared_ptr<BsonObject> payload_; }; diff --git a/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h b/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h index 58877ac611..e763be8c1b 100644 --- a/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h +++ b/src/components/protocol_handler/include/protocol_handler/protocol_handler_impl.h @@ -45,6 +45,7 @@ #include "utils/messagemeter.h" #include "utils/custom_string.h" +#include "utils/semantic_version.h" #include "protocol_handler/protocol_handler.h" #include "protocol_handler/protocol_packet.h" @@ -313,7 +314,7 @@ class ProtocolHandlerImpl uint32_t hash_code, uint8_t service_type, bool protection, - ProtocolPacket::ProtocolVersion& full_version); + utils::SemanticVersion& full_version); /** * \brief Sends acknowledgement of starting session to mobile application @@ -337,7 +338,7 @@ class ProtocolHandlerImpl uint32_t hash_code, uint8_t service_type, bool protection, - ProtocolPacket::ProtocolVersion& full_version, + utils::SemanticVersion& full_version, BsonObject& params); const ProtocolHandlerSettings& get_settings() const OVERRIDE { diff --git a/src/components/protocol_handler/include/protocol_handler/protocol_packet.h b/src/components/protocol_handler/include/protocol_handler/protocol_packet.h index ff084beff8..de4af84915 100644 --- a/src/components/protocol_handler/include/protocol_handler/protocol_packet.h +++ b/src/components/protocol_handler/include/protocol_handler/protocol_packet.h @@ -64,58 +64,6 @@ class ProtocolPacket { }; /** - * \class ProtocolVersion - * \brief Used for storing the full protocol version of a service - * (major.minor.patch). - */ - class ProtocolVersion { - public: - ProtocolVersion(); - ProtocolVersion(uint8_t majorVersion, - uint8_t minorVersion, - uint8_t patchVersion); - ProtocolVersion(ProtocolVersion& other); - ProtocolVersion(std::string versionString); - uint8_t majorVersion; - uint8_t minorVersion; - uint8_t patchVersion; - static inline int16_t cmp(const ProtocolVersion& version1, - const ProtocolVersion& version2) { - int16_t diff = - static_cast<int16_t>(version1.majorVersion - version2.majorVersion); - if (diff == 0) { - diff = - static_cast<int16_t>(version1.minorVersion - version2.minorVersion); - if (diff == 0) { - diff = static_cast<int16_t>(version1.patchVersion - - version2.patchVersion); - } - } - return diff; - } - inline bool operator==(const ProtocolVersion& other) { - return ProtocolVersion::cmp(*this, other) == 0; - } - inline bool operator<(const ProtocolVersion& other) { - return ProtocolVersion::cmp(*this, other) < 0; - } - bool operator>(const ProtocolVersion& other) { - return ProtocolVersion::cmp(*this, other) > 0; - } - inline bool operator<=(const ProtocolVersion& other) { - return ProtocolVersion::cmp(*this, other) <= 0; - } - bool operator>=(const ProtocolVersion& other) { - return ProtocolVersion::cmp(*this, other) >= 0; - } - static inline ProtocolVersion* min(ProtocolVersion& version1, - ProtocolVersion& version2) { - return (version1 < version2) ? &version1 : &version2; - } - std::string to_string(); - }; - - /** * \class ProtocolHeader * \brief Used for storing protocol header of a message. */ diff --git a/src/components/protocol_handler/src/handshake_handler.cc b/src/components/protocol_handler/src/handshake_handler.cc index 8db551cfd6..f6ab08319e 100644 --- a/src/components/protocol_handler/src/handshake_handler.cc +++ b/src/components/protocol_handler/src/handshake_handler.cc @@ -54,7 +54,7 @@ HandshakeHandler::HandshakeHandler( ServiceType service_type, const std::vector<int>& force_protected_service, const bool is_new_service, - ProtocolPacket::ProtocolVersion& full_version, + utils::SemanticVersion& full_version, std::shared_ptr<BsonObject> payload) : protocol_handler_(protocol_handler) , session_observer_(session_observer) @@ -63,13 +63,12 @@ HandshakeHandler::HandshakeHandler( , protocol_version_(protocol_version) , payload_(payload) {} -HandshakeHandler::HandshakeHandler( - ProtocolHandlerImpl& protocol_handler, - SessionObserver& session_observer, - ProtocolPacket::ProtocolVersion& full_version, - const SessionContext& context, - const uint8_t protocol_version, - std::shared_ptr<BsonObject> payload) +HandshakeHandler::HandshakeHandler(ProtocolHandlerImpl& protocol_handler, + SessionObserver& session_observer, + utils::SemanticVersion& full_version, + const SessionContext& context, + const uint8_t protocol_version, + std::shared_ptr<BsonObject> payload) : protocol_handler_(protocol_handler) , session_observer_(session_observer) , context_(context) diff --git a/src/components/protocol_handler/src/protocol_handler_impl.cc b/src/components/protocol_handler/src/protocol_handler_impl.cc index 872a8dc755..0e49e28397 100644 --- a/src/components/protocol_handler/src/protocol_handler_impl.cc +++ b/src/components/protocol_handler/src/protocol_handler_impl.cc @@ -61,8 +61,8 @@ std::string ConvertPacketDataToString(const uint8_t* data, const size_t kStackSize = 65536; -ProtocolPacket::ProtocolVersion defaultProtocolVersion(5, 1, 0); -ProtocolPacket::ProtocolVersion minMultipleTransportsVersion(5, 1, 0); +utils::SemanticVersion defaultProtocolVersion(5, 1, 0); +utils::SemanticVersion minMultipleTransportsVersion(5, 1, 0); ProtocolHandlerImpl::ProtocolHandlerImpl( const ProtocolHandlerSettings& settings, @@ -199,7 +199,7 @@ void ProtocolHandlerImpl::SendStartSessionAck(ConnectionID connection_id, uint8_t service_type, bool protection) { LOG4CXX_AUTO_TRACE(logger_); - ProtocolPacket::ProtocolVersion fullVersion; + utils::SemanticVersion fullVersion; SendStartSessionAck(connection_id, session_id, input_protocol_version, @@ -216,7 +216,7 @@ void ProtocolHandlerImpl::SendStartSessionAck( uint32_t hash_id, uint8_t service_type, bool protection, - ProtocolPacket::ProtocolVersion& full_version) { + utils::SemanticVersion& full_version) { LOG4CXX_AUTO_TRACE(logger_); BsonObject empty_param; @@ -241,7 +241,7 @@ void ProtocolHandlerImpl::SendStartSessionAck( uint32_t hash_id, uint8_t service_type, bool protection, - ProtocolPacket::ProtocolVersion& full_version, + utils::SemanticVersion& full_version, BsonObject& params) { LOG4CXX_AUTO_TRACE(logger_); @@ -252,7 +252,7 @@ void ProtocolHandlerImpl::SendStartSessionAck( const bool proxy_supports_v5_protocol = input_protocol_version >= PROTOCOL_VERSION_5 || (ServiceTypeFromByte(service_type) == kRpc && - full_version.majorVersion >= PROTOCOL_VERSION_5); + full_version.major_version_ >= PROTOCOL_VERSION_5); if (kRpc != service_type) { // In case if input protocol version os bigger then supported, SDL should @@ -307,13 +307,13 @@ void ProtocolHandlerImpl::SendStartSessionAck( ¶ms, strings::hash_id))); // Minimum protocol version supported by both - ProtocolPacket::ProtocolVersion* minVersion = - (full_version.majorVersion < PROTOCOL_VERSION_5) + utils::SemanticVersion* minVersion = + (full_version.major_version_ < PROTOCOL_VERSION_5) ? &defaultProtocolVersion - : ProtocolPacket::ProtocolVersion::min(full_version, - defaultProtocolVersion); + : utils::SemanticVersion::min(full_version, + defaultProtocolVersion); char protocolVersionString[256]; - strncpy(protocolVersionString, (*minVersion).to_string().c_str(), 255); + strncpy(protocolVersionString, (*minVersion).toString().c_str(), 255); const bool protocol_ver_written = bson_object_put_string( ¶ms, strings::protocol_version, protocolVersionString); @@ -1608,22 +1608,22 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessageStartSession( PROTECTION_OFF); return RESULT_OK; } - ProtocolPacket::ProtocolVersion* fullVersion; + utils::SemanticVersion* fullVersion; std::vector<std::string> rejectedParams(0, std::string("")); // Can't check protocol_version because the first packet is v1, but there // could still be a payload, in which case we can get the real protocol // version if (packet.service_type() == kRpc && packet.data_size() != 0) { BsonObject obj = bson_object_from_bytes(packet.data()); - fullVersion = new ProtocolPacket::ProtocolVersion( + fullVersion = new utils::SemanticVersion( std::string(bson_object_get_string(&obj, "protocolVersion"))); bson_object_deinitialize(&obj); // Constructed payloads added in Protocol v5 - if (fullVersion->majorVersion < PROTOCOL_VERSION_5) { + if (fullVersion->major_version_ < PROTOCOL_VERSION_5) { rejectedParams.push_back(std::string("protocolVersion")); } } else { - fullVersion = new ProtocolPacket::ProtocolVersion(); + fullVersion = new utils::SemanticVersion(); } if (!rejectedParams.empty()) { SendStartSessionNAck(connection_id, @@ -1670,11 +1670,11 @@ RESULT_CODE ProtocolHandlerImpl::HandleControlMessageStartSession( #endif // ENABLE_SECURITY if (packet.service_type() == kRpc && packet.data_size() != 0) { BsonObject obj = bson_object_from_bytes(packet.data()); - ProtocolPacket::ProtocolVersion fullVersion( + utils::SemanticVersion fullVersion( bson_object_get_string(&obj, "protocolVersion")); bson_object_deinitialize(&obj); - if (fullVersion.majorVersion >= PROTOCOL_VERSION_5) { + if (fullVersion.major_version_ >= PROTOCOL_VERSION_5) { // Start service without protection SendStartSessionAck(connection_id, session_id, @@ -1880,7 +1880,7 @@ void ProtocolHandlerImpl::NotifySessionStarted( bson_object_deinitialize(&req_param); } - std::shared_ptr<ProtocolPacket::ProtocolVersion> fullVersion; + std::shared_ptr<utils::SemanticVersion> fullVersion; // Can't check protocol_version because the first packet is v1, but there // could still be a payload, in which case we can get the real protocol @@ -1890,15 +1890,14 @@ void ProtocolHandlerImpl::NotifySessionStarted( char* version_param = bson_object_get_string(&request_params, strings::protocol_version); std::string version_string(version_param == NULL ? "" : version_param); - fullVersion = - std::make_shared<ProtocolPacket::ProtocolVersion>(version_string); + fullVersion = std::make_shared<utils::SemanticVersion>(version_string); // Constructed payloads added in Protocol v5 - if (fullVersion->majorVersion < PROTOCOL_VERSION_5) { + if (fullVersion->major_version_ < PROTOCOL_VERSION_5) { rejected_params.push_back(std::string(strings::protocol_version)); } bson_object_deinitialize(&request_params); } else { - fullVersion = std::make_shared<ProtocolPacket::ProtocolVersion>(); + fullVersion = std::make_shared<utils::SemanticVersion>(); } #ifdef ENABLE_SECURITY diff --git a/src/components/protocol_handler/src/protocol_packet.cc b/src/components/protocol_handler/src/protocol_packet.cc index 3cd9e7f781..d5422e11bc 100644 --- a/src/components/protocol_handler/src/protocol_packet.cc +++ b/src/components/protocol_handler/src/protocol_packet.cc @@ -41,6 +41,7 @@ #include "protocol_handler/protocol_packet.h" #include "utils/macro.h" #include "utils/byte_order.h" +#include "utils/semantic_version.h" namespace protocol_handler { @@ -52,48 +53,6 @@ ProtocolPacket::ProtocolData::~ProtocolData() { delete[] data; } -ProtocolPacket::ProtocolVersion::ProtocolVersion() - : majorVersion(0), minorVersion(0), patchVersion(0) {} - -ProtocolPacket::ProtocolVersion::ProtocolVersion(uint8_t majorVersion, - uint8_t minorVersion, - uint8_t patchVersion) - : majorVersion(majorVersion) - , minorVersion(minorVersion) - , patchVersion(patchVersion) {} - -ProtocolPacket::ProtocolVersion::ProtocolVersion(ProtocolVersion& other) { - this->majorVersion = other.majorVersion; - this->minorVersion = other.minorVersion; - this->patchVersion = other.patchVersion; -} - -ProtocolPacket::ProtocolVersion::ProtocolVersion(std::string versionString) - : majorVersion(0), minorVersion(0), patchVersion(0) { - unsigned int majorInt, minorInt, patchInt; - int readElements = sscanf( - versionString.c_str(), "%u.%u.%u", &majorInt, &minorInt, &patchInt); - if (readElements != 3) { - LOG4CXX_WARN(logger_, - "Error while parsing version string: " << versionString); - } else { - majorVersion = static_cast<uint8_t>(majorInt); - minorVersion = static_cast<uint8_t>(minorInt); - patchVersion = static_cast<uint8_t>(patchInt); - } -} - -std::string ProtocolPacket::ProtocolVersion::to_string() { - char versionString[256]; - snprintf(versionString, - 255, - "%u.%u.%u", - static_cast<unsigned int>(majorVersion), - static_cast<unsigned int>(minorVersion), - static_cast<unsigned int>(patchVersion)); - return std::string(versionString); -} - ProtocolPacket::ProtocolHeader::ProtocolHeader() : version(0x00) , protection_flag(PROTECTION_OFF) diff --git a/src/components/protocol_handler/test/protocol_handler_tm_test.cc b/src/components/protocol_handler/test/protocol_handler_tm_test.cc index cfda0a550a..615900c7fa 100644 --- a/src/components/protocol_handler/test/protocol_handler_tm_test.cc +++ b/src/components/protocol_handler/test/protocol_handler_tm_test.cc @@ -48,6 +48,7 @@ #endif // ENABLE_SECURITY #include "transport_manager/mock_transport_manager.h" #include "utils/mock_system_time_handler.h" +#include "utils/semantic_version.h" #include "utils/test_async_waiter.h" #include <bson_object.h> @@ -1539,7 +1540,7 @@ void ProtocolHandlerImplTest::VerifySecondaryTransportParamsInStartSessionAck( const uint8_t input_protocol_version = 5; const uint32_t hash_id = 123456; - ProtocolPacket::ProtocolVersion full_version(5, 1, 0); + utils::SemanticVersion full_version(5, 1, 0); char full_version_string[] = "5.1.0"; // configuration setup @@ -2030,7 +2031,7 @@ TEST_F(ProtocolHandlerImplTest, const uint8_t input_protocol_version = 5; const uint32_t hash_id = 123456; - ProtocolPacket::ProtocolVersion full_version(5, 0, 0); + utils::SemanticVersion full_version(5, 0, 0); char full_version_string[] = "5.0.0"; const size_t maximum_rpc_payload_size = 1500; @@ -2190,7 +2191,7 @@ TEST_F(ProtocolHandlerImplTest, const uint8_t input_protocol_version = 5; const uint32_t hash_id = 123456; - ProtocolPacket::ProtocolVersion full_version(5, 1, 0); + utils::SemanticVersion full_version(5, 1, 0); const size_t maximum_rpc_payload_size = 1500; EXPECT_CALL(protocol_handler_settings_mock, maximum_rpc_payload_size()) @@ -2313,7 +2314,7 @@ TEST_F(ProtocolHandlerImplTest, const uint8_t input_protocol_version = 5; const uint32_t hash_id = 123456; - ProtocolPacket::ProtocolVersion full_version(5, 1, 0); + utils::SemanticVersion full_version(5, 1, 0); const size_t maximum_rpc_payload_size = 1500; EXPECT_CALL(protocol_handler_settings_mock, maximum_rpc_payload_size()) diff --git a/src/components/smart_objects/include/smart_objects/always_false_schema_item.h b/src/components/smart_objects/include/smart_objects/always_false_schema_item.h index 4510f2292a..a7b93012bb 100644 --- a/src/components/smart_objects/include/smart_objects/always_false_schema_item.h +++ b/src/components/smart_objects/include/smart_objects/always_false_schema_item.h @@ -54,14 +54,18 @@ class CAlwaysFalseSchemaItem : public ISchemaItem { * @return Errors::ERROR **/ Errors::eType validate(const SmartObject& Object) OVERRIDE; + /** * @brief Validate smart object. * @param Object Object to validate. * @param report__ object for reporting errors during validation - * @return Errors::ERROR + * @param MessageVersion to check mobile RPC version against RPC Spec History + * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; private: CAlwaysFalseSchemaItem(); diff --git a/src/components/smart_objects/include/smart_objects/always_true_schema_item.h b/src/components/smart_objects/include/smart_objects/always_true_schema_item.h index 8a211339ba..b025723ea6 100644 --- a/src/components/smart_objects/include/smart_objects/always_true_schema_item.h +++ b/src/components/smart_objects/include/smart_objects/always_true_schema_item.h @@ -54,14 +54,18 @@ class CAlwaysTrueSchemaItem : public ISchemaItem { * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object) OVERRIDE; + /** * @brief Validate smart object. * @param Object Object to validate. * @param report__ object for reporting errors during validation + * @param MessageVersion to check mobile RPC version against RPC Spec History * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; private: CAlwaysTrueSchemaItem(); diff --git a/src/components/smart_objects/include/smart_objects/array_schema_item.h b/src/components/smart_objects/include/smart_objects/array_schema_item.h index 3e8be98ee0..4dc416007a 100644 --- a/src/components/smart_objects/include/smart_objects/array_schema_item.h +++ b/src/components/smart_objects/include/smart_objects/array_schema_item.h @@ -38,6 +38,8 @@ #include "smart_objects/always_true_schema_item.h" #include "smart_objects/schema_item_parameter.h" +#include "utils/semantic_version.h" + namespace NsSmartDeviceLink { namespace NsSmartObjects { /** @@ -74,15 +76,15 @@ class CArraySchemaItem : public ISchemaItem { /** * @brief Validate smart object. - * * @param Object Object to validate. * @param report__ object for reporting errors during validation - * message if an error occurs - * + * @param MessageVersion to check mobile RPC version against RPC Spec History * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; /** * @brief Apply schema. @@ -93,7 +95,9 @@ class CArraySchemaItem : public ISchemaItem { * from smart object otherwise contains false. **/ void applySchema(SmartObject& Object, - const bool RemoveFakeParameters) OVERRIDE; + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; /** * @brief Unapply schema. diff --git a/src/components/smart_objects/include/smart_objects/default_shema_item.h b/src/components/smart_objects/include/smart_objects/default_shema_item.h index fdac77d029..7d5b33b1a6 100644 --- a/src/components/smart_objects/include/smart_objects/default_shema_item.h +++ b/src/components/smart_objects/include/smart_objects/default_shema_item.h @@ -55,14 +55,18 @@ class CDefaultSchemaItem : public ISchemaItem { * @return Errors::ERROR **/ Errors::eType validate(const SmartObject& Object) OVERRIDE; + /** * @brief Validate smart object. * @param Object Object to validate. * @param report__ object for reporting errors during validation - * @return Errors::ERROR + * @param MessageVersion to check mobile RPC version against RPC Spec History + * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; /** * @brief Set default value to an object. @@ -113,7 +117,9 @@ Errors::eType CDefaultSchemaItem<Type>::validate(const SmartObject& Object) { template <typename Type> Errors::eType CDefaultSchemaItem<Type>::validate( - const SmartObject& Object, rpc::ValidationReport* report__) { + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { if (getSmartType() != Object.getType()) { std::string validation_info = "Incorrect type, expected: " + SmartObject::typeToString(getSmartType()) + diff --git a/src/components/smart_objects/include/smart_objects/enum_schema_item.h b/src/components/smart_objects/include/smart_objects/enum_schema_item.h index 450bbc6c40..41102933f3 100644 --- a/src/components/smart_objects/include/smart_objects/enum_schema_item.h +++ b/src/components/smart_objects/include/smart_objects/enum_schema_item.h @@ -41,9 +41,35 @@ #include "smart_objects/default_shema_item.h" +#include "utils/semantic_version.h" +#include <boost/optional.hpp> + namespace NsSmartDeviceLink { namespace NsSmartObjects { +struct ElementSignature { + boost::optional<utils::SemanticVersion> mSince; + boost::optional<utils::SemanticVersion> mUntil; + bool mRemoved; + + ElementSignature(std::string since = "", + std::string until = "", + bool removed = false) { + utils::SemanticVersion since_struct(since); + utils::SemanticVersion until_struct(until); + + if (since_struct.isValid()) { + mSince = since_struct; + } + + if (until_struct.isValid()) { + mUntil = until_struct; + } + + mRemoved = removed; + } +}; + template <typename EnumType> class EnumConversionHelper; /** @@ -63,6 +89,19 @@ class TEnumSchemaItem : public CDefaultSchemaItem<EnumType> { const std::set<EnumType>& AllowedElements, const TSchemaItemParameter<EnumType>& DefaultValue = TSchemaItemParameter<EnumType>()); + + /** + * @brief Create a new schema item. + * @param AllowedElements Set of allowed enumeration elements. + * @param DefaultValue Default value. + * @return Shared pointer to a new schema item. + **/ + static std::shared_ptr<TEnumSchemaItem> createWithSignatures( + const std::set<EnumType>& AllowedElements, + const std::map<EnumType, std::vector<ElementSignature> >& + ElementSignatures, + const TSchemaItemParameter<EnumType>& DefaultValue = + TSchemaItemParameter<EnumType>()); /** * @deprecated * @brief Validate smart object. @@ -70,14 +109,27 @@ class TEnumSchemaItem : public CDefaultSchemaItem<EnumType> { * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object) OVERRIDE; + /** * @brief Validate smart object. * @param Object Object to validate. * @param report__ object for reporting errors during validation + * @param MessageVersion to check mobile RPC version against RPC Spec History * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; + /** + * @brief Return the correct history signature based on message version. + * @param signatures Vector reference of enums history items. + * @param MessageVersion RPC Version of mobile app. + **/ + const ElementSignature getSignature( + const std::vector<ElementSignature>& signatures, + const utils::SemanticVersion& MessageVersion); + /** * @brief Apply schema. * This implementation checks if enumeration is represented as string @@ -88,7 +140,9 @@ class TEnumSchemaItem : public CDefaultSchemaItem<EnumType> { * from smart object otherwise contains false. **/ void applySchema(SmartObject& Object, - const bool RemoveFakeParameters) OVERRIDE; + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; /** * @brief Unapply schema. * @param Object Object to unapply schema. @@ -103,12 +157,18 @@ class TEnumSchemaItem : public CDefaultSchemaItem<EnumType> { **/ TEnumSchemaItem(const std::set<EnumType>& AllowedElements, const TSchemaItemParameter<EnumType>& DefaultValue); + + TEnumSchemaItem(const std::set<EnumType>& AllowedElements, + const TSchemaItemParameter<EnumType>& DefaultValue, + const std::map<EnumType, std::vector<ElementSignature> >& + ElementSignatures); SmartType getSmartType() const OVERRIDE; EnumType getDefaultValue() const OVERRIDE; /** * @brief Set of allowed enumeration elements. **/ const std::set<EnumType> mAllowedElements; + std::map<EnumType, std::vector<ElementSignature> > mElementSignatures; /** * @brief Default value. **/ @@ -215,14 +275,52 @@ std::shared_ptr<TEnumSchemaItem<EnumType> > TEnumSchemaItem<EnumType>::create( } template <typename EnumType> +std::shared_ptr<TEnumSchemaItem<EnumType> > +TEnumSchemaItem<EnumType>::createWithSignatures( + const std::set<EnumType>& AllowedElements, + const std::map<EnumType, std::vector<ElementSignature> >& ElementSignatures, + const TSchemaItemParameter<EnumType>& DefaultValue) { + return std::shared_ptr<TEnumSchemaItem<EnumType> >( + new TEnumSchemaItem<EnumType>( + AllowedElements, DefaultValue, ElementSignatures)); +} + +template <typename EnumType> Errors::eType TEnumSchemaItem<EnumType>::validate(const SmartObject& Object) { rpc::ValidationReport report("RPC"); return validate(Object, &report); } template <typename EnumType> +const ElementSignature TEnumSchemaItem<EnumType>::getSignature( + const std::vector<ElementSignature>& signatures, + const utils::SemanticVersion& MessageVersion) { + for (uint i = 0; i < signatures.size(); i++) { + ElementSignature signature = signatures[i]; + // Check if signature matches message version + if (signature.mSince != boost::none && + MessageVersion < signature.mSince.get()) { + // Msg version predates 'since' field, check next entry + continue; + } + if (signature.mUntil != boost::none && + (MessageVersion >= signature.mUntil.get())) { + continue; // Msg version newer than `until` field, check next entry + } + // Found correct signature + return signature; + } + + // Could not match msg version to element siganture + ElementSignature ret; + return ret; +} + +template <typename EnumType> Errors::eType TEnumSchemaItem<EnumType>::validate( - const SmartObject& Object, rpc::ValidationReport* report__) { + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { if (SmartType_Integer != Object.getType()) { std::string validation_info; if (SmartType_String == Object.getType()) { @@ -236,20 +334,53 @@ Errors::eType TEnumSchemaItem<EnumType>::validate( report__->set_validation_info(validation_info); return Errors::INVALID_VALUE; } - if (mAllowedElements.find(static_cast<EnumType>(Object.asInt())) == - mAllowedElements.end()) { + + auto elements_it = + mAllowedElements.find(static_cast<EnumType>(Object.asInt())); + + if (elements_it == mAllowedElements.end()) { std::stringstream stream; stream << "Invalid enum value: " << Object.asInt(); std::string validation_info = stream.str(); report__->set_validation_info(validation_info); return Errors::OUT_OF_RANGE; } + + // Element exists in schema. Check if version is also valid. + if (MessageVersion.isValid()) { + EnumType value = *elements_it; + auto signatures_it = mElementSignatures.find(value); + if (signatures_it != mElementSignatures.end()) { + if (!signatures_it->second.empty()) { + ElementSignature signature = + getSignature(signatures_it->second, MessageVersion); + if (signature.mRemoved) { + // Element was removed for this version + std::string validation_info = "Enum value : " + Object.asString() + + " removed for SyncMsgVersion " + + MessageVersion.toString(); + report__->set_validation_info(validation_info); + return Errors::INVALID_VALUE; + } else if (signature.mSince == boost::none && + signature.mUntil == boost::none) { + // Element does not exist for this version + std::string validation_info = "Enum value : " + Object.asString() + + " does not exist for SyncMsgVersion " + + MessageVersion.toString(); + report__->set_validation_info(validation_info); + return Errors::INVALID_VALUE; + } + } + } + } return Errors::OK; } template <typename EnumType> -void TEnumSchemaItem<EnumType>::applySchema(SmartObject& Object, - const bool RemoveFakeParameters) { +void TEnumSchemaItem<EnumType>::applySchema( + SmartObject& Object, + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion) { if (SmartType_String == Object.getType()) { EnumType enum_val = static_cast<EnumType>(-1); if (ConversionHelper::StringToEnum(Object.asString(), &enum_val)) { @@ -285,6 +416,15 @@ TEnumSchemaItem<EnumType>::TEnumSchemaItem( : CDefaultSchemaItem<EnumType>(DefaultValue) , mAllowedElements(AllowedElements) {} +template <typename EnumType> +TEnumSchemaItem<EnumType>::TEnumSchemaItem( + const std::set<EnumType>& AllowedElements, + const TSchemaItemParameter<EnumType>& DefaultValue, + const std::map<EnumType, std::vector<ElementSignature> >& ElementSignatures) + : CDefaultSchemaItem<EnumType>(DefaultValue) + , mAllowedElements(AllowedElements) + , mElementSignatures(ElementSignatures) {} + } // namespace NsSmartObjects } // namespace NsSmartDeviceLink #endif // SRC_COMPONENTS_SMART_OBJECTS_INCLUDE_SMART_OBJECTS_ENUM_SCHEMA_ITEM_H_ diff --git a/src/components/smart_objects/include/smart_objects/number_schema_item.h b/src/components/smart_objects/include/smart_objects/number_schema_item.h index 2c64538953..0f3246ef5f 100644 --- a/src/components/smart_objects/include/smart_objects/number_schema_item.h +++ b/src/components/smart_objects/include/smart_objects/number_schema_item.h @@ -72,14 +72,18 @@ class TNumberSchemaItem : public CDefaultSchemaItem<NumberType> { * @return Errors::ERROR **/ Errors::eType validate(const SmartObject& Object) OVERRIDE; + /** * @brief Validate smart object. * @param Object Object to validate. * @param report__ object for reporting errors during validation - * @return Errors::ERROR + * @param MessageVersion to check mobile RPC version against RPC Spec History + * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; private: /** @@ -146,7 +150,9 @@ Errors::eType TNumberSchemaItem<NumberType>::validate( template <typename NumberType> Errors::eType TNumberSchemaItem<NumberType>::validate( - const SmartObject& Object, rpc::ValidationReport* report__) { + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { if (!isValidNumberType(Object.getType())) { SmartType expectedType = (typeid(double) == typeid(Object.getType())) ? SmartType_Double diff --git a/src/components/smart_objects/include/smart_objects/object_schema_item.h b/src/components/smart_objects/include/smart_objects/object_schema_item.h index 4c0def558b..4785211694 100644 --- a/src/components/smart_objects/include/smart_objects/object_schema_item.h +++ b/src/components/smart_objects/include/smart_objects/object_schema_item.h @@ -37,6 +37,8 @@ #include <set> #include "utils/macro.h" +#include "utils/semantic_version.h" +#include <boost/optional.hpp> #include "smart_objects/schema_item.h" #include "smart_objects/schema_item_parameter.h" @@ -62,7 +64,21 @@ class CObjectSchemaItem : public ISchemaItem { * @param IsMandatory true if member is mandatory, false * otherwise. Defaults to true. **/ - SMember(const ISchemaItemPtr SchemaItem, const bool IsMandatory = true); + + SMember(const ISchemaItemPtr SchemaItem, + const bool IsMandatory = true, + const std::string& Since = "", + const std::string& Until = "", + const bool IsDeprecated = false, + const bool IsRemoved = false, + const std::vector<CObjectSchemaItem::SMember>& history_vector = {}); + /** + * @brief Checks the version a parameter was removed (until) + * If the mobile's msg version is greater than or + **/ + bool CheckHistoryFieldVersion( + const utils::SemanticVersion& MessageVersion) const; + /** * @brief Member schema item. **/ @@ -71,6 +87,11 @@ class CObjectSchemaItem : public ISchemaItem { * @brief true if member is mandatory, false otherwise. **/ bool mIsMandatory; + boost::optional<utils::SemanticVersion> mSince; + boost::optional<utils::SemanticVersion> mUntil; + bool mIsDeprecated; + bool mIsRemoved; + std::vector<CObjectSchemaItem::SMember> mHistoryVector; }; typedef std::map<std::string, SMember> Members; /** @@ -89,14 +110,18 @@ class CObjectSchemaItem : public ISchemaItem { * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object) OVERRIDE; + /** * @brief Validate smart object. * @param Object Object to validate. * @param report__ object for reporting errors during validation + * @param MessageVersion to check mobile RPC version against RPC Spec History * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; /** * @brief Apply schema. * @param Object Object to apply schema. @@ -104,7 +129,9 @@ class CObjectSchemaItem : public ISchemaItem { * from smart object otherwise contains false. **/ void applySchema(SmartObject& Object, - const bool RemoveFakeParameters) OVERRIDE; + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; /** * @brief Unapply schema. * @param Object Object to unapply schema. @@ -136,7 +163,16 @@ class CObjectSchemaItem : public ISchemaItem { * @brief Removes fake parameters from object. * @param Object Object to remove fake parameters. **/ - void RemoveFakeParams(SmartObject& Object); + void RemoveFakeParams(SmartObject& Object, + const utils::SemanticVersion& MessageVersion); + + /** + * @brief Returns the correct schema item based on message version. + * @param member Schema member + * @param MmessageVersion Semantic Version of mobile message. + **/ + const CObjectSchemaItem::SMember& GetCorrectMember( + const SMember& member, const utils::SemanticVersion& messageVersion); /** * @brief Map of member name to SMember structure describing the object diff --git a/src/components/smart_objects/include/smart_objects/schema_item.h b/src/components/smart_objects/include/smart_objects/schema_item.h index 2b15a1d47a..2e951f7f9f 100644 --- a/src/components/smart_objects/include/smart_objects/schema_item.h +++ b/src/components/smart_objects/include/smart_objects/schema_item.h @@ -40,6 +40,7 @@ #include <memory> #include "utils/macro.h" +#include "utils/semantic_version.h" namespace NsSmartDeviceLink { namespace NsSmartObjects { @@ -67,11 +68,13 @@ class ISchemaItem { * @param Object Object to validate. * @param report__ object for reporting errors during validation * message if an error occurs - * + * @param MessageVersion to check mobile RPC version against RPC Spec Histor * @return NsSmartObjects::Errors::eType **/ - virtual Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__); + virtual Errors::eType validate( + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = utils::SemanticVersion()); /** * @brief Set default value to an object. @@ -100,7 +103,8 @@ class ISchemaItem { **/ virtual void applySchema( NsSmartDeviceLink::NsSmartObjects::SmartObject& Object, - const bool RemoveFakeParameters); + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion = utils::SemanticVersion()); /** * @brief Unapply schema. diff --git a/src/components/smart_objects/include/smart_objects/smart_object.h b/src/components/smart_objects/include/smart_objects/smart_object.h index 6a2cbdb911..d81e18569f 100644 --- a/src/components/smart_objects/include/smart_objects/smart_object.h +++ b/src/components/smart_objects/include/smart_objects/smart_object.h @@ -682,9 +682,12 @@ class SmartObject FINAL { * @brief Validates object according to attached schema. * * @param report__ object for reporting errors during validation + * @param messageVersion of the mobile app to check against RPC Spec Schema * @return Result of validation. */ - Errors::eType validate(rpc::ValidationReport* report__); + Errors::eType validate( + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = utils::SemanticVersion()); /** * @brief Sets new schema diff --git a/src/components/smart_objects/include/smart_objects/smart_schema.h b/src/components/smart_objects/include/smart_objects/smart_schema.h index ada5646043..d3fcc0e28d 100644 --- a/src/components/smart_objects/include/smart_objects/smart_schema.h +++ b/src/components/smart_objects/include/smart_objects/smart_schema.h @@ -76,11 +76,13 @@ class CSmartSchema FINAL { * * @param Object Object to validate. * @param report__ object for reporting errors during validation - * + * @param MessageVersion to check mobile RPC version against RPC Spec History * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) const; + rpc::ValidationReport* report__, + const utils::SemanticVersion& messageVersion = + utils::SemanticVersion()) const; /** * @brief Set new root schema item. @@ -97,7 +99,10 @@ class CSmartSchema FINAL { * @param RemoveFakeParameters contains true if need to remove fake parameters * from smart object otherwise contains false. **/ - void applySchema(SmartObject& Object, const bool RemoveFakeParameters); + void applySchema( + SmartObject& Object, + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion = utils::SemanticVersion()); /** * @brief The reverse SmartObject conversion using schema. diff --git a/src/components/smart_objects/include/smart_objects/string_schema_item.h b/src/components/smart_objects/include/smart_objects/string_schema_item.h index 50418debc0..dcaad364b2 100644 --- a/src/components/smart_objects/include/smart_objects/string_schema_item.h +++ b/src/components/smart_objects/include/smart_objects/string_schema_item.h @@ -66,14 +66,18 @@ class CStringSchemaItem : public CDefaultSchemaItem<std::string> { * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object) OVERRIDE; + /** * @brief Validate smart object. * @param Object Object to validate. * @param report__ object for reporting errors during validation + * @param MessageVersion to check mobile RPC version against RPC Spec History * @return NsSmartObjects::Errors::eType **/ Errors::eType validate(const SmartObject& Object, - rpc::ValidationReport* report__) OVERRIDE; + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion = + utils::SemanticVersion()) OVERRIDE; private: /** diff --git a/src/components/smart_objects/src/always_false_schema_item.cc b/src/components/smart_objects/src/always_false_schema_item.cc index 203ae11236..db16640265 100644 --- a/src/components/smart_objects/src/always_false_schema_item.cc +++ b/src/components/smart_objects/src/always_false_schema_item.cc @@ -47,9 +47,12 @@ Errors::eType CAlwaysFalseSchemaItem::validate(const SmartObject& object) { } Errors::eType CAlwaysFalseSchemaItem::validate( - const SmartObject& object, rpc::ValidationReport* report__) { + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { report__->set_validation_info("Generic error"); return Errors::ERROR; } + } // namespace NsSmartObjects } // namespace NsSmartDeviceLink diff --git a/src/components/smart_objects/src/always_true_schema_item.cc b/src/components/smart_objects/src/always_true_schema_item.cc index 774bcea121..0a3b8be134 100644 --- a/src/components/smart_objects/src/always_true_schema_item.cc +++ b/src/components/smart_objects/src/always_true_schema_item.cc @@ -43,8 +43,10 @@ Errors::eType CAlwaysTrueSchemaItem::validate(const SmartObject& object) { return Errors::OK; } -Errors::eType CAlwaysTrueSchemaItem::validate(const SmartObject& object, - rpc::ValidationReport* report__) { +Errors::eType CAlwaysTrueSchemaItem::validate( + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { return Errors::OK; } diff --git a/src/components/smart_objects/src/array_schema_item.cc b/src/components/smart_objects/src/array_schema_item.cc index f006267f10..60081c5222 100644 --- a/src/components/smart_objects/src/array_schema_item.cc +++ b/src/components/smart_objects/src/array_schema_item.cc @@ -47,8 +47,10 @@ Errors::eType CArraySchemaItem::validate(const SmartObject& Object) { return validate(Object, &report); } -Errors::eType CArraySchemaItem::validate(const SmartObject& Object, - rpc::ValidationReport* report__) { +Errors::eType CArraySchemaItem::validate( + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { if (SmartType_Array != Object.getType()) { std::string validation_info = "Incorrect type, expected: " + SmartObject::typeToString(SmartType_Array) + @@ -80,8 +82,10 @@ Errors::eType CArraySchemaItem::validate(const SmartObject& Object, for (size_t i = 0u; i < array_len; ++i) { std::stringstream strVal; strVal << i; - const Errors::eType result = mElementSchemaItem->validate( - Object.getElement(i), &report__->ReportSubobject(strVal.str())); + const Errors::eType result = + mElementSchemaItem->validate(Object.getElement(i), + &report__->ReportSubobject(strVal.str()), + MessageVersion); if (Errors::OK != result) { return result; } @@ -89,11 +93,14 @@ Errors::eType CArraySchemaItem::validate(const SmartObject& Object, return Errors::OK; } -void CArraySchemaItem::applySchema(SmartObject& Object, - const bool RemoveFakeParameters) { +void CArraySchemaItem::applySchema( + SmartObject& Object, + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion) { if (SmartType_Array == Object.getType()) { for (size_t i = 0U; i < Object.length(); ++i) { - mElementSchemaItem->applySchema(Object[i], RemoveFakeParameters); + mElementSchemaItem->applySchema( + Object[i], RemoveFakeParameters, MessageVersion); } } } diff --git a/src/components/smart_objects/src/object_schema_item.cc b/src/components/smart_objects/src/object_schema_item.cc index c79b97689d..879c280a67 100644 --- a/src/components/smart_objects/src/object_schema_item.cc +++ b/src/components/smart_objects/src/object_schema_item.cc @@ -47,9 +47,57 @@ namespace NsSmartObjects { CObjectSchemaItem::SMember::SMember() : mSchemaItem(CAlwaysFalseSchemaItem::create()), mIsMandatory(true) {} -CObjectSchemaItem::SMember::SMember(const ISchemaItemPtr SchemaItem, - const bool IsMandatory) - : mSchemaItem(SchemaItem), mIsMandatory(IsMandatory) {} +CObjectSchemaItem::SMember::SMember( + const ISchemaItemPtr SchemaItem, + const bool IsMandatory, + const std::string& Since, + const std::string& Until, + const bool IsDeprecated, + const bool IsRemoved, + const std::vector<CObjectSchemaItem::SMember>& history_vector) + : mSchemaItem(SchemaItem), mIsMandatory(IsMandatory) { + if (Since.size() > 0) { + utils::SemanticVersion since_struct(Since); + if (since_struct.isValid()) { + mSince = since_struct; + } + } + if (Until.size() > 0) { + utils::SemanticVersion until_struct(Until); + if (until_struct.isValid()) { + mUntil = until_struct; + } + } + mIsDeprecated = IsDeprecated; + mIsRemoved = IsRemoved; + mHistoryVector = history_vector; +} + +bool CObjectSchemaItem::SMember::CheckHistoryFieldVersion( + const utils::SemanticVersion& MessageVersion) const { + if (MessageVersion.isValid()) { + if (mSince != boost::none) { + if (MessageVersion < mSince.get()) { + return false; // Msg version predates `since` field + } else { + if (mUntil != boost::none && (MessageVersion >= mUntil.get())) { + return false; // Msg version newer than `until` field + } else { + return true; // Mobile msg version falls within specified version + // range + } + } + } + + if (mUntil != boost::none && (MessageVersion >= mUntil.get())) { + return false; // Msg version newer than `until` field + } else { + return true; // Mobile msg version falls within specified version range + } + } + + return true; // Not enough version information. Default true. +} std::shared_ptr<CObjectSchemaItem> CObjectSchemaItem::create( const Members& members) { @@ -61,8 +109,10 @@ Errors::eType CObjectSchemaItem::validate(const SmartObject& object) { return validate(object, &report); } -Errors::eType CObjectSchemaItem::validate(const SmartObject& object, - rpc::ValidationReport* report__) { +Errors::eType CObjectSchemaItem::validate( + const SmartObject& object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { if (SmartType_Map != object.getType()) { std::string validation_info = "Incorrect type, expected: " + SmartObject::typeToString(SmartType_Map) + @@ -78,10 +128,12 @@ Errors::eType CObjectSchemaItem::validate(const SmartObject& object, ++it) { const std::string& key = it->first; const SMember& member = it->second; + const SMember& correct_member = GetCorrectMember(member, MessageVersion); std::set<std::string>::const_iterator key_it = object_keys.find(key); if (object_keys.end() == key_it) { - if (member.mIsMandatory) { + if (correct_member.mIsMandatory == true && + correct_member.mIsRemoved == false) { std::string validation_info = "Missing mandatory parameter: " + key; report__->set_validation_info(validation_info); return Errors::MISSING_MANDATORY_PARAMETER; @@ -89,8 +141,11 @@ Errors::eType CObjectSchemaItem::validate(const SmartObject& object, continue; } const SmartObject& field = object.getElement(key); - const Errors::eType result = - member.mSchemaItem->validate(field, &report__->ReportSubobject(key)); + + Errors::eType result = Errors::OK; + // Check if MessageVersion matches schema version + result = correct_member.mSchemaItem->validate( + field, &report__->ReportSubobject(key), MessageVersion); if (Errors::OK != result) { return result; } @@ -99,14 +154,16 @@ Errors::eType CObjectSchemaItem::validate(const SmartObject& object, return Errors::OK; } -void CObjectSchemaItem::applySchema(SmartObject& Object, - const bool RemoveFakeParameters) { +void CObjectSchemaItem::applySchema( + SmartObject& Object, + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion) { if (SmartType_Map != Object.getType()) { return; } if (RemoveFakeParameters) { - RemoveFakeParams(Object); + RemoveFakeParams(Object, MessageVersion); } SmartObject default_value; @@ -117,10 +174,12 @@ void CObjectSchemaItem::applySchema(SmartObject& Object, if (!Object.keyExists(key)) { if (member.mSchemaItem->setDefaultValue(default_value)) { Object[key] = default_value; - member.mSchemaItem->applySchema(Object[key], RemoveFakeParameters); + member.mSchemaItem->applySchema( + Object[key], RemoveFakeParameters, MessageVersion); } } else { - member.mSchemaItem->applySchema(Object[key], RemoveFakeParameters); + member.mSchemaItem->applySchema( + Object[key], RemoveFakeParameters, MessageVersion); } } } @@ -173,21 +232,48 @@ size_t CObjectSchemaItem::GetMemberSize() { CObjectSchemaItem::CObjectSchemaItem(const Members& members) : mMembers(members) {} -void CObjectSchemaItem::RemoveFakeParams(SmartObject& Object) { +void CObjectSchemaItem::RemoveFakeParams( + SmartObject& Object, const utils::SemanticVersion& MessageVersion) { for (SmartMap::const_iterator it = Object.map_begin(); it != Object.map_end();) { const std::string& key = it->first; - if (mMembers.end() == mMembers.find(key) + std::map<std::string, SMember>::const_iterator members_it = + mMembers.find(key); + if (mMembers.end() == members_it // FIXME(EZamakhov): Remove illegal usage of filed in AM && key.compare(connection_key) != 0 && key.compare(binary_data) != 0 && key.compare(app_id) != 0) { ++it; Object.erase(key); + + } else if (mMembers.end() != members_it && + GetCorrectMember(members_it->second, MessageVersion) + .mIsRemoved) { + ++it; + Object.erase(key); } else { - it++; + ++it; + } + } +} + +const CObjectSchemaItem::SMember& CObjectSchemaItem::GetCorrectMember( + const SMember& member, const utils::SemanticVersion& messageVersion) { + // Check if member is the correct version + if (member.CheckHistoryFieldVersion(messageVersion)) { + return member; + } + // Check for history tag items + if (!member.mHistoryVector.empty()) { + for (uint i = 0; i < member.mHistoryVector.size(); i++) { + if (member.mHistoryVector[i].CheckHistoryFieldVersion(messageVersion)) { + return member.mHistoryVector[i]; + } } } + // Return member as default + return member; } } // namespace NsSmartObjects diff --git a/src/components/smart_objects/src/schema_item.cc b/src/components/smart_objects/src/schema_item.cc index 22735d40d5..ae7f3665bb 100644 --- a/src/components/smart_objects/src/schema_item.cc +++ b/src/components/smart_objects/src/schema_item.cc @@ -39,8 +39,10 @@ Errors::eType ISchemaItem::validate(const SmartObject& Object) { return Errors::ERROR; } -Errors::eType ISchemaItem::validate(const SmartObject& object, - rpc::ValidationReport* report__) { +Errors::eType ISchemaItem::validate( + const SmartObject& object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { return Errors::ERROR; } @@ -53,7 +55,8 @@ bool ISchemaItem::hasDefaultValue(SmartObject& Object) { } void ISchemaItem::applySchema(SmartObject& Object, - const bool RemoveFakeParameters) {} + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion) {} void ISchemaItem::unapplySchema(SmartObject& Object) {} diff --git a/src/components/smart_objects/src/smart_object.cc b/src/components/smart_objects/src/smart_object.cc index a0925eef05..691f45f63b 100644 --- a/src/components/smart_objects/src/smart_object.cc +++ b/src/components/smart_objects/src/smart_object.cc @@ -878,8 +878,10 @@ Errors::eType SmartObject::validate() { return validate(&report); } -Errors::eType SmartObject::validate(rpc::ValidationReport* report__) { - return m_schema.validate(*this, report__); +Errors::eType SmartObject::validate( + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { + return m_schema.validate(*this, report__, MessageVersion); } void SmartObject::setSchema(const CSmartSchema& schema) { diff --git a/src/components/smart_objects/src/smart_schema.cc b/src/components/smart_objects/src/smart_schema.cc index 7509ea80f1..3ab94caf85 100644 --- a/src/components/smart_objects/src/smart_schema.cc +++ b/src/components/smart_objects/src/smart_schema.cc @@ -45,9 +45,11 @@ Errors::eType CSmartSchema::validate(const SmartObject& Object) const { return validate(Object, &report); } -Errors::eType CSmartSchema::validate(const SmartObject& object, - rpc::ValidationReport* report__) const { - return mSchemaItem->validate(object, report__); +Errors::eType CSmartSchema::validate( + const SmartObject& object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) const { + return mSchemaItem->validate(object, report__, MessageVersion); } void CSmartSchema::setSchemaItem(const ISchemaItemPtr schemaItem) { @@ -55,8 +57,9 @@ void CSmartSchema::setSchemaItem(const ISchemaItemPtr schemaItem) { } void CSmartSchema::applySchema(SmartObject& Object, - const bool RemoveFakeParameters) { - mSchemaItem->applySchema(Object, RemoveFakeParameters); + const bool RemoveFakeParameters, + const utils::SemanticVersion& MessageVersion) { + mSchemaItem->applySchema(Object, RemoveFakeParameters, MessageVersion); } void CSmartSchema::unapplySchema(SmartObject& Object) { diff --git a/src/components/smart_objects/src/string_schema_item.cc b/src/components/smart_objects/src/string_schema_item.cc index b403607ede..f3c39eff8f 100644 --- a/src/components/smart_objects/src/string_schema_item.cc +++ b/src/components/smart_objects/src/string_schema_item.cc @@ -51,8 +51,10 @@ Errors::eType CStringSchemaItem::validate(const SmartObject& Object) { return validate(Object, &report); } -Errors::eType CStringSchemaItem::validate(const SmartObject& Object, - rpc::ValidationReport* report__) { +Errors::eType CStringSchemaItem::validate( + const SmartObject& Object, + rpc::ValidationReport* report__, + const utils::SemanticVersion& MessageVersion) { if (SmartType_String != Object.getType()) { std::string validation_info = "Incorrect type, expected: " + SmartObject::typeToString(SmartType_String) + diff --git a/src/components/smart_objects/test/CObjectSchemaItem_test.cc b/src/components/smart_objects/test/CObjectSchemaItem_test.cc index 523b25a8d8..5a0dcefa10 100644 --- a/src/components/smart_objects/test/CObjectSchemaItem_test.cc +++ b/src/components/smart_objects/test/CObjectSchemaItem_test.cc @@ -44,6 +44,7 @@ #include "smart_objects/object_schema_item.h" #include "formatters/generic_json_formatter.h" #include "formatters/CSmartFactory.h" +#include "utils/semantic_version.h" namespace formatters = NsSmartDeviceLink::NsJSONHandler::Formatters; namespace smartobj = NsSmartDeviceLink::NsSmartObjects; @@ -136,6 +137,36 @@ class ObjectSchemaItemTest : public ::testing::Test { schemaMembersMap[Keys::SUCCESS] = CObjectSchemaItem::SMember(CBoolSchemaItem::create(), false); + // Create fake param that has breaking history changes + std::vector<CObjectSchemaItem::SMember> fake_param_history_vector; + + std::shared_ptr<ISchemaItem> fake_param_SchemaItem = + CArraySchemaItem::create( + CStringSchemaItem::create(TSchemaItemParameter<size_t>(1), + TSchemaItemParameter<size_t>(99), + TSchemaItemParameter<std::string>()), + TSchemaItemParameter<size_t>(1), + TSchemaItemParameter<size_t>(100)); + + std::shared_ptr<ISchemaItem> fake_param_history_v1_SchemaItem = + CArraySchemaItem::create( + CStringSchemaItem::create(TSchemaItemParameter<size_t>(1), + TSchemaItemParameter<size_t>(99), + TSchemaItemParameter<std::string>()), + TSchemaItemParameter<size_t>(1), + TSchemaItemParameter<size_t>(100)); + + fake_param_history_vector.push_back(CObjectSchemaItem::SMember( + fake_param_history_v1_SchemaItem, true, "", "4.5.0", false, false)); + schemaMembersMap["fakeParam"] = + CObjectSchemaItem::SMember(fake_param_SchemaItem, + false, + "4.5.0", + "", + false, + false, + fake_param_history_vector); + CObjectSchemaItem::Members rootMembersMap; rootMembersMap[S_PARAMS] = CObjectSchemaItem::SMember( CObjectSchemaItem::create(paramsMembersMap), true); @@ -160,6 +191,36 @@ TEST_F(ObjectSchemaItemTest, validation_correct) { EXPECT_EQ(std::string(""), rpc::PrettyFormat(report)); } +TEST_F(ObjectSchemaItemTest, validation_correct_with_new_version) { + SmartObject obj; + obj[S_PARAMS][S_FUNCTION_ID] = 0; + obj[S_PARAMS][S_CORRELATION_ID] = 0XFF0; + obj[S_PARAMS][S_PROTOCOL_VERSION] = 1; + obj[S_MSG_PARAMS][Keys::RESULT_CODE] = 0; + obj[S_MSG_PARAMS][Keys::INFO] = "0123456789"; + obj[S_MSG_PARAMS][Keys::SUCCESS] = true; + + utils::SemanticVersion messageVersion(4, 5, 0); + rpc::ValidationReport report("RPC"); + EXPECT_EQ(Errors::OK, schema_item->validate(obj, &report, messageVersion)); + EXPECT_EQ(std::string(""), rpc::PrettyFormat(report)); +} + +TEST_F(ObjectSchemaItemTest, validation_invalid_data_with_old_version) { + SmartObject obj; + obj[S_PARAMS][S_FUNCTION_ID] = 0; + obj[S_PARAMS][S_CORRELATION_ID] = 0XFF0; + obj[S_PARAMS][S_PROTOCOL_VERSION] = 1; + obj[S_MSG_PARAMS][Keys::RESULT_CODE] = 0; + obj[S_MSG_PARAMS][Keys::INFO] = "0123456789"; + obj[S_MSG_PARAMS][Keys::SUCCESS] = true; + + utils::SemanticVersion messageVersion(3, 0, 0); + rpc::ValidationReport report("RPC"); + EXPECT_EQ(Errors::MISSING_MANDATORY_PARAMETER, + schema_item->validate(obj, &report, messageVersion)); +} + TEST_F(ObjectSchemaItemTest, validation_correct_skip_not_mandatory) { SmartObject obj; obj[S_PARAMS][S_FUNCTION_ID] = 1; diff --git a/tools/InterfaceGenerator/MsgVersionGenerate.py b/tools/InterfaceGenerator/MsgVersionGenerate.py index 86251cd419..4a24a3239f 100644 --- a/tools/InterfaceGenerator/MsgVersionGenerate.py +++ b/tools/InterfaceGenerator/MsgVersionGenerate.py @@ -12,14 +12,25 @@ def generate_msg_version(file_name, path_to_storage): """ tree = xml.etree.ElementTree.parse(file_name) root = tree.getroot() - if (root.tag == "interface" and "version" in root.attrib): + if (root.tag == "interface" and "version" and "minVersion" in root.attrib): check_version_format(root.attrib["version"]) array = (root.attrib["version"]).split(".") major_version = array[0] minor_version = array[1] patch_version = array[2] - if (major_version.isdigit() and minor_version.isdigit() and patch_version.isdigit): - data_for_storage = prepare_data_for_storage(major_version, minor_version, patch_version) + + check_minimum_version_format(root.attrib["minVersion"]) + minimum_version_array = (root.attrib["minVersion"]).split(".") + if (len(minimum_version_array) == 2): + minimum_version_array.append("0") + minimum_major_version = minimum_version_array[0] + minimum_minor_version = minimum_version_array[1] + minimum_patch_version = minimum_version_array[2] + + if (major_version.isdigit() and minor_version.isdigit() and patch_version.isdigit() and + minimum_major_version.isdigit() and minimum_minor_version.isdigit() and minimum_patch_version.isdigit()): + data_for_storage = prepare_data_for_storage(major_version, minor_version, patch_version, + minimum_major_version, minimum_minor_version, minimum_patch_version) store_data_to_file(path_to_storage, data_for_storage) else: raise RPCBase.ParseError("Attribute version has incorect value in MOBILE_API.xml") @@ -45,7 +56,16 @@ def check_version_format(version): raise RPCBase.ParseError("Incorrect format of version please check MOBILE_API.xml. " "Need format of version major_version.minor_version.patch_version") -def prepare_data_for_storage(major_version, minor_version, patch_version): + +def check_minimum_version_format(version): + """Checks correctness of format of version + """ + p = re.compile('\d+\\.\d+\\.\d+|\d+\\.\d+') + result = p.match(version) + if result == None or (result.end() != len(version)): + raise RPCBase.ParseError("Incorrect format of version please check MOBILE_API.xml. " + "Need format of minVersion major_version.minor_version or major_version.minor_version.patch_version") +def prepare_data_for_storage(major_version, minor_version, patch_version, minimum_major_version, minimum_minor_version, minimum_patch_version): """Prepares data to store to file. """ temp = Template( @@ -80,8 +100,12 @@ def prepare_data_for_storage(major_version, minor_version, patch_version): u'''const uint16_t major_version = $m_version;\n''' u'''const uint16_t minor_version = $min_version;\n''' u'''const uint16_t patch_version = $p_version;\n''' + u'''const uint16_t minimum_major_version = $min_major_version;\n''' + u'''const uint16_t minimum_minor_version = $min_minor_version;\n''' + u'''const uint16_t minimum_patch_version = $min_patch_version;\n''' u'''} // namespace application_manager\n''' u'''#endif // GENERATED_MSG_VERSION_H''') - data_to_file = temp.substitute(m_version = major_version, min_version = minor_version, p_version = patch_version) + data_to_file = temp.substitute(m_version = major_version, min_version = minor_version, p_version = patch_version, + min_major_version = minimum_major_version, min_minor_version = minimum_minor_version, min_patch_version = minimum_patch_version) return data_to_file diff --git a/tools/InterfaceGenerator/generator/Model.py b/tools/InterfaceGenerator/generator/Model.py index ee458d934a..e7cce732ed 100755 --- a/tools/InterfaceGenerator/generator/Model.py +++ b/tools/InterfaceGenerator/generator/Model.py @@ -124,7 +124,8 @@ class InterfaceItemBase(object): """ def __init__(self, name, description=None, design_description=None, - issues=None, todos=None, platform=None, default_value=None, scope=None): + issues=None, todos=None, platform=None, default_value=None, scope=None, + since=None, until=None, deprecated=None, removed=None, history=None): self.name = name self.description = description if description is not None else [] self.design_description = \ @@ -134,6 +135,11 @@ class InterfaceItemBase(object): self.platform = platform self.default_value = default_value self.scope = scope + self.since = since + self.until = until + self.deprecated = deprecated + self.removed = removed + self.history = history class EnumElement(InterfaceItemBase): @@ -149,13 +155,18 @@ class EnumElement(InterfaceItemBase): def __init__(self, name, description=None, design_description=None, issues=None, todos=None, platform=None, internal_name=None, - value=None): + value=None, since=None, until=None, deprecated=None, removed=None, history=None): super(EnumElement, self).__init__( name, description=description, design_description=design_description, issues=issues, todos=todos, - platform=platform) + platform=platform, history=history) self.internal_name = internal_name self.value = value + self.since = since + self.until = until + self.deprecated = deprecated + self.removed = removed + @property def primary_name(self): @@ -180,15 +191,19 @@ class Enum(InterfaceItemBase): def __init__(self, name, description=None, design_description=None, issues=None, todos=None, platform=None, internal_scope=None, - elements=None, scope=None): + elements=None, scope=None, since=None, until=None, deprecated=None, removed=None, history=None): super(Enum, self).__init__( name, description=description, design_description=design_description, issues=issues, todos=todos, - platform=platform, scope=scope) + platform=platform, scope=scope, history=history) self.internal_scope = internal_scope self.elements = \ elements if elements is not None else collections.OrderedDict() + self.since = since + self.until = until + self.deprecated = deprecated + self.removed = removed class EnumSubset(InterfaceItemBase): @@ -204,15 +219,19 @@ class EnumSubset(InterfaceItemBase): def __init__(self, name, enum, description=None, design_description=None, issues=None, todos=None, platform=None, - allowed_elements=None): + allowed_elements=None, since=None, until=None, deprecated=None, removed=None, history=None): super(EnumSubset, self).__init__( name, description=description, design_description=design_description, issues=issues, todos=todos, - platform=platform) + platform=platform, history=history) self.enum = enum self.allowed_elements = \ allowed_elements if allowed_elements is not None else {} + self.since = since + self.until = until + self.deprecated = deprecated + self.removed = removed class Param(InterfaceItemBase): @@ -229,15 +248,20 @@ class Param(InterfaceItemBase): def __init__(self, name, param_type, description=None, design_description=None, issues=None, todos=None, - platform=None, is_mandatory=True, default_value=None, scope=None): + platform=None, is_mandatory=True, default_value=None, scope=None, + since=None, until=None, deprecated=None, removed=None, history=None): super(Param, self).__init__( name, description=description, design_description=design_description, issues=issues, todos=todos, - platform=platform, default_value=default_value, scope=scope) + platform=platform, default_value=default_value, scope=scope, history=history) self.is_mandatory = is_mandatory self.param_type = param_type self.default_value = default_value + self.since = since + self.until = until + self.deprecated = deprecated + self.removed=removed class FunctionParam(Param): @@ -251,11 +275,13 @@ class FunctionParam(Param): def __init__(self, name, param_type, description=None, design_description=None, issues=None, todos=None, - platform=None, is_mandatory=True, default_value=None, scope=None): + platform=None, is_mandatory=True, default_value=None, scope=None, + since=None, until=None, deprecated=None, removed=None, history=None): super(FunctionParam, self).__init__( name, param_type=param_type, description=description, design_description=design_description, issues=issues, todos=todos, - platform=platform, is_mandatory=is_mandatory, default_value=default_value, scope=scope) + platform=platform, is_mandatory=is_mandatory, default_value=default_value, + scope=scope, since=since, until=until, deprecated=deprecated, removed=removed, history=history) self.default_value = default_value @@ -270,11 +296,13 @@ class Struct(InterfaceItemBase): """ def __init__(self, name, description=None, design_description=None, - issues=None, todos=None, platform=None, members=None, scope=None): + issues=None, todos=None, platform=None, members=None, scope=None, + since=None, until=None, deprecated=None, removed=None, history=None): super(Struct, self).__init__( name, description=description, design_description=design_description, issues=issues, todos=todos, - platform=platform, scope=scope) + platform=platform, scope=scope, since=since, until=until, + deprecated=deprecated, removed=removed, history=history) self.members = \ members if members is not None else collections.OrderedDict() @@ -293,11 +321,11 @@ class Function(InterfaceItemBase): def __init__(self, name, function_id, message_type, description=None, design_description=None, issues=None, todos=None, - platform=None, params=None, scope=None): + platform=None, params=None, scope=None, since=None, until=None, deprecated=None, removed=None, history=None): super(Function, self).__init__( name, description=description, design_description=design_description, issues=issues, todos=todos, - platform=platform, scope=scope) + platform=platform, scope=scope, since=since, until=until, deprecated=deprecated, removed=removed, history=history) self.function_id = function_id self.message_type = message_type diff --git a/tools/InterfaceGenerator/generator/generators/SmartFactoryBase.py b/tools/InterfaceGenerator/generator/generators/SmartFactoryBase.py index 0c500dee8e..6546b0ea89 100755 --- a/tools/InterfaceGenerator/generator/generators/SmartFactoryBase.py +++ b/tools/InterfaceGenerator/generator/generators/SmartFactoryBase.py @@ -505,7 +505,6 @@ class CodeGenerator(object): String with structs implementation source code. """ - processed_enums = [] return self._struct_impl_template.substitute( namespace=namespace, @@ -518,9 +517,46 @@ class CodeGenerator(object): schema_items_decl=self._gen_schema_items_decls( struct.members.values()), schema_item_fill=self._gen_schema_items_fill( - struct.members.values())), + struct.members.values(), struct.since, struct.until, struct.deprecated, struct.removed)), 1)) + + def _enum_has_history_present(self, enum): + ''' + Check if any elements in an enum has history signature + ''' + for element in enum.param_type.elements.values(): + if ( element.history is not None or + element.since is not None or + element.until is not None or + element.removed is not None ): + return True + return False + + def _element_has_history_present(self, element): + ''' + Check if a specific element has a history signature + ''' + if ( element.history is not None or + element.since is not None or + element.until is not None or + element.removed is not None ): + return True + return False + + def _enum_param_type_has_history_present(self, param_type): + ''' + Check if any elements in an enum has history signature + ''' + for element in param_type.elements.values(): + if ( element.history is not None or + element.since is not None or + element.until is not None or + element.removed is not None ): + return True + return False + + def _gen_schema_loc_decls(self, members, processed_enums): """Generate local declarations of variables for schema. @@ -540,6 +576,7 @@ class CodeGenerator(object): for member in members: if type(member.param_type) is Model.Enum and \ member.param_type.name not in processed_enums: + has_history = self._enum_has_history_present(member) local_var = self._gen_schema_loc_emum_var_name( member.param_type) result = u"\n".join( @@ -555,6 +592,46 @@ class CodeGenerator(object): enum=member.param_type.name, value=x.primary_name) for x in member.param_type.elements.values()])]) + + if has_history == True: + history_result = u"\n" + history_result += self._impl_code_loc_decl_enum_history_set_template.substitute( + type=member.param_type.name) + history_result += u"\n" + history_result += u"\n".join( + [self._impl_code_loc_decl_enum_history_set_value_init_template.substitute( + enum=member.param_type.name, + value=x.primary_name) + for x in member.param_type.elements.values() if self._element_has_history_present(x)]) + history_result += u"\n" + history_map_result = [] + + for x in member.param_type.elements.values(): + if self._element_has_history_present(x): + history_map_result.append( + self._impl_code_loc_decl_enum_history_set_insert_template. + substitute( + enum=member.param_type.name, + value=x.primary_name, + since=x.since if x.since is not None else "", + until=x.until if x.until is not None else "", + removed=x.removed if x.removed is not None else u"false")) + if(x.history is not None) : + history_list = x.history + for item in history_list: + history_map_result.append( + self._impl_code_loc_decl_enum_history_set_insert_template. + substitute( + enum=member.param_type.name, + value=x.primary_name, + since=item.since if item.since is not None else "", + until=item.until if item.until is not None else "", + removed=item.removed if item.removed is not None else u"false")) + + history_result += u"\n".join(history_map_result) + result += "\n" + result += history_result + processed_enums.append(member.param_type.name) result = u"".join([result, u"\n\n"]) if result else u"" elif type(member.param_type) is Model.EnumSubset: @@ -599,10 +676,40 @@ class CodeGenerator(object): """ - result = u"\n\n".join( - [self._gen_schema_item_decl(x) for x in members]) + result_array = [] + for x in members: + result_array.append(self._gen_schema_item_decl(x)) + count = 0 + if x.history is not None: + history_list = x.history + for item in history_list: + item.name += "_history_v" + str(len(history_list)-count) + result_array.append(self._gen_schema_item_decl(item)) + count += 1 + result_array.append(self._gen_history_vector_decl(history_list, x.name)) + + result = u"\n\n".join(result_array) + return result + + def _gen_history_vector_decl(self, history_list, name): + """Generate History Vector Declaration. + + Generates the declaration and initialization + of a vector of schema items + + Arguments: + history_list -> list of history items + name -> name of parent parameter name + + Returns: + String with history array code. + """ + result_array = [] + result_array.append(self._impl_code_shared_ptr_vector_template.substitute(var_name = name)) + result = u"\n".join(result_array) + return result + - return u"".join([result, u"\n\n"]) if result else u"" def _gen_schema_item_decl(self, member): """Generate schema item declaration. @@ -696,16 +803,30 @@ class CodeGenerator(object): code = self._impl_code_struct_item_template.substitute( name=param.name) elif type(param) is Model.Enum: - code = self._impl_code_enum_item_template.substitute( - type=param.name, - params=u"".join( - [self._gen_schema_loc_emum_var_name(param), - u", ", - self._gen_schema_item_param_values( - [[u"".join([param.name, u"::eType"]), - u"".join([param.name, u"::", - default_value.primary_name]) if - default_value is not None else None]])])) + if self._enum_param_type_has_history_present(param): + code = self._impl_code_enum_item_with_history_template.substitute( + type=param.name, + params=u"".join( + [self._gen_schema_loc_emum_var_name(param), + u", ", + self._impl_gen_schema_enum_history_map_template.substitute(name=param.name), + u", ", + self._gen_schema_item_param_values( + [[u"".join([param.name, u"::eType"]), + u"".join([param.name, u"::", + default_value.primary_name]) if + default_value is not None else None]])])) + else: + code = self._impl_code_enum_item_template.substitute( + type=param.name, + params=u"".join( + [self._gen_schema_loc_emum_var_name(param), + u", ", + self._gen_schema_item_param_values( + [[u"".join([param.name, u"::eType"]), + u"".join([param.name, u"::", + default_value.primary_name]) if + default_value is not None else None]])])) elif type(param) is Model.EnumSubset: code = self._impl_code_enum_item_template.substitute( type=param.enum.name, @@ -746,7 +867,7 @@ class CodeGenerator(object): return result - def _gen_schema_items_fill(self, members): + def _gen_schema_items_fill(self, members=None, since=None, until=None, deprecated=None, removed=None): """Generate schema items fill code. Generates source code that fills new schema with items. @@ -758,10 +879,16 @@ class CodeGenerator(object): String with function schema items fill code. """ - - result = u"\n".join( - [self._gen_schema_item_fill(x) for x in members]) - + result_array = [] + for x in members: + #If history, create Smember History vector first + if x.history is not None: + history_list = x.history + for item in history_list: + result_array.append(self._gen_history_vector_item_fill(item, x.name)) + result_array.append(self._gen_schema_item_fill(x, since, until, deprecated, removed)) + + result = u"\n".join(result_array) return u"".join([result, u"\n\n"]) if result else u"" def _gen_schema_params_fill(self, message_type_name): @@ -781,7 +908,34 @@ class CodeGenerator(object): raise GenerateError("Unexpected call to the unimplemented function.") - def _gen_schema_item_fill(self, member): + def _check_member_history(self, member): + """ + Checks set of rules that history items are valid + Raises error if rules are violated + """ + if (member.since is None and + member.until is None and + member.deprecated is None and + member.removed is None and + member.history is None): + return + if (member.history is not None and member.since is None): + raise GenerateError("Error: Missing since version parameter for " + member.name) + if (member.until is not None): + raise GenerateError("Error: Until should only exist in history tag for " + member.name) + if (member.history is None): + if(member.until is not None or + member.deprecated is not None or + member.removed is not None): + raise GenerateError("Error: No history present for " + member.name) + if (member.deprecated is not None and member.removed is not None): + raise GenerateError("Error: Deprecated and removed should not be present together for " + member.name) + if(member.history is not None): + for item in member.history: + if item.since is None or item.until is None: + raise GenerateError("Error: History items require since and until parameters for " + member.name) + + def _gen_schema_item_fill(self, member, since, until, deprecated, removed): """Generate schema item fill code. Generates source code that fills new schema with item. @@ -793,11 +947,62 @@ class CodeGenerator(object): String with schema item fill code. """ + self._check_member_history(member) + + if (since is not None or + member.since is not None): + if member.history is not None: + return self._impl_code_item_fill_template_with_version_and_history_vector.substitute( + name=member.name, + var_name=self._gen_schema_item_var_name(member), + is_mandatory=u"true" if member.is_mandatory is True else u"false", + since=member.since if member.since is not None else since if since is not None else "", + until=member.until if member.until is not None else until if until is not None else "", + deprecated=member.deprecated if member.deprecated is not None else deprecated if deprecated is not None else u"false", + removed=member.removed if member.removed is not None else removed if removed is not None else u"false", + vector_name=member.name) + else: + return self._impl_code_item_fill_template_with_version.substitute( + name=member.name, + var_name=self._gen_schema_item_var_name(member), + is_mandatory=u"true" if member.is_mandatory is True else u"false", + since=member.since if member.since is not None else since if since is not None else "", + until=member.until if member.until is not None else until if until is not None else "", + deprecated=member.deprecated if member.deprecated is not None else deprecated if deprecated is not None else u"false", + removed=member.removed if member.removed is not None else removed if removed is not None else u"false") + else: + return self._impl_code_item_fill_template.substitute( + name=member.name, + var_name=self._gen_schema_item_var_name(member), + is_mandatory=u"true" if member.is_mandatory is True else u"false") - return self._impl_code_item_fill_template.substitute( - name=member.name, - var_name=self._gen_schema_item_var_name(member), - is_mandatory=u"true" if member.is_mandatory is True else u"false") + def _gen_history_vector_item_fill(self, member, vector_name): + """Generate schema item fill code. + + Generates source code that fills history vector with item. + + Keyword arguments: + member -- struct member/function parameter to process. + + Returns: + String with schema item fill code. + + """ + + if (member.since is not None or + member.until is not None or + member.deprecated is not None or + member.removed is not None): + return self._impl_code_append_history_vector_template.substitute( + vector_name=vector_name, + name=member.name, + mandatory=u"true" if member.is_mandatory is True else u"false", + since=member.since if member.since is not None else "", + until=member.until if member.until is not None else "", + deprecated=member.deprecated if member.deprecated is not None else u"false", + removed=member.removed if member.removed is not None else u"false") + else: + print "Warning! History item does not have any version history. Omitting %s" % member.name @staticmethod def _gen_schema_item_var_name(member): @@ -899,7 +1104,7 @@ class CodeGenerator(object): schema_items_decl=self._gen_schema_items_decls( function.params.values()), schema_item_fill=self._gen_schema_items_fill( - function.params.values()), + function.params.values(), function.since, function.until, function.deprecated, function.removed), schema_params_fill=self._gen_schema_params_fill( function.message_type.name)), 1)) @@ -1498,13 +1703,32 @@ class CodeGenerator(object): _impl_code_loc_decl_enum_template = string.Template( u'''std::set<${type}::eType> ${var_name};''') + _impl_code_loc_decl_enum_history_set_template = string.Template( + u'''std::map<${type}::eType, std::vector<ElementSignature>> ${type}_element_signatures;''') + _impl_code_loc_decl_enum_insert_template = string.Template( u'''${var_name}.insert(${enum}::${value});''') + _impl_code_loc_decl_enum_history_set_value_init_template = string.Template( + u'''${enum}_element_signatures[${enum}::${value}] = std::vector<ElementSignature>();''' + ) + + _impl_code_loc_decl_enum_history_set_insert_template = string.Template( + u'''${enum}_element_signatures[${enum}::${value}].push_back(ElementSignature("${since}", "${until}", ${removed}));''') + + _impl_gen_schema_enum_history_map_template = string.Template( + u'''${name}_element_signatures''') + _impl_code_item_decl_temlate = string.Template( u'''${comment}''' u'''std::shared_ptr<ISchemaItem> ${var_name} = ${item_decl};''') + _impl_code_shared_ptr_vector_template = string.Template( + u'''std::vector<CObjectSchemaItem::SMember> ${var_name}_history_vector;''') + + _impl_code_append_history_vector_template = string.Template( + u'''${vector_name}_history_vector.push_back(CObjectSchemaItem::SMember(${name}_SchemaItem, ${mandatory}, "${since}", "${until}", ${deprecated}, ${removed}));''') + _impl_code_integer_item_template = string.Template( u'''TNumberSchemaItem<${type}>::create(${params})''') @@ -1524,6 +1748,9 @@ class CodeGenerator(object): _impl_code_enum_item_template = string.Template( u'''TEnumSchemaItem<${type}::eType>::create(${params})''') + _impl_code_enum_item_with_history_template = string.Template( + u'''TEnumSchemaItem<${type}::eType>::createWithSignatures(${params})''') + _impl_code_item_param_value_template = string.Template( u'''TSchemaItemParameter<$type>($value)''') @@ -1531,6 +1758,14 @@ class CodeGenerator(object): u'''schema_members["${name}"] = CObjectSchemaItem::''' u'''SMember(${var_name}, ${is_mandatory});''') + _impl_code_item_fill_template_with_version = string.Template( + u'''schema_members["${name}"] = CObjectSchemaItem::''' + u'''SMember(${var_name}, ${is_mandatory}, "${since}", "${until}", ${deprecated}, ${removed});''') + + _impl_code_item_fill_template_with_version_and_history_vector = string.Template( + u'''schema_members["${name}"] = CObjectSchemaItem::''' + u'''SMember(${var_name}, ${is_mandatory}, "${since}", "${until}", ${deprecated}, ${removed}, ${vector_name}_history_vector);''') + _function_impl_template = string.Template( u'''CSmartSchema $namespace::$class_name::''' u'''InitFunction_${function_id}_${message_type}(\n''' diff --git a/tools/InterfaceGenerator/generator/parsers/RPCBase.py b/tools/InterfaceGenerator/generator/parsers/RPCBase.py index 4853916dad..21f07e6ac5 100755 --- a/tools/InterfaceGenerator/generator/parsers/RPCBase.py +++ b/tools/InterfaceGenerator/generator/parsers/RPCBase.py @@ -6,7 +6,7 @@ Contains base parser for SDLRPC v1/v2 and JSON RPC XML format. import collections import xml.etree.ElementTree - +import re from generator import Model @@ -203,16 +203,35 @@ class Parser(object): internal_scope = None scope = None + since = None + until = None + deprecated = None + removed = None + result = None for attribute in attributes: if attribute == "internal_scope": internal_scope = attributes[attribute] elif attribute == "scope": scope = attributes[attribute] + elif attribute == "since": + result = self._parse_version(attributes[attribute]) + since = result + elif attribute == "until": + result = self._parse_version(attributes[attribute]) + until = result + elif attribute == "deprecated": + deprecated = attributes[attribute] + elif attribute == "removed": + removed = attributes[attribute] else: raise ParseError("Unexpected attribute '" + attribute + "' in enum '" + params["name"] + "'") params["internal_scope"] = internal_scope params["scope"] = scope + params["since"] = since + params["until"] = until + params["deprecated"] = deprecated + params["removed"] = removed elements = collections.OrderedDict() for subelement in subelements: @@ -236,13 +255,32 @@ class Parser(object): params, subelements, attrib = self._parse_base_item(element, prefix) scope = None + since = None + until = None + deprecated = None + removed = None + result = None for attribute in attrib: if attribute == "scope": scope = attrib[attribute] + elif attribute == "since": + result = self._parse_version(attrib[attribute]) + since = result + elif attribute == "until": + result = self._parse_version(attrib[attribute]) + until = result + elif attribute == "deprecated": + deprecated = attributes[attribute] + elif attribute == "removed": + removed = attrib[attribute] else: raise ParseError("Unexpected attribute '" + attribute + "' in struct '" + params["name"] + "'") params["scope"] = scope + params["since"] = since + params["until"] = until + params["deprecated"] = deprecated + params["removed"] = removed members = collections.OrderedDict() for subelement in subelements: @@ -271,13 +309,32 @@ class Parser(object): attributes) scope = None + since = None + until = None + deprecated = None + removed = None + result = None for attribute in attributes: if attribute == "scope": scope = attributes[attribute] + elif attribute == "since": + result = self._parse_version(attributes[attribute]) + since = result + elif attribute == "until": + result = self._parse_version(attributes[attribute]) + until = result + elif attribute == "deprecated": + deprecated = attributes[attribute] + elif attribute == "removed": + removed = attributes[attribute] params["function_id"] = function_id params["message_type"] = message_type params["scope"] = scope + params["since"] = since + params["until"] = until + params["deprecated"] = deprecated + params["removed"] = removed function_params = collections.OrderedDict() for subelement in subelements: @@ -359,6 +416,8 @@ class Parser(object): issues = [] todos = [] subelements = [] + history = None + warnings = [] if "name" not in element.attrib: raise ParseError("Name is not specified for " + element.tag) @@ -379,6 +438,12 @@ class Parser(object): todos.append(self._parse_simple_element(subelement)) elif subelement.tag == "issue": issues.append(self._parse_issue(subelement)) + elif subelement.tag == "history": + if history is not None: + raise ParseError("Elements can only have one history tag: " + element.tag) + history = self._parse_history(subelement, prefix, element) + elif subelement.tag == "warning": + warnings.append(self._parse_simple_element(subelement)) else: subelements.append(subelement) @@ -386,6 +451,7 @@ class Parser(object): params["design_description"] = design_description params["issues"] = issues params["todos"] = todos + params["history"] = history return params, subelements, attrib @@ -443,6 +509,11 @@ class Parser(object): internal_name = None value = None + since = None + until = None + deprecated = None + removed = None + result = None for attribute in attributes: if attribute == "internal_name": internal_name = attributes[attribute] @@ -452,9 +523,22 @@ class Parser(object): except: raise ParseError("Invalid value for enum element: '" + attributes[attribute] + "'") + elif attribute == "since": + result = self._parse_version(attributes[attribute]) + since = result + elif attribute == "until": + result = self._parse_version(attributes[attribute]) + until = result + elif attribute == "deprecated": + deprecated = attributes[attribute] + elif attribute == "removed": + removed = attributes[attribute] params["internal_name"] = internal_name params["value"] = value - + params["since"] = since + params["until"] = until + params["deprecated"] = deprecated + params["removed"] = removed # Magic usage is correct # pylint: disable=W0142 return Model.EnumElement(**params) @@ -548,6 +632,25 @@ class Parser(object): """ params, subelements, attrib = self._parse_base_item(element, "") + since_version = self._extract_attrib(attrib, "since") + if since_version is not None: + result = self._parse_version(since_version) + params["since"] = result + + until_version = self._extract_attrib(attrib, "until") + if until_version is not None: + result = self._parse_version(until_version) + params["until"] = result + + deprecated = self._extract_attrib(attrib, "deprecated") + if deprecated is not None: + params["deprecated"] = deprecated + + removed = self._extract_attrib(attrib, "removed") + if removed is not None: + params["removed"] = removed + + is_mandatory = self._extract_attrib(attrib, "mandatory") if is_mandatory is None: raise ParseError("'mandatory' is not specified for parameter '" + @@ -765,3 +868,48 @@ class Parser(object): print ("Ignoring attribute '" + name + "'") return True + + def _parse_version(self, version): + """ + Validates if a version supplied is in the correct + format of Major.Minor.Patch. If Major.Minor format + is supplied, a patch version of 0 will be added to + the end. + """ + p = re.compile('\d+\\.\d+\\.\d+|\d+\\.\d+') + result = p.match(version) + if result == None or (result.end() != len(version)): + raise RPCBase.ParseError("Incorrect format of version please check MOBILE_API.xml. " + "Need format of major_version.minor_version or major_version.minor_version.patch_version") + + version_array = version.split(".") + if (len(version_array) == 2): + version_array.append("0") + dot_str = "." + return dot_str.join(version_array) + + def _parse_history(self, history, prefix, parent): + if history.tag != "history": + raise ParseError("Invalid history tag: " + interface.tag) + + items = [] + + for subelement in history: + if subelement.tag == "enum" and parent.tag == "enum": + items.append(self._parse_enum(subelement, prefix)) + elif subelement.tag == "element" and parent.tag == "element": + items.append(self._parse_enum_element(subelement)) + elif subelement.tag == "description" and parent.tag == "description": + items.append(self._parse_simple_element(subelement)) + elif subelement.tag == "struct" and parent.tag == "struct": + items.append(self._parse_struct(subelement, prefix)) + elif subelement.tag == "param" and parent.tag == "param": + items.append(self._parse_function_param(subelement, prefix)) + elif subelement.tag == "function" and parent.tag == "function": + items.append(self.__parse_function(subelement, prefix)) + else: + raise ParseError("A history tag must be nested within the element it notes the history for. Fix item: '" + + parent.attrib["name"] + "'") + + return items + |