diff --git a/CMakeLists.txt b/CMakeLists.txt index a5509bd..5ed2221 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ add_subdirectory (src) add_subdirectory (tests) if(PERFORMANCE_TESTS) - add_subdirectory (performance) + #add_subdirectory (performance) endif() if(BUILD_EXAMPLES) diff --git a/performance/testApi/modules/api_module/api/generated/olink/testapiclient.cpp b/performance/testApi/modules/api_module/api/generated/olink/testapiclient.cpp index b6ed1bc..c5cbc51 100644 --- a/performance/testApi/modules/api_module/api/generated/olink/testapiclient.cpp +++ b/performance/testApi/modules/api_module/api/generated/olink/testapiclient.cpp @@ -126,7 +126,7 @@ std::future TestApiClient::funcIntAsync(int paramInt) static const auto operationId = ApiGear::ObjectLink::Name::createMemberId(olinkObjectName(), "funcInt"); m_node->invokeRemote(operationId, nlohmann::json::array({paramInt}), [&resultPromise](ApiGear::ObjectLink::InvokeReplyArg arg) { - const int& value = arg.value.get(); + const int& value = arg.value.content.get(); resultPromise.set_value(value); }); return resultPromise.get_future().get(); @@ -157,7 +157,7 @@ std::future TestApiClient::funcFloatAsync(float paramFloat) static const auto operationId = ApiGear::ObjectLink::Name::createMemberId(olinkObjectName(), "funcFloat"); m_node->invokeRemote(operationId, nlohmann::json::array({paramFloat}), [&resultPromise](ApiGear::ObjectLink::InvokeReplyArg arg) { - const float& value = arg.value.get(); + const float& value = arg.value.content.get(); resultPromise.set_value(value); }); return resultPromise.get_future().get(); @@ -188,7 +188,7 @@ std::future TestApiClient::funcStringAsync(const std::string& param static const auto operationId = ApiGear::ObjectLink::Name::createMemberId(olinkObjectName(), "funcString"); m_node->invokeRemote(operationId, nlohmann::json::array({paramString}), [&resultPromise](ApiGear::ObjectLink::InvokeReplyArg arg) { - const std::string& value = arg.value.get(); + const std::string& value = arg.value.content.get(); resultPromise.set_value(value); }); return resultPromise.get_future().get(); diff --git a/src/olink/clientnode.cpp b/src/olink/clientnode.cpp index 4c99a83..e307178 100644 --- a/src/olink/clientnode.cpp +++ b/src/olink/clientnode.cpp @@ -28,7 +28,15 @@ void ClientNode::linkRemote(const std::string& objectId) { static const std::string linkRemoteLog = "ClientNode.linkRemote: "; emitLog(LogLevel::Info, linkRemoteLog, objectId); - emitWrite(Protocol::linkMessage(objectId)); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::linkMessage(*writer, objectId)); + } + } m_registry.unsetNode(objectId); m_registry.setNode(m_nodeId, objectId); } @@ -41,11 +49,19 @@ void ClientNode::unlinkRemote(const std::string& objectId) if (sink){ sink->olinkOnRelease(); } - emitWrite(Protocol::unlinkMessage(objectId)); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::unlinkMessage(*writer, objectId)); + } + } m_registry.unsetNode(objectId); } -void ClientNode::invokeRemote(const std::string& methodId, const nlohmann::json& args, InvokeReplyFunc func) +void ClientNode::invokeRemote(const std::string& methodId, const OLinkContent& args, InvokeReplyFunc func) { static const std::string invokeRemoteLog = "ClientNode.invokeRemote: "; emitLog(LogLevel::Info, invokeRemoteLog, methodId); @@ -53,16 +69,30 @@ void ClientNode::invokeRemote(const std::string& methodId, const nlohmann::json& std::unique_lock lock(m_pendingInvokesMutex); m_invokesPending[requestId] = func; lock.unlock(); - nlohmann::json msg = Protocol::invokeMessage(requestId, methodId, args); - emitWrite(msg); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::invokeMessage(*writer, requestId, methodId, args)); + } + } } -void ClientNode::setRemoteProperty(const std::string& propertyId, const nlohmann::json& value) +void ClientNode::setRemoteProperty(const std::string& propertyId, const OLinkContent& value) { static const std::string setRemotePropertyLog = "ClientNode.setRemoteProperty: "; emitLog(LogLevel::Info, setRemotePropertyLog, propertyId); - nlohmann::json msg = Protocol::setPropertyMessage(propertyId, value); - emitWrite(msg); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::setPropertyMessage(*writer, propertyId, value)); + } + } } ClientRegistry& ClientNode::registry() @@ -85,7 +115,7 @@ unsigned long ClientNode::getNodeId() const return m_nodeId; } -void ClientNode::handleInit(const std::string& objectId, const nlohmann::json& props) +void ClientNode::handleInit(const std::string& objectId, const OLinkContent& props) { static const std::string handeInitLog = "ClientNode.handleInit: "; emitLogWithPayload(LogLevel::Info, props, handeInitLog, objectId); @@ -99,7 +129,7 @@ void ClientNode::handleInit(const std::string& objectId, const nlohmann::json& p } } -void ClientNode::handlePropertyChange(const std::string& propertyId, const nlohmann::json& value) +void ClientNode::handlePropertyChange(const std::string& propertyId, const OLinkContent& value) { static const std::string handlePropertyChangedlog = "ClientNode.handlePropertyChange: "; emitLogWithPayload(LogLevel::Info, value, handlePropertyChangedlog, propertyId); @@ -113,7 +143,7 @@ void ClientNode::handlePropertyChange(const std::string& propertyId, const nlohm } } -void ClientNode::handleInvokeReply(int requestId, const std::string& methodId, const nlohmann::json& value) +void ClientNode::handleInvokeReply(int requestId, const std::string& methodId, const OLinkContent& value) { static const std::string handleInvokeLog = "ClientNode.handleInvokeReply: "; emitLogWithPayload(LogLevel::Info, value, handleInvokeLog, methodId); @@ -135,7 +165,7 @@ void ClientNode::handleInvokeReply(int requestId, const std::string& methodId, c } } -void ClientNode::handleSignal(const std::string& signalId, const nlohmann::json& args) +void ClientNode::handleSignal(const std::string& signalId, const OLinkContent& args) { static const std::string handleSignalLog ="ClientNode.handleSignal: "; emitLog(LogLevel::Info, handleSignalLog, signalId); @@ -148,10 +178,10 @@ void ClientNode::handleSignal(const std::string& signalId, const nlohmann::json& } } -void ClientNode::handleError(int msgType, int requestId, const std::string& error) +void ClientNode::handleError(MsgType msgType, int requestId, const std::string& error) { static const std::string errorLog = "ClientNode.handleError: "; - emitLog(LogLevel::Info, errorLog, std::to_string(msgType), std::to_string(requestId), error); + emitLog(LogLevel::Info, errorLog, toString(msgType), std::to_string(requestId), error); } int ClientNode::nextRequestId() diff --git a/src/olink/clientnode.h b/src/olink/clientnode.h index e4514f8..e9c2106 100644 --- a/src/olink/clientnode.h +++ b/src/olink/clientnode.h @@ -1,6 +1,7 @@ #pragma once #include "core/olink_common.h" +#include "core/olinkcontent.h" #include "core/types.h" #include "iclientnode.h" #include "core/basenode.h" @@ -55,9 +56,9 @@ class OLINK_EXPORT ClientNode : public BaseNode, public IClientNode, public std: /** IClientNode::unlinkRemote implementation. */ void unlinkRemote(const std::string& objectId) override; /** IClientNode::invokeRemote implementation. */ - void invokeRemote(const std::string& methodId, const nlohmann::json& args=nlohmann::json{}, InvokeReplyFunc func=nullptr) override; + void invokeRemote(const std::string& methodId, const OLinkContent& args= OLinkContent(), InvokeReplyFunc func=nullptr) override; /** IClientNode::setRemoteProperty implementation. */ - void setRemoteProperty(const std::string& propertyId, const nlohmann::json& value) override; + void setRemoteProperty(const std::string& propertyId, const OLinkContent& value) override; /* The registry in which client is registered*/ ClientRegistry& registry(); @@ -70,15 +71,15 @@ class OLINK_EXPORT ClientNode : public BaseNode, public IClientNode, public std: protected: /** IProtocolListener::handleInit implementation */ - void handleInit(const std::string& objectId, const nlohmann::json& props) override; + void handleInit(const std::string& objectId, const OLinkContent& props) override; /** IProtocolListener::handlePropertyChange implementation */ - void handlePropertyChange(const std::string& propertyId, const nlohmann::json& value) override; + void handlePropertyChange(const std::string& propertyId, const OLinkContent& value) override; /** IProtocolListener::handleInvokeReply implementation */ - void handleInvokeReply(int requestId, const std::string& methodId, const nlohmann::json& value) override; + void handleInvokeReply(int requestId, const std::string& methodId, const OLinkContent& value) override; /** IProtocolListener::handleSignal implementation */ - void handleSignal(const std::string& signalId, const nlohmann::json& args) override; + void handleSignal(const std::string& signalId, const OLinkContent& args) override; /** IProtocolListener::handleError implementation */ - void handleError(int msgType, int requestId, const std::string& error) override; + void handleError(MsgType msgType, int requestId, const std::string& error) override; /** * Returns a request id for outgoing messages. diff --git a/src/olink/core/basenode.cpp b/src/olink/core/basenode.cpp index 28d3965..5c793c0 100644 --- a/src/olink/core/basenode.cpp +++ b/src/olink/core/basenode.cpp @@ -8,30 +8,29 @@ namespace static const std::string notImplementedLog = "not implemented "; } -void BaseNode::onWrite(WriteMessageFunc func) +void BaseNode::onWrite(WriteMessageFunc func, std::shared_ptr serializer) { m_writeFunc = func; + m_serializer = serializer; } -void BaseNode::emitWrite(const nlohmann::json& msg) +void BaseNode::emitWrite(const OLinkMessage& msg) { static const std::string writeMessageLog = "writeMessage: "; emitLogWithPayload(LogLevel::Debug, msg, writeMessageLog); - if(m_writeFunc) { - m_writeFunc(m_converter.toString(msg)); + if(m_writeFunc && m_serializer) { + m_writeFunc(m_serializer->toNetworkFormat(msg)); } else { static const std::string noWriterSetLog = "Messages are not sent if the write function is not set"; emitLog(LogLevel::Warning, noWriterSetLog); } } -void BaseNode::setMessageFormat(MessageFormat format) -{ - m_converter.setMessageFormat(format); -} void BaseNode::handleMessage(const std::string& data) { - m_protocol.handleMessage(m_converter.fromString(data), *this); + auto msg = m_serializer->fromNetworkFormat(data); + auto deserializer = m_serializer->createReader(msg); + m_protocol.handleMessage(*(deserializer.get()), *this); } void BaseNode::handleLink(const std::string& objectId) @@ -44,39 +43,44 @@ void BaseNode::handleUnlink(const std::string& objectId) emitLog(LogLevel::Warning, notImplementedLog, std::string(__func__), objectId); } -void BaseNode::handleInvoke(int, const std::string& methodId, const nlohmann::json& args) +void BaseNode::handleInvoke(int, const std::string& methodId, const OLinkContent& args) { emitLogWithPayload(LogLevel::Warning, args, notImplementedLog, std::string(__func__), methodId, " args "); } -void BaseNode::handleSetProperty(const std::string& propertyId, const nlohmann::json& value) +void BaseNode::handleSetProperty(const std::string& propertyId, const OLinkContent& value) { emitLogWithPayload(LogLevel::Warning, value, notImplementedLog, std::string(__func__), propertyId, " value "); } -void BaseNode::handleInit(const std::string& objectId, const nlohmann::json& props) +void BaseNode::handleInit(const std::string& objectId, const OLinkContent& props) { emitLogWithPayload(LogLevel::Warning, props, notImplementedLog, std::string(__func__), objectId, " props "); } -void BaseNode::handleInvokeReply(int requestId, const std::string& methodId, const nlohmann::json& value) +void BaseNode::handleInvokeReply(int requestId, const std::string& methodId, const OLinkContent& value) { - emitLog(LogLevel::Warning, notImplementedLog, std::string(__func__), methodId, " requestId ", std::to_string(requestId), " value ", value); + emitLogWithPayload(LogLevel::Warning, value, notImplementedLog, std::string(__func__), methodId, " requestId ", std::to_string(requestId), " value "); } -void BaseNode::handleSignal(const std::string& signalId, const nlohmann::json& args) +void BaseNode::handleSignal(const std::string& signalId, const OLinkContent& args) { emitLogWithPayload(LogLevel::Warning, args, notImplementedLog, std::string(__func__), signalId, " args "); } -void BaseNode::handlePropertyChange(const std::string& propertyId, const nlohmann::json& value) +void BaseNode::handlePropertyChange(const std::string& propertyId, const OLinkContent& value) { emitLogWithPayload(LogLevel::Warning, value, notImplementedLog, std::string(__func__), propertyId, " value "); } -void BaseNode::handleError(int, int requestId, const std::string& error) +void BaseNode::handleError(MsgType, int requestId, const std::string& error) { emitLog(LogLevel::Warning, notImplementedLog, std::string(__func__), " requestId ", std::to_string(requestId), " error ", error); } +std::shared_ptr BaseNode::getSerializer() +{ + return m_serializer; +} + } } // ApiGear::ObjectLink diff --git a/src/olink/core/basenode.h b/src/olink/core/basenode.h index c92aeef..a15f250 100644 --- a/src/olink/core/basenode.h +++ b/src/olink/core/basenode.h @@ -3,8 +3,8 @@ #include "protocol.h" #include "types.h" #include "olink_common.h" -#include "nlohmann/json.hpp" #include +#include "imessageserializer.h" namespace ApiGear { namespace ObjectLink { @@ -21,18 +21,13 @@ class OLINK_EXPORT BaseNode: public LoggerBase, * Network layer implementation should deliver this function, * with which messages are sent through network. */ - void onWrite(WriteMessageFunc func); + void onWrite(WriteMessageFunc func, std::shared_ptr serializer); /** * Use this function to format message and send it through the network. * It uses the WriteMessageFunc provided by network layer implementation with onWrite(WriteMessageFunc) call. * @param j The data to send, translated according to chosen network message format before sending. */ - virtual void emitWrite(const nlohmann::json& j); - - /** - * Use to change messages network format. - */ - void setMessageFormat(MessageFormat format); + virtual void emitWrite(const OLinkMessage& j); // Implementation::IMessageHandler void handleMessage(const std::string& data) override; @@ -42,24 +37,25 @@ class OLINK_EXPORT BaseNode: public LoggerBase, // Empty, logging only implementation of IProtocolListener::handleUnlink, should be overwritten on server side. void handleUnlink(const std::string& objectId) override; // Empty, logging only implementation of IProtocolListener::handleInvoke, should be overwritten on server side. - void handleInvoke(int requestId, const std::string& methodId, const nlohmann::json& args) override; + void handleInvoke(int requestId, const std::string& methodId, const OLinkContent& args) override; // Empty, logging only implementation of IProtocolListener::handleSetProperty, should be overwritten on server side. - void handleSetProperty(const std::string& propertyId, const nlohmann::json& value) override; + void handleSetProperty(const std::string& propertyId, const OLinkContent& value) override; // Empty, logging only implementation of IProtocolListener::handleInit, should be overwritten on client side. - void handleInit(const std::string& objectId, const nlohmann::json& props) override; + void handleInit(const std::string& objectId, const OLinkContent& props) override; // Empty, logging only implementation of IProtocolListener::handleInvokeReply, should be overwritten on client side. - void handleInvokeReply(int requestId, const std::string& methodId, const nlohmann::json& value) override; + void handleInvokeReply(int requestId, const std::string& methodId, const OLinkContent& value) override; // Empty, logging only implementation of IProtocolListener::handleSignal, should be overwritten on client side. - void handleSignal(const std::string& signalId, const nlohmann::json& args) override; + void handleSignal(const std::string& signalId, const OLinkContent& args) override; // Empty, logging only implementation of IProtocolListener::handlePropertyChange, should be overwritten on client side. - void handlePropertyChange(const std::string& propertyId, const nlohmann::json& value) override; + void handlePropertyChange(const std::string& propertyId, const OLinkContent& value) override; // Empty, logging only implementation of IProtocolListener::handleError, should be overwritten on both client and server side. - void handleError(int msgType, int requestId, const std::string& error) override; + void handleError(MsgType msgType, int requestId, const std::string& error) override; + std::shared_ptr getSerializer(); private: /** Function with which messages are sent through network after translation to chosen network format */ WriteMessageFunc m_writeFunc = nullptr; - /** A message converter, translates messages to and from chosen network format*/ - MessageConverter m_converter = MessageFormat::JSON; + /** A message serializer, that specifies how to encode and decode messages. Used by protocol. */ + std::shared_ptr m_serializer; /** ObjectLink protocol*/ Protocol m_protocol; }; diff --git a/src/olink/core/defaultcontentserializer.h b/src/olink/core/defaultcontentserializer.h new file mode 100644 index 0000000..fa723f2 --- /dev/null +++ b/src/olink/core/defaultcontentserializer.h @@ -0,0 +1,158 @@ +/* +* MIT License +* +* Copyright (c) 2021 ApiGear +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#pragma once + +#include "olink_common.h" +#include "nlohmann/json.hpp" +#include "olinkcontent.h" +#include + +namespace ApiGear { +namespace ObjectLink { + + +inline void from_json(const nlohmann::json& j, InitialProperty& p) +{ + p.propertyName = j[0].get(); + p.propertyValue.content = j[1]; +} +inline void to_json(nlohmann::json& j, const InitialProperty& p) +{ + j = nlohmann::json{ p.propertyName, p.propertyValue.content }; +} + +// last call of recursive fillContent(nlohmann::json::array& content_array, T const& first, Parameters const&... rest) +inline void fillContent(nlohmann::json& content_array, size_t current) +{ + (void)content_array; + (void)current; +} + +template +void fillContent(nlohmann::json& content_array, size_t current, const T& first, const Parameters&... rest) +{ + content_array[current] = first; + current++; + fillContent(content_array, current, rest...); +} + + +namespace NlohmannSerializer +{ + template + InitialProperty toInitialProperty(const std::string& name, const ArgType& arg) + { + InitialProperty prop; + prop.propertyName = name; + prop.propertyValue.content = arg; + return prop; + } + + + template + void fromInitialProperty(const InitialProperty& input, ValueType& target) + { + target = input.propertyValue.content.get(); + } + + + + namespace Value + { + + template + OLinkContent serialize(const ArgType& arg) + { + OLinkContent output = {}; + output.content = nlohmann::json(arg); + return output; + } + + template + void deserialize(const OLinkContent& input, ValueType& target) + { + target = input.content.get(); + } + + + } //namespace Value + + namespace Arguments + { + // Use to write arguments for: notifySignal, invoke, even if there is a single argument + template + OLinkContent serialize(const Parameters& ...inputs) + { + OLinkContent content = {}; + content.content = nlohmann::json::array(); + auto inputs_size = sizeof...(inputs); + content.content.get_ptr()->reserve(inputs_size); + size_t current = 0; + fillContent(content.content, current, inputs...); + return content; + } + + inline size_t argumentsCount(OLinkContent& content) + { + return content.content.size(); + } + + // Use to read for arguments: notifySignal, invoke + struct Deserializer + { + Deserializer(const OLinkContent& content) + :m_content(content) + {} + + template + void getNext(ArgType& arg) + { + arg = m_content.content[currentIndex].get(); + currentIndex++; + } + + bool hasNextElement() + { + return currentIndex < m_content.content.size(); + } + + size_t argumentsCount() + { + return m_content.content.size(); + } + + private: + const OLinkContent& m_content; + size_t currentIndex = 0; + }; + + } //Arguments + +} //NlohmannSerializer + + + + + +}} //namespace ApiGear::ObjectLink \ No newline at end of file diff --git a/src/olink/core/defaultmessageserializer.h b/src/olink/core/defaultmessageserializer.h new file mode 100644 index 0000000..b8b4343 --- /dev/null +++ b/src/olink/core/defaultmessageserializer.h @@ -0,0 +1,219 @@ +#pragma once + +#include "types.h" +#include "olink_common.h" +#include "nlohmann/json.hpp" +#include "olinkcontent.h" +#include +#include "imessageserializer.h" +#include "defaultcontentserializer.h" + + +namespace ApiGear { +namespace ObjectLink { + +/** +* Choose one of the available message formats for object link protocol messages. +* It describes network packing only, the message is still formatted with nlohmann::json +*/ +enum MessageFormat +{ + JSON = 1, + BSON = 2, + MSGPACK = 3, + CBOR = 4 +}; + +class NlohmannMessageWriter: public IMessageWriter +{ +public: + NlohmannMessageWriter() + { + message.message = nlohmann::json::array(); + } + + OLinkMessage& writeNext(MsgType msgType) final + { + message.message.emplace_back(msgType); + return message; + } + + OLinkMessage& writeNext(int value) final + { + message.message.emplace_back(value); + return message; + } + + OLinkMessage& writeNext(const std::string& value) final + { + message.message.emplace_back(value); + return message; + } + + OLinkMessage& writeNext(const OLinkContent& value) final + { + message.message.emplace_back(value.content); + return message; + } + + +private: + OLinkMessage message; +}; + + + +class OLINK_EXPORT NlohmannMessageConverter : public INetworkFormatConverter +{ +public: + /**ctor + * @param format network message format used for packing messages + */ + NlohmannMessageConverter(MessageFormat format) + : m_format(format) + {} + /** + * Change network format used for message packing. + * @param format Requested message format. + */ + void setMessageFormat(MessageFormat format) + { + m_format = format; + } + + OLinkMessage fromNetworkFormat(const std::string& message) + { + OLinkMessage olinkMessage; + switch (m_format) { + case MessageFormat::JSON: + olinkMessage.message = nlohmann::json::parse(message); break; + case MessageFormat::BSON: + olinkMessage.message = nlohmann::json::from_bson(message); break; + case MessageFormat::MSGPACK: + olinkMessage.message = nlohmann::json::from_msgpack(message); break; + case MessageFormat::CBOR: + olinkMessage.message = nlohmann::json::from_cbor(message); break; + } + + return olinkMessage; + } + + ::std::string toNetworkFormat(const OLinkMessage& j) + { + switch (m_format) { + case MessageFormat::JSON: + return j.message.dump(); + case MessageFormat::BSON: + { + auto bsonData = nlohmann::json::to_bson(j.message); + return std::string(bsonData.begin(), bsonData.end()); + } + case MessageFormat::MSGPACK: + { + auto msgPackData = nlohmann::json::to_msgpack(j.message); + return std::string(msgPackData.begin(), msgPackData.end()); + } + case MessageFormat::CBOR: + { + auto cbotData = nlohmann::json::to_cbor(j.message); + return std::string(cbotData.begin(), cbotData.end()); + } + } + return std::string(); + } +private: + /**Currently used network message format*/ + MessageFormat m_format; +}; + +class NlohmannMessageReader : public IMessageReader +{ +public: + NlohmannMessageReader(const OLinkMessage& message) + :m_message(message) + {} + + void readNext(std::string& value) final + { + getNext(value); + } + + void readNext(OLinkContent& value) final + { + value.content = m_message.message[currentIndex].get(); + currentIndex++; + } + + void readNext(int value) final + { + getNext(value); + } + + + virtual std::string getAsString() const + { + return m_message.message.dump(); + } + + void readNext(MsgType& arg) + { + arg = static_cast(m_message.message[currentIndex].get()); + currentIndex++; + } + + bool validate(std::string& out_error) const + { + if (!m_message.message.is_array()) { + out_error = "message must be array"; + return false; + } + return true; + } +private: + + template + void getNext(ArgType& arg) + { + arg = m_message.message[currentIndex].get(); + currentIndex++; + } + + const OLinkMessage& m_message; + size_t currentIndex = 0; +}; + +class NlohmannMessageSerializer : public IMessageSerializer +{ +public: + NlohmannMessageSerializer(MessageFormat format = MessageFormat::JSON) + : converter(format) + { + } + + void setMessageFormat(MessageFormat format) + { + converter.setMessageFormat(format); + } + + std::unique_ptr createReader(const OLinkMessage& msg) final + { + return std::make_unique< NlohmannMessageReader>(msg); + } + std::unique_ptr createWriter() final + { + return std::make_unique< NlohmannMessageWriter>(); + } + OLinkMessage fromNetworkFormat(const std::string& message) final + { + return converter.fromNetworkFormat(message); + } + std::string toNetworkFormat(const OLinkMessage& j)final + { + return converter.toNetworkFormat(j); + } +private: + NlohmannMessageConverter converter; + size_t index = 0; +}; + +}} //ApiGear::ObjectLink \ No newline at end of file diff --git a/src/olink/core/imessageserializer.h b/src/olink/core/imessageserializer.h new file mode 100644 index 0000000..fe910c8 --- /dev/null +++ b/src/olink/core/imessageserializer.h @@ -0,0 +1,63 @@ +#pragma once + +#include "types.h" +#include "olink_common.h" +#include +#include + +namespace ApiGear +{ +namespace ObjectLink { + +class OLINK_EXPORT IMessageReader +{ +public: + virtual ~IMessageReader() = default; + virtual void readNext(MsgType& arg) = 0; + virtual void readNext(std::string& value) = 0; + virtual void readNext(OLinkContent& props) = 0; + virtual void readNext(int value) = 0; + virtual bool validate(std::string& out_error) const = 0; + virtual std::string getAsString() const = 0; +}; + +class OLINK_EXPORT INetworkFormatConverter +{ +public: + virtual ~INetworkFormatConverter() = default; + /** + * Unpacks message received from network according to selected message format. + * @param message A message received from network. + * @return Unpacked message in json format. + */ + virtual OLinkMessage fromNetworkFormat(const std::string& message) = 0; + /** + * Formats message to selected network message format. + * @param j Message to send, not formated. + * @return message in network message format. + */ + virtual std::string toNetworkFormat(const OLinkMessage& j) = 0; +}; + + +class OLINK_EXPORT IMessageWriter +{ +public: + virtual ~IMessageWriter() = default; + virtual OLinkMessage& writeNext(MsgType msgType) = 0; + virtual OLinkMessage& writeNext(const std::string& value) = 0; + virtual OLinkMessage& writeNext(const OLinkContent& props) = 0; + virtual OLinkMessage& writeNext(int id) = 0; +}; + +class OLINK_EXPORT IMessageSerializer : public INetworkFormatConverter +{ +public: + virtual ~IMessageSerializer() = default; + virtual std::unique_ptr createReader(const OLinkMessage& ) = 0; + virtual std::unique_ptr createWriter() = 0; +}; + + + +}} // namespace ApiGear::ObjectLink diff --git a/src/olink/core/olinkcontent.h b/src/olink/core/olinkcontent.h new file mode 100644 index 0000000..94da212 --- /dev/null +++ b/src/olink/core/olinkcontent.h @@ -0,0 +1,54 @@ +/* +* MIT License +* +* Copyright (c) 2021 ApiGear +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#pragma once + +#include "olink_common.h" +#include "nlohmann/json.hpp" +#include + +namespace ApiGear { +namespace ObjectLink { + + +struct OLinkContent +{ + //std::string content; + nlohmann::json content = {}; +}; + +inline std::string getAsString(OLinkContent content) +{ + return content.content.dump(); +}; + +struct InitialProperty +{ + std::string propertyName = ""; + OLinkContent propertyValue = {}; +}; +inline bool operator==(const OLinkContent& lhs, const OLinkContent& rhs) +{ + return lhs.content == rhs.content; +} +}} //namespace ApiGear::ObjectLink \ No newline at end of file diff --git a/src/olink/core/protocol.cpp b/src/olink/core/protocol.cpp index 6b8082e..c5eabcf 100644 --- a/src/olink/core/protocol.cpp +++ b/src/olink/core/protocol.cpp @@ -29,136 +29,158 @@ namespace ApiGear { namespace ObjectLink { -nlohmann::json Protocol::linkMessage(const std::string& objectId) + + +OLinkMessage& Protocol::linkMessage(IMessageWriter& serializer, const std::string& objectId) { - return nlohmann::json::array( - { MsgType::Link, objectId } - ); + serializer.writeNext(MsgType::Link); + return serializer.writeNext(objectId); } -nlohmann::json Protocol::unlinkMessage(const std::string& objectId) +OLinkMessage& Protocol::unlinkMessage(IMessageWriter& serializer, const std::string& objectId) { - return nlohmann::json::array( - { MsgType::Unlink, objectId } - ); + serializer.writeNext(MsgType::Unlink); + return serializer.writeNext(objectId); } -nlohmann::json Protocol::initMessage(const std::string& objectId, const nlohmann::json& props) +OLinkMessage& Protocol::initMessage(IMessageWriter& serializer, const std::string& objectId, const OLinkContent& props) { - return nlohmann::json::array( - { MsgType::Init, objectId, props } - ); + serializer.writeNext(MsgType::Init); + serializer.writeNext(objectId); + return serializer.writeNext(props); } -nlohmann::json Protocol::setPropertyMessage(const std::string& propertyId, const nlohmann::json& value) +OLinkMessage& Protocol::setPropertyMessage(IMessageWriter& serializer, const std::string& propertyId, const OLinkContent& value) { - return nlohmann::json::array( - { MsgType::SetProperty, propertyId, value } - ); - + serializer.writeNext(MsgType::SetProperty); + serializer.writeNext(propertyId); + return serializer.writeNext(value); } -nlohmann::json Protocol::propertyChangeMessage(const std::string& propertyId, const nlohmann::json& value) +OLinkMessage& Protocol::propertyChangeMessage(IMessageWriter& serializer, const std::string& propertyId, const OLinkContent& value) { - return nlohmann::json::array( - { MsgType::PropertyChange, propertyId, value } - ); + serializer.writeNext(MsgType::PropertyChange); + serializer.writeNext(propertyId); + return serializer.writeNext(value); } -nlohmann::json Protocol::invokeMessage(int requestId, const std::string& methodId, const nlohmann::json& args) +OLinkMessage& Protocol::invokeMessage(IMessageWriter& serializer, int requestId, const std::string& methodId, const OLinkContent& args) { - return nlohmann::json::array( - { MsgType::Invoke, requestId, methodId, args } - ); + serializer.writeNext(MsgType::Invoke); + serializer.writeNext(requestId); + serializer.writeNext(methodId); + return serializer.writeNext(args); } -nlohmann::json Protocol::invokeReplyMessage(int requestId, const std::string& methodId, const nlohmann::json& value) +OLinkMessage& Protocol::invokeReplyMessage(IMessageWriter& serializer, int requestId, const std::string& methodId, const OLinkContent& value) { - return nlohmann::json::array( - { MsgType::InvokeReply, requestId, methodId, value } - ); + serializer.writeNext(MsgType::InvokeReply); + serializer.writeNext(requestId); + serializer.writeNext(methodId); + return serializer.writeNext(value); } -nlohmann::json Protocol::signalMessage(const std::string& signalId , const nlohmann::json& args) +OLinkMessage& Protocol::signalMessage(IMessageWriter& serializer, const std::string& signalId , const OLinkContent& args) { - return nlohmann::json::array( - { MsgType::Signal, signalId, args } - ); + serializer.writeNext(MsgType::Signal); + serializer.writeNext(signalId); + return serializer.writeNext(args); } -nlohmann::json Protocol::errorMessage(MsgType msgType, int requestId, const std::string& error) +OLinkMessage& Protocol::errorMessage(IMessageWriter& serializer, MsgType msgType, int requestId, const std::string& error) { - return nlohmann::json::array( - { MsgType::Error, msgType, requestId, error } - ); + serializer.writeNext(msgType); + serializer.writeNext(requestId); + return serializer.writeNext(error); } -bool Protocol::handleMessage(const nlohmann::json& msg, IProtocolListener& listener) { - +bool Protocol::handleMessage(IMessageReader& deserializer, IProtocolListener& listener) +{ m_lastError = ""; - if(!msg.is_array()) { - m_lastError = "message must be array"; + auto isValid = deserializer.validate(m_lastError); + if(!isValid) { return false; } - const int msgType = msg[0].get(); + MsgType msgType; + deserializer.readNext(msgType); switch(msgType) { - case int(MsgType::Link): { - const auto& objectId = msg[1].get(); + case MsgType::Link: { + std::string objectId = ""; + deserializer.readNext(objectId); listener.handleLink(objectId); break; } - case int(MsgType::Init): { - const auto& objectId = msg[1].get(); - const auto& props = msg[2].get(); + case MsgType::Init: { + std::string objectId = ""; + OLinkContent props = {}; + deserializer.readNext(objectId); + deserializer.readNext(props); listener.handleInit(objectId, props); break; } - case int(MsgType::Unlink): { - const auto& objectId = msg[1].get(); + case MsgType::Unlink: { + std::string objectId = ""; + deserializer.readNext(objectId); listener.handleUnlink(objectId); break; } - case int(MsgType::SetProperty): { - const auto& propertyId = msg[1].get(); - const auto& value = msg[2].get(); + case MsgType::SetProperty: { + std::string propertyId = ""; + OLinkContent value = {}; + deserializer.readNext(propertyId); + deserializer.readNext(value); listener.handleSetProperty(propertyId, value); break; } - case int(MsgType::PropertyChange): { - const auto& propertyId = msg[1].get(); - const auto& value = msg[2].get(); + case MsgType::PropertyChange: { + std::string propertyId = ""; + OLinkContent value = {}; + deserializer.readNext(propertyId); + deserializer.readNext(value); listener.handlePropertyChange(propertyId, value); break; } - case int(MsgType::Invoke): { - const auto& id = msg[1].get(); - const auto& methodId = msg[2].get(); - const auto& args = msg[3].get(); - listener.handleInvoke(id, methodId, args); + case MsgType::Invoke: { + int callId = 0; + std::string methodId = ""; + OLinkContent args = {}; + //TODO DOROTA + deserializer.readNext(callId); + deserializer.readNext(methodId); + deserializer.readNext(args); + listener.handleInvoke(callId, methodId, args); break; } - case int(MsgType::InvokeReply): { - const auto& id = msg[1].get(); - const auto& methodId = msg[2].get(); - const auto& value = msg[3].get(); - listener.handleInvokeReply(id, methodId, value); + case MsgType::InvokeReply: { + int callId = 0; + std::string methodId = ""; + OLinkContent value = {}; + deserializer.readNext(callId); + deserializer.readNext(methodId); + deserializer.readNext(value); + listener.handleInvokeReply(callId, methodId, value); break; } - case int(MsgType::Signal): { - const auto& signalId = msg[1].get(); - const auto& args = msg[2].get(); + case MsgType::Signal: { + std::string signalId = ""; + OLinkContent args = {}; + deserializer.readNext(signalId); + deserializer.readNext(args); listener.handleSignal(signalId, args); break; } - case int(MsgType::Error): { - const auto& msgTypeErr = msg[1].get(); - const auto& requestId = msg[2].get(); - const auto& error = msg[3].get(); + case MsgType::Error: { + MsgType msgTypeErr = MsgType::Error; + int requestId = 0; + std::string error = ""; + deserializer.readNext(msgTypeErr); + deserializer.readNext(requestId); + deserializer.readNext(error); listener.handleError(msgTypeErr, requestId, error); break; } default: - m_lastError = "message not supported: " + msg.dump(); + m_lastError = "message not supported: " + deserializer.getAsString(); return false; } return true; diff --git a/src/olink/core/protocol.h b/src/olink/core/protocol.h index e790937..7745d49 100644 --- a/src/olink/core/protocol.h +++ b/src/olink/core/protocol.h @@ -27,6 +27,7 @@ #include "types.h" #include "nlohmann/json.hpp" #include +#include "imessageserializer.h" namespace ApiGear { namespace ObjectLink { @@ -59,19 +60,19 @@ class OLINK_EXPORT IProtocolListener * @param objectId Id of an object for which Init message was received * @param props Current values of properties for object service. */ - virtual void handleInit(const std::string& objectId, const nlohmann::json& props) = 0; + virtual void handleInit(const std::string& objectId, const OLinkContent& props) = 0; /** * Server side handler, handles setProperty message. * @param propertyId Unambiguously describes property in object for which setProperty message was received. * @param value A value to which client request a property to be set. */ - virtual void handleSetProperty(const std::string& propertyId, const nlohmann::json& value) = 0; + virtual void handleSetProperty(const std::string& propertyId, const OLinkContent& value) = 0; /** * Client side handler, handles propertyChange message. * @param propertyId Unambiguously describes property in object for which propertyChange message was received. * @param value A current value of property on server side */ - virtual void handlePropertyChange(const std::string& propertyId, const nlohmann::json& value) = 0; + virtual void handlePropertyChange(const std::string& propertyId, const OLinkContent& value) = 0; /** * Server side handler, handles Invoke message. * Implementation shall call the method on object and return the result the invokeReplyMessage. @@ -79,7 +80,7 @@ class OLINK_EXPORT IProtocolListener * @param methodId Unambiguously describes method in object for which invoke message was received. * @param args Arguments with which method should be invoked. */ - virtual void handleInvoke(int requestId, const std::string& methodId, const nlohmann::json& args) = 0; + virtual void handleInvoke(int requestId, const std::string& methodId, const OLinkContent& args) = 0; /** * Client side handler, handles invokeReply message. * @param requestId Identifier of a request with which the client requested method invocation. @@ -87,13 +88,13 @@ class OLINK_EXPORT IProtocolListener * @param methodId Unambiguously describes method in object for which invokeReply message was received. * @param value Method's result value. */ - virtual void handleInvokeReply(int requestId, const std::string& methodId, const nlohmann::json& value) = 0; + virtual void handleInvokeReply(int requestId, const std::string& methodId, const OLinkContent& value) = 0; /** * Client side handler, handles signal message. * @param signalId Unambiguously describes signal in object for which signal message was received. * @param args Arguments with which signal was emitted. */ - virtual void handleSignal(const std::string& signalId, const nlohmann::json& args) = 0; + virtual void handleSignal(const std::string& signalId, const OLinkContent& args) = 0; /** * Handles error message. * @param msgType Type of a message for which error occurred. @@ -101,7 +102,7 @@ class OLINK_EXPORT IProtocolListener * It should be used to inform the caller that the response for this call will never arrive and it should not wait for it. * @param error The error message. */ - virtual void handleError(int msgType, int requestId, const std::string& error) = 0; + virtual void handleError(MsgType msgType, int requestId, const std::string& error) = 0; }; /** @@ -119,7 +120,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param objectId Id of a service object to which client wants to connect. * @return Composed linkMessage in json format. */ - static nlohmann::json linkMessage(const std::string& objectId); + static OLinkMessage& linkMessage(IMessageWriter& serializer, const std::string& objectId); /** * Life-cycle message. * Composes an unlink message for given objectId. @@ -127,7 +128,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param objectId Id of a service that client no longer wants to use. * @return Composed unlinkMessage in json format. */ - static nlohmann::json unlinkMessage(const std::string& objectId); + static OLinkMessage& unlinkMessage(IMessageWriter& serializer, const std::string& objectId); /** * Life-cycle message. * Composes an init message for given objectId and payload in json format. @@ -137,7 +138,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param props Current state of the properties provided by service. * @return Composed initMessage in json format. */ - static nlohmann::json initMessage(const std::string& objectId, const nlohmann::json& props); + static OLinkMessage& initMessage(IMessageWriter& serializer, const std::string& objectId, const OLinkContent& props); /** * Properties message. * Composes request a change of property described with propretyId. @@ -146,7 +147,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param value Requested value of the property. * @return Composed setPropertyMessage in json format. */ - static nlohmann::json setPropertyMessage(const std::string& propertyId, const nlohmann::json& value); + static OLinkMessage& setPropertyMessage(IMessageWriter& serializer, const std::string& propertyId, const OLinkContent& value); /** * Properties message. * Composes a notification message for change of property described with propretyId. @@ -155,7 +156,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param value Current value of the property. * @return Composed propertyChangeMessage in json format. */ - static nlohmann::json propertyChangeMessage(const std::string& propertyId, const nlohmann::json& value); + static OLinkMessage& propertyChangeMessage(IMessageWriter& serializer, const std::string& propertyId, const OLinkContent& value); /** * Method message. * Composes a request of method invocation message for a methodId. @@ -165,7 +166,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param args Arguments with which method should be invoked. * @return Composed invokeMessage in json format. */ - static nlohmann::json invokeMessage(int requestId, const std::string& methodId, const nlohmann::json& args); + static OLinkMessage& invokeMessage(IMessageWriter& serializer, int requestId, const std::string& methodId, const OLinkContent& args); /** * Method message. * Composes a response to a method invocation message for a methodId. @@ -175,7 +176,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param value Value that is an outcome of method invocation. * @return Composed invokeReplyMessage in json format. */ - static nlohmann::json invokeReplyMessage(int requestId, const std::string& methodId, const nlohmann::json& value); + static OLinkMessage& invokeReplyMessage(IMessageWriter& serializer, int requestId, const std::string& methodId, const OLinkContent& value); /** * Signal message. * Composes a notification message for signal emitted for signalId. @@ -184,7 +185,7 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param args Arguments with which the signal was emitted. * @return Composed signalMessage in json format. */ - static nlohmann::json signalMessage(const std::string& signalId, const nlohmann::json& args); + static OLinkMessage& signalMessage(IMessageWriter& serializer, const std::string& signalId, const OLinkContent& args); /** * Error message. * Send this message to inform that message was not accepted. @@ -192,15 +193,14 @@ class OLINK_EXPORT Protocol : public LoggerBase * @param error Error description. * @return Composed error message in json format. */ - static nlohmann::json errorMessage(MsgType msgType, int requestId, const std::string&error); - + static OLinkMessage& errorMessage(IMessageWriter& serializer, MsgType msgType, int requestId, const std::string&error); /** * Decodes the message and calls appropriate function handler with decoded arguments. * @param msg A message payload in json format. * @param listener An object providing handlers for protocol messages. * @return true if message translation was successful and a proper listener handler was called, false otherwise. */ - bool handleMessage(const nlohmann::json& msg, IProtocolListener& listener); + bool handleMessage(IMessageReader& deserializer, IProtocolListener& listener); /** @return error for most recent handleMessage execution*/ std::string lastError(); diff --git a/src/olink/core/types.cpp b/src/olink/core/types.cpp index 213f5e6..5d1d75f 100644 --- a/src/olink/core/types.cpp +++ b/src/olink/core/types.cpp @@ -57,59 +57,6 @@ std::string Name::createMemberId(const std::string& objectId, const std::string& return objectId + "/" + memberName; } -// ******************************************************************** -// MessageConverter -// ******************************************************************** - -MessageConverter::MessageConverter(MessageFormat format) - : m_format(format) -{ -} - -void MessageConverter::setMessageFormat(MessageFormat format) -{ - m_format = format; -} - -nlohmann::json MessageConverter::fromString(const std::string& message) -{ - switch(m_format) { - case MessageFormat::JSON: - return nlohmann::json::parse(message); - case MessageFormat::BSON: - return nlohmann::json::from_bson(message); - case MessageFormat::MSGPACK: - return nlohmann::json::from_msgpack(message); - case MessageFormat::CBOR: - return nlohmann::json::from_cbor(message); - } - - return nlohmann::json(); -} - -std::string MessageConverter::toString(const nlohmann::json& j) -{ - switch(m_format) { - case MessageFormat::JSON: - return j.dump(); - case MessageFormat::BSON: - { - auto bsonData = nlohmann::json::to_bson(j); - return std::string(bsonData.begin(), bsonData.end()); - } - case MessageFormat::MSGPACK: - { - auto msgPackData = nlohmann::json::to_msgpack(j); - return std::string(msgPackData.begin(), msgPackData.end()); - } - case MessageFormat::CBOR: - { - auto cbotData = nlohmann::json::to_cbor(j); - return std::string(cbotData.begin(), cbotData.end()); - } - } - return std::string(); -} std::string toString(MsgType type) { static std::map typeNames = { diff --git a/src/olink/core/types.h b/src/olink/core/types.h index eccda9b..d96ad17 100644 --- a/src/olink/core/types.h +++ b/src/olink/core/types.h @@ -25,6 +25,7 @@ #include "olink_common.h" #include "nlohmann/json.hpp" +#include "olinkcontent.h" #include namespace ApiGear { namespace ObjectLink { @@ -47,21 +48,18 @@ enum class MsgType : int Error = 99 }; + +struct OLinkMessage +{ + //std::string message; + nlohmann::json message; +}; + /** * Use this function to convert message type to string with the message name. */ std::string toString(MsgType type); -/** -* Choose one of the available message formats for object link protocol messages. -*/ -enum MessageFormat -{ - JSON = 1, - BSON = 2, - MSGPACK = 3, - CBOR = 4 -}; /** * Logging levels for logs across the application. @@ -97,37 +95,6 @@ class OLINK_EXPORT Name { static std::string createMemberId(const std::string& objectId, const std::string& memberName); }; -/** -* -*/ -class OLINK_EXPORT MessageConverter { -public: - /**ctor - * @param format network message format used for packing messages - */ - MessageConverter(MessageFormat format); - /** - * Change network format used for message packing. - * @param format Requested message format. - */ - void setMessageFormat(MessageFormat format); - /** - * Unpacks message received from network according to selected message format. - * @param message A message received from network. - * @return Unpacked message in json format. - */ - nlohmann::json fromString(const std::string& message); - /** - * Formats message to selected network message format. - * @param j message Message to send, not formated. - * @return message in network message format. - */ - std::string toString(const nlohmann::json& j); -private: - /**Currently used network message format*/ - MessageFormat m_format; -}; - /** * Interface for handling network messages. */ @@ -151,7 +118,7 @@ class OLINK_EXPORT InvokeReplyArg { /**Consists of invoked method name and objectId of an object it was invoked on*/ std::string methodId; /** Result of the method invocation. */ - nlohmann::json value; + OLinkContent value; }; /** A type of function for handling invokeReply message*/ @@ -205,7 +172,24 @@ class OLINK_EXPORT LoggerBase { /** * Use this function to log message with payload that needs to be converted to string. - * @param payload a message to be converted to a string, which is a high cost operation. + * @param payload a payload to be converted to a string, which is a high cost operation. + * Payload is put at the end of the created log message. + * Conversion is performed only if message will be logged: + * the log function is set and log level is not lower than a set log level. + * @param logMessage a log message to log. + * Have in mind that this operation has a high cost and should not be use often. + */ + template + void emitLogWithPayload(LogLevel level, const OLinkContent& payload, const Parameters& ...params) + { + if (m_logFunc && level >= m_Loglevel) { + emitLog(level, params..., payload.content.dump()); + } + } + + /** + * Use this function to log message with payload that needs to be converted to string. + * @param message a message to be converted to a string, which is a high cost operation. * Payload is put at the end of the created log message. * Conversion is performed only if message will be logged: * the log function is set and log level is not lower than a set log level. @@ -213,10 +197,10 @@ class OLINK_EXPORT LoggerBase { * Have in mind that this operation has a high cost and should not be use often. */ template - void emitLogWithPayload(LogLevel level, const nlohmann::json& payload, const Parameters& ...params) + void emitLogWithPayload(LogLevel level, const OLinkMessage& message, const Parameters& ...params) { if (m_logFunc && level >= m_Loglevel) { - emitLog(level, params..., payload.dump()); + emitLog(level, params..., message.message.dump()); } } diff --git a/src/olink/iclientnode.h b/src/olink/iclientnode.h index d54ae9a..3cccfa2 100644 --- a/src/olink/iclientnode.h +++ b/src/olink/iclientnode.h @@ -1,8 +1,8 @@ #pragma once -#include "nlohmann/json.hpp" #include "core/olink_common.h" #include "core/types.h" +#include "core/olinkcontent.h" #include namespace ApiGear{ @@ -40,7 +40,7 @@ class OLINK_EXPORT IClientNode { * see ApiGear::ObjectLink::Name::createMemberId to create methodId. * see also: ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName */ - virtual void invokeRemote(const std::string& methodId, const nlohmann::json& args = nlohmann::json{}, InvokeReplyFunc func = nullptr) = 0; + virtual void invokeRemote(const std::string& methodId, const OLinkContent& args = OLinkContent(), InvokeReplyFunc func = nullptr) = 0; /** * Request a service to change a property to requested value. * Once the request is accepted and property is changed the service side will send propertyChangeMessage. @@ -50,7 +50,7 @@ class OLINK_EXPORT IClientNode { * see ApiGear::ObjectLink::Name::createMemberId to create propertyId . * see also: ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName */ - virtual void setRemoteProperty(const std::string& propertyId, const nlohmann::json& value) = 0; + virtual void setRemoteProperty(const std::string& propertyId, const OLinkContent& value) = 0; }; }} // ApiGear::ObjectLink diff --git a/src/olink/iobjectsink.h b/src/olink/iobjectsink.h index f8a72a1..406de38 100644 --- a/src/olink/iobjectsink.h +++ b/src/olink/iobjectsink.h @@ -1,7 +1,7 @@ #pragma once #include "core/olink_common.h" -#include "nlohmann/json.hpp" +#include "core/olinkcontent.h" #include namespace ApiGear{ @@ -33,7 +33,7 @@ class OLINK_EXPORT IObjectSink { * see ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName to extract objectId and signal name * see also: ApiGear::ObjectLink::Name::createMemberId */ - virtual void olinkOnSignal(const std::string& signalId, const nlohmann::json& args) = 0; + virtual void olinkOnSignal(const std::string& signalId, const OLinkContent& args) = 0; /** * Handler function for serving the property changed message from the service. * @param propertyId The property identifier in object. Consists the objectId and property name. @@ -42,14 +42,14 @@ class OLINK_EXPORT IObjectSink { * see ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName to extract objectId and property name * see also: ApiGear::ObjectLink::Name::createMemberId */ - virtual void olinkOnPropertyChanged(const std::string& propertyId, const nlohmann::json& value) = 0; + virtual void olinkOnPropertyChanged(const std::string& propertyId, const OLinkContent& value) = 0; /** * Handler function for serving the Init message. * @param objectId The olink object identifier for which the connection was established. * @param props The current values for all the properties from service side. * @param node The endpoint to with which client sends messages. */ - virtual void olinkOnInit(const std::string& objectId, const nlohmann::json& props, IClientNode* node) = 0; + virtual void olinkOnInit(const std::string& objectId, const OLinkContent& props, IClientNode* node) = 0; /** * USe this function to inform the sink that connection with service was released. */ diff --git a/src/olink/iobjectsource.h b/src/olink/iobjectsource.h index bad436a..4938f9d 100644 --- a/src/olink/iobjectsource.h +++ b/src/olink/iobjectsource.h @@ -1,7 +1,7 @@ #pragma once #include "core/olink_common.h" -#include +#include "core/olinkcontent.h" #include namespace ApiGear { @@ -34,7 +34,7 @@ class OLINK_EXPORT IObjectSource { * see ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName to extract objectId and signal name * see also: ApiGear::ObjectLink::Name::createMemberId */ - virtual nlohmann::json olinkInvoke(const std::string& methodId, const nlohmann::json& args) = 0; + virtual OLinkContent olinkInvoke(const std::string& methodId, const OLinkContent& args) = 0; /** * Handler function for requesting a property change. * @param propertyId The property identifier in object. Consists of the objectId and property name. @@ -43,7 +43,7 @@ class OLINK_EXPORT IObjectSource { * see ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName to extract objectId and signal name * see also: ApiGear::ObjectLink::Name::createMemberId */ - virtual void olinkSetProperty(const std::string& propertyId, const nlohmann::json& value) = 0; + virtual void olinkSetProperty(const std::string& propertyId, const OLinkContent& value) = 0; /** * Handler function for client request linking with this service. * @param objectId The olink object identifier for which the connection was established. @@ -60,7 +60,7 @@ class OLINK_EXPORT IObjectSource { * A helper function for getting state of object. * @return State of object in json format, containing pairs of property name and its value. */ - virtual nlohmann::json olinkCollectProperties() = 0; + virtual OLinkContent olinkCollectProperties() = 0; }; }} // ApiGear::ObjectLink diff --git a/src/olink/iremotenode.h b/src/olink/iremotenode.h index d95bd56..5cb3336 100644 --- a/src/olink/iremotenode.h +++ b/src/olink/iremotenode.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include "core/olinkcontent.h" #include "core/olink_common.h" namespace ApiGear { @@ -22,7 +22,7 @@ class OLINK_EXPORT IRemoteNode { * see ApiGear::ObjectLink::Name::createMemberId to create propertyId . * see also: ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName */ - virtual void notifyPropertyChange(const std::string& propertyId, const nlohmann::json& value) = 0; + virtual void notifyPropertyChange(const std::string& propertyId, const OLinkContent& value) = 0; /** * Sends notification that signal has was emitted by service on server side. * @param signalId Identifier that consists of the objectId and the name of the signal. @@ -31,7 +31,7 @@ class OLINK_EXPORT IRemoteNode { * see ApiGear::ObjectLink::Name::createMemberId to create propertyId . * see also: ApiGear::ObjectLink::Name::getObjectId, ApiGear::ObjectLink::getMemberName */ - virtual void notifySignal(const std::string& signalId, const nlohmann::json& args) = 0; + virtual void notifySignal(const std::string& signalId, const OLinkContent& args) = 0; }; }} //ApiGear::ObjectLink diff --git a/src/olink/remotenode.cpp b/src/olink/remotenode.cpp index 828778d..4e88b28 100644 --- a/src/olink/remotenode.cpp +++ b/src/olink/remotenode.cpp @@ -58,8 +58,16 @@ void RemoteNode::handleLink(const std::string& objectId) if(source) { m_registry.addNodeForSource(m_nodeId, objectId); source->olinkLinked(objectId, this); - nlohmann::json props = source->olinkCollectProperties(); - emitWrite(Protocol::initMessage(objectId, props)); + auto props = source->olinkCollectProperties(); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::initMessage(*writer, objectId, props)); + } + } } else { static const std::string noLinkToSourceLog = "no source to link: "; emitLog(LogLevel::Warning, noLinkToSourceLog, objectId); @@ -76,7 +84,7 @@ void RemoteNode::handleUnlink(const std::string& objectId) } } -void RemoteNode::handleSetProperty(const std::string& propertyId, const nlohmann::json& value) +void RemoteNode::handleSetProperty(const std::string& propertyId, const OLinkContent& value) { auto objectId = ApiGear::ObjectLink::Name::getObjectId(propertyId); auto source = m_registry.getSource(objectId).lock(); @@ -85,24 +93,48 @@ void RemoteNode::handleSetProperty(const std::string& propertyId, const nlohmann } } -void RemoteNode::handleInvoke(int requestId, const std::string& methodId, const nlohmann::json& args) +void RemoteNode::handleInvoke(int requestId, const std::string& methodId, const OLinkContent& args) { auto objectId = ApiGear::ObjectLink::Name::getObjectId(methodId); auto source = m_registry.getSource(objectId).lock(); if(source) { - nlohmann::json value = source->olinkInvoke(methodId, args); - emitWrite(Protocol::invokeReplyMessage(requestId, methodId, value)); + auto value = source->olinkInvoke(methodId, args); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::invokeReplyMessage(*writer, requestId, methodId, { value })); + } + } } } -void RemoteNode::notifyPropertyChange(const std::string& propertyId, const nlohmann::json& value) +void RemoteNode::notifyPropertyChange(const std::string& propertyId, const OLinkContent& value) { - emitWrite(Protocol::propertyChangeMessage(propertyId, value)); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::propertyChangeMessage(*writer, propertyId, value)); + } + } } -void RemoteNode::notifySignal(const std::string& signalId, const nlohmann::json& args) +void RemoteNode::notifySignal(const std::string& signalId, const OLinkContent& args) { - emitWrite(Protocol::signalMessage(signalId, args)); + auto serializer = getSerializer(); + if (serializer) + { + auto writer = serializer->createWriter(); + if (writer) + { + emitWrite(Protocol::signalMessage(*writer, signalId, args)); + } + } } RemoteRegistry& RemoteNode::registry() diff --git a/src/olink/remotenode.h b/src/olink/remotenode.h index 6ffe6f6..7a3aa70 100644 --- a/src/olink/remotenode.h +++ b/src/olink/remotenode.h @@ -79,14 +79,14 @@ class OLINK_EXPORT RemoteNode: public BaseNode, public IRemoteNode, public std:: /** IProtocolListener::handleUnlink implementation.*/ void handleUnlink(const std::string& objectId) override; /** IProtocolListener::handleSetProperty implementation. */ - void handleSetProperty(const std::string& propertyId, const nlohmann::json& value) override; + void handleSetProperty(const std::string& propertyId, const OLinkContent& value) override; /** IProtocolListener::handleInvoke implementation. */ - void handleInvoke(int requestId, const std::string& methodId, const nlohmann::json& args) override; + void handleInvoke(int requestId, const std::string& methodId, const OLinkContent& args) override; /** IRemoteNode::notifyPropertyChange implementation. */ - void notifyPropertyChange(const std::string& propertyId, const nlohmann::json& value) override; + void notifyPropertyChange(const std::string& propertyId, const OLinkContent& value) override; /** IRemoteNode::notifySignal implementation. */ - void notifySignal(const std::string& signalId, const nlohmann::json& args) override; + void notifySignal(const std::string& signalId, const OLinkContent& args) override; /* * The id that registry assigned to a node. diff --git a/tests/matchers.h b/tests/matchers.h index 323216f..e53e84b 100644 --- a/tests/matchers.h +++ b/tests/matchers.h @@ -3,7 +3,7 @@ #include #include #include "olink/core/types.h" - +#include "olink/core/imessageserializer.h" // Parameter matcher to match a string argument // @param keywords. List of words that has to be found in the string argument. @@ -43,12 +43,12 @@ inline auto contains_keywords(std::vector keywords) // @param message converter used for translation of messages. // @return true if all of the words from keywords were found in matched argument from function call, // false if one or more of keywords were missing in an argument. -inline auto network_message_contains_keywords(std::vector keywords, ApiGear::ObjectLink::MessageConverter& converter) +inline auto network_message_contains_keywords(std::vector keywords, ApiGear::ObjectLink::INetworkFormatConverter& converter) { return trompeloeil::make_matcher( [&converter](const std::string& networkMessage, std::vector keywords) { - auto translatedMessage = converter.fromString(networkMessage).dump(); + auto translatedMessage = converter.fromNetworkFormat(networkMessage).message.dump(); for (auto& keyword : keywords) { if (translatedMessage.find(keyword) == std::string::npos) diff --git a/tests/sinkobject.hpp b/tests/sinkobject.hpp index b76dbac..fa210ce 100644 --- a/tests/sinkobject.hpp +++ b/tests/sinkobject.hpp @@ -5,8 +5,10 @@ #include "olink/clientregistry.h" #include #include +#include "olink/core/defaultcontentserializer.h" using namespace ApiGear::ObjectLink; +namespace ContentSerializer = ApiGear::ObjectLink::NlohmannSerializer; class CalcSink: public IObjectSink { public: @@ -23,21 +25,21 @@ class CalcSink: public IObjectSink { return m_total; } void setTotal(int value) { - client()->setRemoteProperty("demo.Calc/total", value); + client()->setRemoteProperty("demo.Calc/total", ContentSerializer::Value::serialize(value)); } int add(int a) { InvokeReplyFunc func = [](InvokeReplyArg arg) { - std::cout << "invoke reply" << arg.methodId << arg.value.dump(); + std::cout << "invoke reply" << arg.methodId << ApiGear::ObjectLink::getAsString(arg.value); }; - client()->invokeRemote("demo.Calc/add", { a }, func); + client()->invokeRemote("demo.Calc/add", ContentSerializer::Arguments::serialize(a), func); return -1; } int sub(int a) { InvokeReplyFunc func = [](InvokeReplyArg arg) { - std::cout << "invoke reply " << arg.methodId << arg.value.dump(); + std::cout << "invoke reply " << arg.methodId << ApiGear::ObjectLink::getAsString(arg.value); }; - client()->invokeRemote("demo.Calc/sub", { a }, func); + client()->invokeRemote("demo.Calc/sub", ContentSerializer::Arguments::serialize(a), func); return -1; } IClientNode *client() const { @@ -52,28 +54,36 @@ class CalcSink: public IObjectSink { std::string olinkObjectName() override { return "demo.Calc"; } - void olinkOnSignal(const std::string& name, const nlohmann::json& args) override { - std::cout << "onSignal" << name << args.dump() << std::endl; - events.push_back({name, args}); + void olinkOnSignal(const std::string& name, const OLinkContent& args) override { + std::cout << "onSignal" << name << ApiGear::ObjectLink::getAsString(args) << std::endl; + ContentSerializer::Arguments::Deserializer reader(args); + int signalArgValue; + reader.getNext(signalArgValue); + events.push_back(std::make_pair(name, signalArgValue)); } - void olinkOnPropertyChanged(const std::string& name, const nlohmann::json& value) override { - std::cout << "onPropertyChanged" << name << value.dump() << std::endl; + void olinkOnPropertyChanged(const std::string& name, const OLinkContent& value) override { + std::cout << "onPropertyChanged" << name << ApiGear::ObjectLink::getAsString(value) << std::endl; std::string path = Name::getMemberName(name); if(path == "total") { - int total = value.get(); + int total = 0; + ContentSerializer::Value::deserialize(value, total); if(m_total != total) { m_total = total; } } } - void olinkOnInit(const std::string& name, const nlohmann::json& props, IClientNode* client) override { - std::cout << "CalcSink.olinkOnInit: " << name << props.dump() << std::endl; + void olinkOnInit(const std::string& name, const OLinkContent& props, IClientNode* client) override { + std::cout << "CalcSink.olinkOnInit: " << name << ApiGear::ObjectLink::getAsString(props) << std::endl; m_client = client; m_ready = true; - if(props.contains("total")) { - int total = props["total"].get(); + InitialProperty expectTotalProperty; + ContentSerializer::Arguments::Deserializer reader(props); + reader.getNext(expectTotalProperty); + if(expectTotalProperty.propertyName == "total") { + int total = 0; + ContentSerializer::fromInitialProperty(expectTotalProperty, total); if(m_total != total) { m_total = total; } @@ -84,7 +94,7 @@ class CalcSink: public IObjectSink { m_client = nullptr; } public: - std::list events; + std::list> events; private: IClientNode *m_client; ClientRegistry& m_registry; diff --git a/tests/sourceobject.hpp b/tests/sourceobject.hpp index ce7f0a1..0a2f16a 100644 --- a/tests/sourceobject.hpp +++ b/tests/sourceobject.hpp @@ -5,7 +5,10 @@ #include "olink/iobjectsource.h" #include +#include "olink/core/defaultcontentserializer.h" + using namespace ApiGear::ObjectLink; +namespace ContentSerializer = ApiGear::ObjectLink::NlohmannSerializer; class CalcSource: public IObjectSource { public: @@ -26,11 +29,11 @@ class CalcSource: public IObjectSource { int add(int value) { m_total += value; - remoteNode()->notifyPropertyChange("demo.Calc/total", m_total); - m_events.push_back({ "demo.Calc/add", value }); - m_events.push_back({ "demo.Calc/total", m_total }); + remoteNode()->notifyPropertyChange("demo.Calc/total", ContentSerializer::Value::serialize(m_total)); + m_events.push_back(std::make_pair("demo.Calc/sub", value)); + m_events.push_back(std::make_pair("demo.Calc/total", m_total)); if(m_total >= 10) { - remoteNode()->notifySignal("demo.Calc/hitUpper", { 10 }); + remoteNode()->notifySignal("demo.Calc/hitUpper", ContentSerializer::Arguments::serialize( 10 )); m_events.push_back({ "demo.Calc/hitUpper", 10 }); } return m_total; @@ -38,41 +41,44 @@ class CalcSource: public IObjectSource { int sub(int value) { m_total -= value; - remoteNode()->notifyPropertyChange("demo.Calc/total", m_total); - m_events.push_back({ "demo.Calc/sub", value }); - m_events.push_back({ "demo.Calc/total", m_total }); + remoteNode()->notifyPropertyChange("demo.Calc/total", ContentSerializer::Value::serialize(m_total)); + m_events.push_back(std::make_pair("demo.Calc/sub", value)); + m_events.push_back(std::make_pair("demo.Calc/total", m_total)); if(m_total <= 0) { - remoteNode()->notifySignal("demo.Calc/hitLower", { 0 }); - m_events.push_back({ "demo.Calc/hitLower", 0 }); + remoteNode()->notifySignal("demo.Calc/hitLower", ContentSerializer::Arguments::serialize(0)); + m_events.push_back(std::make_pair("demo.Calc/hitLower", 0)); } return m_total; } void notifyShutdown(int timeout) { - remoteNode()->notifySignal("demo.Calc/timeout", { timeout }); + remoteNode()->notifySignal("demo.Calc/timeout", ContentSerializer::Arguments::serialize(timeout)); } // IServiceObjectListener interface public: std::string olinkObjectName() override { return "demo.Calc"; } - nlohmann::json olinkInvoke(const std::string& name, const nlohmann::json& args) override { - std::cout << "invoke" << name << args.dump(); + OLinkContent olinkInvoke(const std::string& name, const OLinkContent& args) override { + std::cout << "invoke" << name << ApiGear::ObjectLink::getAsString(args); std::string path = Name::getMemberName(name); if(path == "add") { - int a = args[0].get(); - int result = add(a); + int a=0; + ContentSerializer::Arguments::Deserializer reader(args); + reader.getNext(a); + auto result = ContentSerializer::Value::serialize(add(a)); return result; } - return nlohmann::json(); + return OLinkContent(); } - void olinkSetProperty(const std::string& name, const nlohmann::json& value) override { - std::cout << "setProperty" << name << value.dump(); + void olinkSetProperty(const std::string& name, const OLinkContent& value) override { + std::cout << "setProperty" << name << ApiGear::ObjectLink::getAsString(value); std::string path = Name::getMemberName(name); if(path == "total") { - int total = value.get(); + int total = 0; + ContentSerializer::Value::deserialize(value, total); if(m_total != total) { m_total = total; - remoteNode()->notifyPropertyChange(name, total); + remoteNode()->notifyPropertyChange(name, value); } } } @@ -85,13 +91,13 @@ class CalcSource: public IObjectSource { std::cout << "unlinked" << name; m_node = nullptr; } - nlohmann::json olinkCollectProperties() override + OLinkContent olinkCollectProperties() override { - return {{ "total", m_total }}; + return ContentSerializer::Arguments::serialize(ContentSerializer::toInitialProperty(std::string("total"), m_total)); } private: IRemoteNode* m_node; RemoteRegistry* m_registry; int m_total; - std::vector m_events; + std::list> m_events; }; diff --git a/tests/test_client_node.cpp b/tests/test_client_node.cpp index e43fc7d..b0ea9b7 100644 --- a/tests/test_client_node.cpp +++ b/tests/test_client_node.cpp @@ -5,31 +5,39 @@ #include "olink/clientnode.h" #include "olink/clientregistry.h" #include "olink/core/types.h" +#include "olink/core/defaultmessageserializer.h" +#include "olink/core/defaultcontentserializer.h" #include "mocks.h" #include "matchers.h" +namespace ContentSerializer = ApiGear::ObjectLink::NlohmannSerializer; + namespace { // helper constants used in test. std::string sink1Id = "tests.sink1"; std::string sink2Id = "tests.sink2"; std::string propertyName = "exampleProprety"; - nlohmann::json propertyValue = {{8}}; - nlohmann::json otherPropertyValue = {{115}}; - nlohmann::json exampleInitProperties = { {propertyName, "some_string" }, {"property2", 9}, {"arg2", false } }; + std::string stringProperty = "some string"; + auto serializer = std::make_shared(); + int someIntValue = 9; + bool someBoolValue= false; + auto propertyValue = ContentSerializer::Value::serialize(8); + auto otherPropertyValue = ContentSerializer::Value::serialize(115); + ApiGear::ObjectLink::OLinkContent exampleInitProperties = ContentSerializer::Arguments::serialize( + ContentSerializer::toInitialProperty(propertyName, stringProperty), + ContentSerializer::toInitialProperty("property2", someIntValue), + ContentSerializer::toInitialProperty("property3", someBoolValue)); std::string methodName = "exampleMethod"; std::string signalName = "exampleSingal"; - nlohmann::json exampleArguments = {{"arg1", "some_string" }, {"arg2", 9}, {"arg2", false } }; - - // Converter used in tests, should be same as one used by node. - ApiGear::ObjectLink::MessageConverter converter(ApiGear::ObjectLink::MessageFormat::JSON); + ApiGear::ObjectLink::OLinkContent exampleArguments = ContentSerializer::Arguments::serialize(stringProperty, 9, false); // Helper function which gets from a InvokeMessage a requestId given by clientNode. int retrieveRequestId(const std::string& networkMessage) { - const auto& requestMessage = converter.fromString(networkMessage); - REQUIRE(requestMessage[0].get() == static_cast(ApiGear::ObjectLink::MsgType::Invoke)); - int result = requestMessage[1].get(); + const auto& requestMessage = serializer->fromNetworkFormat(networkMessage); + REQUIRE(requestMessage.message[0].get() == static_cast(ApiGear::ObjectLink::MsgType::Invoke)); + int result = requestMessage.message[1].get(); return result; }; @@ -53,7 +61,7 @@ TEST_CASE("Client Node") // Test node kept as ptr to destroy before going out of scope of test. Allows testing destructor. auto testedNode = ApiGear::ObjectLink::ClientNode::create(registry); // Setting up an output for sending messages. This function must be set by user. - testedNode->onWrite([&outputMock](const auto& msg){outputMock.writeMessage(msg); }); + testedNode->onWrite([&outputMock](const auto& msg){outputMock.writeMessage(msg); }, serializer); SECTION("Typical setup and tear down scenario - the node ends life before the sinks, client node need to unlink sinks") { @@ -61,16 +69,16 @@ TEST_CASE("Client Node") registry.addSink(sink1); // link sinks - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink2Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink2Id)))); testedNode->linkRemote(sink1Id); testedNode->linkRemote(sink2Id); REQUIRE(registry.getNode(sink1Id).lock() == testedNode); REQUIRE(registry.getNode(sink2Id).lock() == testedNode); // Expectations on dtor - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1Id)))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink2Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink2Id)))); REQUIRE_CALL(*sink1, olinkOnRelease()); REQUIRE_CALL(*sink2, olinkOnRelease()); @@ -101,7 +109,7 @@ TEST_CASE("Client Node") FORBID_CALL(outputMock, writeMessage(ANY(std::string))); // No olinkRelease call as there is no sink - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink3Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink3Id)))); testedNode->unlinkRemote(sink3Id); testedNode.reset(); @@ -113,14 +121,14 @@ TEST_CASE("Client Node") registry.addSink(sink2); // link sinks with one call - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink2Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink2Id)))); testedNode->linkRemote(sink1Id); testedNode->linkRemote(sink2Id); // unlink sinks with one call - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink2->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink2->olinkObjectName())))); REQUIRE_CALL(*sink2, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode->unlinkRemote(sink2Id); @@ -133,28 +141,28 @@ TEST_CASE("Client Node") registry.addSink(sink1); // link sinks - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); testedNode->linkRemote(sink1Id); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); testedNode->linkRemote(sink1Id); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); registry.removeSink(sink1Id); - FORBID_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1Id)))); + FORBID_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1Id)))); testedNode.reset(); } SECTION("invoking method and handling the invoke reply - successful scenario") { registry.addSink(sink1); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); testedNode->linkRemote(sink1Id); registry.addSink(sink2); // Sink doesn't have to be linked to get back the response. @@ -168,16 +176,18 @@ TEST_CASE("Client Node") // Invoke for sink2 // Expect Invoke request to be sent on invokeRemote. Retrieve request id given by node. - REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink2, exampleArguments.dump()}, converter))) + auto expectedrgumentsAsString = ApiGear::ObjectLink::getAsString(exampleArguments); + REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink2, expectedrgumentsAsString }, *serializer))) .LR_SIDE_EFFECT(firstRequestId = retrieveRequestId(_1)); - testedNode->invokeRemote(methodIdSink2, exampleArguments, [&outputMock](auto args){outputMock.writeMessage(args.methodId + args.value.dump()); }); + + testedNode->invokeRemote(methodIdSink2, exampleArguments, [&outputMock](auto args) {outputMock.writeMessage(args.methodId + args.value.content.dump()); }); REQUIRE(firstRequestId != notSetRequestValue); // Invoke for sink1 // Expect Invoke request to be sent on invokeRemote. Retrieve request id given by node. - REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink1, exampleArguments.dump() }, converter))) + REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink1, expectedrgumentsAsString }, *serializer))) .LR_SIDE_EFFECT(secondRequestId = retrieveRequestId(_1)); - testedNode->invokeRemote(methodIdSink1, exampleArguments, [&outputMock](auto args){outputMock.writeMessage(args.methodId + args.value.dump()); }); + testedNode->invokeRemote(methodIdSink1, exampleArguments, [&outputMock](auto args){outputMock.writeMessage(args.methodId + args.value.content.dump()); }); REQUIRE(secondRequestId != notSetRequestValue); REQUIRE(secondRequestId != firstRequestId); @@ -186,21 +196,25 @@ TEST_CASE("Client Node") // Send replies in different order than it was requested // First handler for sink1 will be called. // prepare reply - nlohmann::json functionResult2 = {{ 17 } }; - const auto& invokeReplyMessage2 = ApiGear::ObjectLink::Protocol::invokeReplyMessage(secondRequestId, methodIdSink1, functionResult2); + auto functionResult2 = ContentSerializer::Value::serialize(17); + auto writer1 = serializer->createWriter(); + const auto& invokeReplyMessage2 = ApiGear::ObjectLink::Protocol::invokeReplyMessage(*writer1, secondRequestId, methodIdSink1, functionResult2 ); // expect callback to be called - REQUIRE_CALL(outputMock, writeMessage(methodIdSink1 + functionResult2.dump())); - testedNode->handleMessage(converter.toString(invokeReplyMessage2)); + auto result2 = ApiGear::ObjectLink::getAsString(functionResult2); + REQUIRE_CALL(outputMock, writeMessage(methodIdSink1 + result2)); + testedNode->handleMessage(serializer->toNetworkFormat(invokeReplyMessage2)); // prepare reply - nlohmann::json functionResult1 = {{ 74 } }; - const auto& invokeReplyMessage1 = ApiGear::ObjectLink::Protocol::invokeReplyMessage(firstRequestId, methodIdSink2, functionResult1); + auto functionResult1 = ContentSerializer::Value::serialize(74); + auto writer2 = serializer->createWriter(); + const auto& invokeReplyMessage1 = ApiGear::ObjectLink::Protocol::invokeReplyMessage(*writer2, firstRequestId, methodIdSink2, functionResult1 ); // expect callback to be called - REQUIRE_CALL(outputMock, writeMessage(methodIdSink2 + functionResult1.dump())); - testedNode->handleMessage(converter.toString(invokeReplyMessage1)); + auto result1 = ApiGear::ObjectLink::getAsString(functionResult1); + REQUIRE_CALL(outputMock, writeMessage(methodIdSink2 + result1)); + testedNode->handleMessage(serializer->toNetworkFormat(invokeReplyMessage1)); // test clean up for linked node - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); @@ -218,20 +232,22 @@ TEST_CASE("Client Node") auto requestId = notSetRequestValue; // Prepare node to be waiting for invoke reply with requestId it creates on sending and for methodIdSink1. - REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink1, exampleArguments.dump() }, converter))) + auto exampleArgumentsAsString = ApiGear::ObjectLink::getAsString(exampleArguments); + REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink1, exampleArgumentsAsString }, *serializer))) .LR_SIDE_EFFECT(requestId = retrieveRequestId(_1)); - testedNode->invokeRemote(methodIdSink1, exampleArguments, [&outputMock](auto args){outputMock.writeMessage(args.methodId + args.value.dump()); }); + testedNode->invokeRemote(methodIdSink1, exampleArguments, [&outputMock](auto args){outputMock.writeMessage(args.methodId + ApiGear::ObjectLink::getAsString(args.value)); }); REQUIRE(requestId != notSetRequestValue); // Prepare reply with wrong request id. - nlohmann::json functionResult = { { 17 } }; - const auto& invokeReplyMessage = ApiGear::ObjectLink::Protocol::invokeReplyMessage(requestId, methodIdSink1, functionResult); + auto functionResult = ContentSerializer::Value::serialize(17); + auto invokeReplyMessage = ApiGear::ObjectLink::Protocol::invokeReplyMessage(*(serializer->createWriter()), requestId, methodIdSink1, functionResult); // expect no callback - REQUIRE_CALL(outputMock, writeMessage(methodIdSink1 + functionResult.dump())); - testedNode->handleMessage(converter.toString(invokeReplyMessage)); + auto result = ApiGear::ObjectLink::getAsString(functionResult); + REQUIRE_CALL(outputMock, writeMessage(methodIdSink1 + result)); + testedNode->handleMessage(serializer->toNetworkFormat(invokeReplyMessage)); // test clean up - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode.reset(); @@ -251,18 +267,19 @@ TEST_CASE("Client Node") auto otherRequestId = 157; // Prepare node to be waiting for invoke reply with requestId it creates on sending and for methodIdSink1. - REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink1, exampleArguments.dump() }, converter))) + REQUIRE_CALL(outputMock, writeMessage(network_message_contains_keywords({ methodIdSink1, ApiGear::ObjectLink::getAsString(exampleArguments) }, *serializer))) .LR_SIDE_EFFECT(requestId = retrieveRequestId(_1)); - testedNode->invokeRemote(methodIdSink1, exampleArguments, [&outputMock](auto args){outputMock.writeMessage(args.methodId + args.value.dump()); }); + testedNode->invokeRemote(methodIdSink1, exampleArguments, + [&outputMock](auto args){outputMock.writeMessage(args.methodId + ApiGear::ObjectLink::getAsString(args.value)); }); REQUIRE(requestId != notSetRequestValue); REQUIRE(requestId != otherRequestId); // Prepare reply with wrong sink id in method id. - nlohmann::json functionResult = { { 17 } }; - const auto& invokeReplyMessage = ApiGear::ObjectLink::Protocol::invokeReplyMessage(otherRequestId, methodIdSink2, functionResult); + auto functionResult = ContentSerializer::Value::serialize(17); + auto invokeReplyMessage = ApiGear::ObjectLink::Protocol::invokeReplyMessage(*(serializer->createWriter()), otherRequestId, methodIdSink2, functionResult); // expect no callback FORBID_CALL(outputMock, writeMessage(ANY(std::string))); - testedNode->handleMessage(converter.toString(invokeReplyMessage)); + testedNode->handleMessage(serializer->toNetworkFormat(invokeReplyMessage)); // test clean up testedNode.reset(); @@ -273,21 +290,21 @@ TEST_CASE("Client Node") { registry.addSink(sink1); registry.addSink(sink2); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink2Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink2Id)))); testedNode->linkRemote(sink1Id); testedNode->linkRemote(sink2Id); auto signalId = ApiGear::ObjectLink::Name::createMemberId(sink2Id, signalName); - const auto& signalMessage = ApiGear::ObjectLink::Protocol::signalMessage(signalId, exampleArguments); + auto signalMessage = ApiGear::ObjectLink::Protocol::signalMessage(*(serializer->createWriter()), signalId, exampleArguments); REQUIRE_CALL(*sink2, olinkOnSignal(signalId, exampleArguments)); - testedNode->handleMessage(converter.toString(signalMessage)); + testedNode->handleMessage(serializer->toNetworkFormat(signalMessage)); // test clean up - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink2->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink2->olinkObjectName())))); REQUIRE_CALL(*sink2, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode->unlinkRemote(sink2Id); @@ -298,17 +315,17 @@ TEST_CASE("Client Node") SECTION("handling signal message - no existing object") { registry.addSink(sink1); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); testedNode->linkRemote(sink1Id); auto signalId = ApiGear::ObjectLink::Name::createMemberId(sink2Id, signalName); - const auto& signalMessage = ApiGear::ObjectLink::Protocol::signalMessage(signalId, exampleArguments); + auto signalMessage = ApiGear::ObjectLink::Protocol::signalMessage(*(serializer->createWriter()), signalId, exampleArguments); FORBID_CALL(*sink1, olinkOnSignal(signalId, exampleArguments)); - testedNode->handleMessage(converter.toString(signalMessage)); + testedNode->handleMessage(serializer->toNetworkFormat(signalMessage)); // test clean up - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode.reset(); @@ -319,21 +336,21 @@ TEST_CASE("Client Node") registry.addSink(sink1); registry.addSink(sink2); // after sink is successfully linked the sink will get init message from server - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink2Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink2Id)))); testedNode->linkRemote(sink1Id); testedNode->linkRemote(sink2Id); - const auto& InitMessage = ApiGear::ObjectLink::Protocol::initMessage(sink2Id, exampleInitProperties); - const auto& networkFormatedInit = converter.toString(InitMessage); + auto InitMessage = ApiGear::ObjectLink::Protocol::initMessage(*(serializer->createWriter()), sink2Id, exampleInitProperties); + auto networkFormatedInit = serializer->toNetworkFormat(InitMessage); REQUIRE_CALL(*sink2, olinkOnInit(sink2Id, exampleInitProperties, testedNode.get())); testedNode->handleMessage(networkFormatedInit); // test clean up - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink2->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink2->olinkObjectName())))); REQUIRE_CALL(*sink2, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode->unlinkRemote(sink2Id); @@ -344,18 +361,18 @@ TEST_CASE("Client Node") { registry.addSink(sink1); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); testedNode->linkRemote(sink1Id); // Send message for not registered sink 2. - const auto& InitMessage = ApiGear::ObjectLink::Protocol::initMessage(sink2Id, exampleInitProperties); - const auto& networkFormatedInit = converter.toString(InitMessage); + auto InitMessage = ApiGear::ObjectLink::Protocol::initMessage(*(serializer->createWriter()), sink2Id, exampleInitProperties); + auto networkFormatedInit = serializer->toNetworkFormat(InitMessage); FORBID_CALL(*sink1, olinkOnInit(sink2Id, exampleInitProperties, testedNode.get())); testedNode->handleMessage(networkFormatedInit); // test clean up - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode.reset(); @@ -365,17 +382,17 @@ TEST_CASE("Client Node") { registry.addSink(sink1); registry.addSink(sink2); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink2Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink2Id)))); testedNode->linkRemote(sink1Id); testedNode->linkRemote(sink2Id); const auto propertyId = ApiGear::ObjectLink::Name::createMemberId(sink2Id, propertyName); - const auto& requestPropertyChangeMessage = ApiGear::ObjectLink::Protocol::setPropertyMessage(propertyId, propertyValue); - const auto& networkFormatedRequestPropertyChange = converter.toString(requestPropertyChangeMessage); + auto requestPropertyChangeMessage = ApiGear::ObjectLink::Protocol::setPropertyMessage(*(serializer->createWriter()), propertyId, propertyValue); + auto networkFormatedRequestPropertyChange = serializer->toNetworkFormat(requestPropertyChangeMessage); - const auto& publishedPropertyChangeMessage = ApiGear::ObjectLink::Protocol::propertyChangeMessage(propertyId, otherPropertyValue); - const auto& networkFormatedPublishedPropertyChange = converter.toString(publishedPropertyChangeMessage); + auto publishedPropertyChangeMessage = ApiGear::ObjectLink::Protocol::propertyChangeMessage(*(serializer->createWriter()), propertyId, otherPropertyValue); + auto networkFormatedPublishedPropertyChange = serializer->toNetworkFormat(publishedPropertyChangeMessage); // Expect Request to be sent on setRemotePropertyCall. REQUIRE_CALL(outputMock, writeMessage(networkFormatedRequestPropertyChange)); @@ -386,9 +403,9 @@ TEST_CASE("Client Node") testedNode->handleMessage(networkFormatedPublishedPropertyChange); // test clean up - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink2->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink2->olinkObjectName())))); REQUIRE_CALL(*sink2, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode->unlinkRemote(sink2Id); @@ -398,15 +415,15 @@ TEST_CASE("Client Node") SECTION("handling propertyChanged message - for non existing object") { registry.addSink(sink1); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(sink1Id)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), sink1Id)))); testedNode->linkRemote(sink1Id); const auto propertyId = ApiGear::ObjectLink::Name::createMemberId(sink2Id, propertyName); - const auto& requestPropertyChangeMessage = ApiGear::ObjectLink::Protocol::setPropertyMessage(propertyId, propertyValue); - const auto& networkFormatedRequestPropertyChange = converter.toString(requestPropertyChangeMessage); + auto requestPropertyChangeMessage = ApiGear::ObjectLink::Protocol::setPropertyMessage(*(serializer->createWriter()), propertyId, propertyValue); + auto networkFormatedRequestPropertyChange = serializer->toNetworkFormat(requestPropertyChangeMessage); - const auto& publishedPropertyChangeMessage = ApiGear::ObjectLink::Protocol::propertyChangeMessage(propertyId, otherPropertyValue); - const auto& networkFormatedPublishedPropertyChange = converter.toString(publishedPropertyChangeMessage); + auto publishedPropertyChangeMessage = ApiGear::ObjectLink::Protocol::propertyChangeMessage(*(serializer->createWriter()), propertyId, otherPropertyValue); + auto networkFormatedPublishedPropertyChange = serializer->toNetworkFormat(publishedPropertyChangeMessage); // Expect Request to be sent on setRemotePropertyCall, even if it is not correct - this node doesn't serve sink2. REQUIRE_CALL(outputMock, writeMessage(networkFormatedRequestPropertyChange)); @@ -417,7 +434,7 @@ TEST_CASE("Client Node") testedNode->handleMessage(networkFormatedPublishedPropertyChange); // test clean up - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(sink1->olinkObjectName())))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), sink1->olinkObjectName())))); REQUIRE_CALL(*sink1, olinkOnRelease()); testedNode->unlinkRemote(sink1Id); testedNode.reset(); @@ -429,6 +446,7 @@ TEST_CASE("Client Node") auto nodeWithoutSetWriteFunction = ApiGear::ObjectLink::ClientNode::create(registry); nodeWithoutSetWriteFunction->onLog([&outputMock](auto level, const auto& msg){outputMock.logMessage(level, msg); }); + nodeWithoutSetWriteFunction->onWrite(nullptr, serializer); nodeWithoutSetWriteFunction->setLogLevel(ApiGear::ObjectLink::LogLevel::Info); registry.addSink(sink1); diff --git a/tests/test_olink.cpp b/tests/test_olink.cpp index 9f17b43..fbecde1 100644 --- a/tests/test_olink.cpp +++ b/tests/test_olink.cpp @@ -14,10 +14,15 @@ #include #include +#include "olink/core/defaultmessageserializer.h" +#include "olink/core/defaultcontentserializer.h" + +namespace ContentSerializer = ApiGear::ObjectLink::NlohmannSerializer; + -using json = nlohmann::json; using namespace ApiGear::ObjectLink; +auto serializer = std::make_shared(); TEST_CASE("link") { @@ -26,6 +31,7 @@ TEST_CASE("link") ClientRegistry clientRegistry; clientRegistry.onLog(ConsoleLogger::logFunc()); + // setup service auto remote = RemoteNode::createRemoteNode(registry); auto source = std::make_shared(registry); @@ -40,12 +46,12 @@ TEST_CASE("link") WriteMessageFunc clientWriteFunc = [&remote](std::string msg) { remote->handleMessage(msg); }; - client->onWrite(clientWriteFunc); + client->onWrite(clientWriteFunc, serializer); WriteMessageFunc sourceWriteFunc = [&client](std::string msg) { client->handleMessage(msg); }; - remote->onWrite(sourceWriteFunc); + remote->onWrite(sourceWriteFunc, serializer); SECTION("link ->, <- init") { // not initialized sink, with total=0 @@ -82,12 +88,12 @@ TEST_CASE("setProperty") WriteMessageFunc sinkWriteFunc = [&remote](std::string msg) { remote->handleMessage(msg); }; - client->onWrite(sinkWriteFunc); + client->onWrite(sinkWriteFunc, serializer); WriteMessageFunc sourceWriteFunc = [&client](std::string msg) { client->handleMessage(msg); }; - remote->onWrite(sourceWriteFunc); + remote->onWrite(sourceWriteFunc, serializer); // register source object registry.addSource(source); @@ -127,12 +133,12 @@ TEST_CASE("signal") WriteMessageFunc sinkWriteFunc = [&remote](std::string msg) { remote->handleMessage(msg); }; - client->onWrite(sinkWriteFunc); + client->onWrite(sinkWriteFunc, serializer); WriteMessageFunc sourceWriteFunc = [&client](std::string msg) { client->handleMessage(msg); }; - remote->onWrite(sourceWriteFunc); + remote->onWrite(sourceWriteFunc, serializer); // register source object registry.addSource(source); @@ -170,12 +176,12 @@ TEST_CASE("invoke") WriteMessageFunc clientWriteFunc = [&remote](std::string msg) { remote->handleMessage(msg); }; - client->onWrite(clientWriteFunc); + client->onWrite(clientWriteFunc, serializer); WriteMessageFunc serviceWriteFunc = [&client](std::string msg) { client->handleMessage(msg); }; - remote->onWrite(serviceWriteFunc); + remote->onWrite(serviceWriteFunc, serializer); // register source object diff --git a/tests/test_protocol.cpp b/tests/test_protocol.cpp index 2cd1528..9a622ce 100644 --- a/tests/test_protocol.cpp +++ b/tests/test_protocol.cpp @@ -6,73 +6,155 @@ #include #include "nlohmann/json.hpp" -using json = nlohmann::json; +#include "olink/core/defaultmessageserializer.h" +#include "olink/core/defaultcontentserializer.h" + using namespace ApiGear::ObjectLink; +namespace ContentSerializer = ApiGear::ObjectLink::NlohmannSerializer; TEST_CASE("protocol") { + auto serializer = std::make_shared(); std::string name = "demo.Calc"; - json props = {{ "count", 0 }}; + int initialValueForCount = 0; + std::string initPropertyName = "count"; + auto initialProperties = ContentSerializer::Arguments::serialize(ContentSerializer::toInitialProperty("count", initialValueForCount)); int value = 1; - json args = {1, 2}; + int int_argument = 1; + std::string string_argument = "some"; int requestId = 1; MsgType msgType = MsgType::Invoke; std::string error = "failed"; + MsgType created_type; + std::string created_id; + OLinkContent messageContent; + int expected_requestId = 9999; //Different than requestId, will be overridden with correct value. + SECTION("link") { - json msg = Protocol::linkMessage(name); - REQUIRE(msg[0] == MsgType::Link); - REQUIRE(msg[1] == name); + auto msg = Protocol::linkMessage(*(serializer->createWriter()), name); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(created_id); + REQUIRE(created_type == MsgType::Link); + REQUIRE(created_id == name); } SECTION("unlink") { - json msg = Protocol::unlinkMessage(name); - REQUIRE(msg[0] == MsgType::Unlink); - REQUIRE(msg[1] == name); + auto msg = Protocol::unlinkMessage(*(serializer->createWriter()), name); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(created_id); + REQUIRE(created_type == MsgType::Unlink); + REQUIRE(created_id == name); } SECTION("init") { - json msg = Protocol::initMessage(name, props); - REQUIRE(msg[0] == MsgType::Init); - REQUIRE(msg[1] == name); - REQUIRE(msg[2] == props); + auto msg = Protocol::initMessage(*(serializer->createWriter()), name, initialProperties); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(created_id); + msgReader->readNext(messageContent); + ContentSerializer::Arguments::Deserializer contentReader(messageContent); + InitialProperty expectedInitialProperty; + contentReader.getNext(expectedInitialProperty); + int expectedInitValue = 9999; + ContentSerializer::fromInitialProperty(expectedInitialProperty, expectedInitValue); + + REQUIRE(created_type == MsgType::Init); + REQUIRE(created_id == name); + REQUIRE(expectedInitialProperty.propertyName == initPropertyName); + REQUIRE(expectedInitValue == initialValueForCount); } SECTION("setProperty") { - json msg = Protocol::setPropertyMessage(name, value); - REQUIRE(msg[0] == MsgType::SetProperty); - REQUIRE(msg[1] == name); - REQUIRE(msg[2] == value); + auto msg = Protocol::setPropertyMessage(*(serializer->createWriter()), name, ContentSerializer::Value::serialize(value)); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(created_id); + msgReader->readNext(messageContent); + int expectedValue = 9999; + ContentSerializer::Value::deserialize(messageContent, expectedValue); + REQUIRE(created_type == MsgType::SetProperty); + REQUIRE(created_id == name); + REQUIRE(expectedValue == value); } SECTION("propertyChange") { - json msg = Protocol::propertyChangeMessage(name, value); - REQUIRE(msg[0] == MsgType::PropertyChange); - REQUIRE(msg[1] == name); - REQUIRE(msg[2] == value); + auto msg = Protocol::propertyChangeMessage(*(serializer->createWriter()), name, ContentSerializer::Value::serialize( value )); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(created_id); + msgReader->readNext(messageContent); + int expectedValue = 9999; + ContentSerializer::Value::deserialize(messageContent, expectedValue); + REQUIRE(created_type == MsgType::PropertyChange); + REQUIRE(created_id == name); + REQUIRE(expectedValue == value); } SECTION("invoke") { - json msg = Protocol::invokeMessage(requestId, name, args); - REQUIRE(msg[0] == MsgType::Invoke); - REQUIRE(msg[1] == requestId); - REQUIRE(msg[2] == name); - REQUIRE(msg[3] == args); + auto msg = Protocol::invokeMessage(*(serializer->createWriter()), requestId, name, ContentSerializer::Arguments::serialize(int_argument, string_argument)); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(expected_requestId); + msgReader->readNext(created_id); + msgReader->readNext(messageContent); + + REQUIRE(created_type == MsgType::Invoke); + REQUIRE(created_id == name); + REQUIRE(expected_requestId == requestId); + + ContentSerializer::Arguments::Deserializer contentReader(messageContent); + int expected_int = 9999; + std::string expected_string = "this should be filled with expected content"; + contentReader.getNext(expected_int); + contentReader.getNext(expected_string); + + REQUIRE(expected_int == int_argument); + REQUIRE(expected_string == string_argument); } SECTION("invokeReply") { - json msg = Protocol::invokeReplyMessage(requestId, name, value); - REQUIRE(msg[0] == MsgType::InvokeReply); - REQUIRE(msg[1] == requestId); - REQUIRE(msg[2] == name); - REQUIRE(msg[3] == value); + auto msg = Protocol::invokeReplyMessage(*(serializer->createWriter()), requestId, name, ContentSerializer::Value::serialize( value )); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(expected_requestId); + msgReader->readNext(created_id); + msgReader->readNext(messageContent); + + int expectedValue = 9999; + ContentSerializer::Value::deserialize(messageContent, expectedValue); + + REQUIRE(created_type == MsgType::InvokeReply); + REQUIRE(created_id == name); + REQUIRE(expectedValue == value); } SECTION("signal") { - json msg = Protocol::signalMessage(name, args); - REQUIRE(msg[0] == MsgType::Signal); - REQUIRE(msg[1] == name); - REQUIRE(msg[2] == args); + auto msg = Protocol::signalMessage(*(serializer->createWriter()), name, ContentSerializer::Arguments::serialize(int_argument, string_argument)); + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(created_id); + msgReader->readNext(messageContent); + ContentSerializer::Arguments::Deserializer contentReader(messageContent); + int expected_int = 9999; + std::string expected_string = "this should be filled with expected content"; + contentReader.getNext(expected_int); + contentReader.getNext(expected_string); + REQUIRE(created_type == MsgType::Signal); + REQUIRE(created_id == name); + REQUIRE(expected_int == int_argument); + REQUIRE(expected_string == string_argument); } SECTION("error") { - json msg = Protocol::errorMessage(msgType, requestId, error); - REQUIRE(msg[0] == MsgType::Error); - REQUIRE(msg[1] == msgType); - REQUIRE(msg[2] == requestId); - REQUIRE(msg[3] == error); + auto msg = Protocol::errorMessage(*(serializer->createWriter()), msgType, requestId, error); + REQUIRE(msgType != MsgType::Error); + MsgType expected_msgTypeWithError = MsgType::Error;// should be overridden + std::string expected_error = ""; + auto msgReader = serializer->createReader(msg); + msgReader->readNext(created_type); + msgReader->readNext(expected_msgTypeWithError); + msgReader->readNext(expected_requestId); + msgReader->readNext(expected_error); + + REQUIRE(created_type == MsgType::Error); + REQUIRE(expected_msgTypeWithError == msgType); + REQUIRE(expected_requestId == requestId); + REQUIRE(expected_error == error); } } diff --git a/tests/test_remote_node.cpp b/tests/test_remote_node.cpp index de8775b..e6ecc65 100644 --- a/tests/test_remote_node.cpp +++ b/tests/test_remote_node.cpp @@ -6,32 +6,42 @@ #include "olink/remoteregistry.h" #include "olink/core/types.h" +#include "olink/core/defaultmessageserializer.h" +#include "olink/core/defaultcontentserializer.h" + #include "mocks.h" #include "matchers.h" +namespace ContentSerializer = ApiGear::ObjectLink::NlohmannSerializer; + namespace { // helper constants used in test. std::string source1Id = "tests.source1"; std::string source2Id = "tests.source2"; std::string propertyName = "exampleProprety"; - nlohmann::json propertyValue = { {8} }; - nlohmann::json otherPropertyValue = { {115} }; - nlohmann::json exampleInitProperties = { {propertyName, "some_string" }, {"property2", 9}, {"arg2", false } }; + std::string stringProperty = "some string"; std::string methodName = "exampleMethod"; std::string signalName = "exampleSingal"; - nlohmann::json exampleArguments = { {"arg1", "some_string" }, {"arg2", 9}, {"arg2", false } }; - - // Converter used in tests, should be same as one used by node. - ApiGear::ObjectLink::MessageConverter converter(ApiGear::ObjectLink::MessageFormat::JSON); + auto serializer = std::make_shared(); + int someIntValue = 9; + bool someBoolValue = false; + auto propertyValue = ContentSerializer::Value::serialize(8); + auto otherPropertyValue = ContentSerializer::Value::serialize(115); + ApiGear::ObjectLink::OLinkContent exampleInitProperties = ContentSerializer::Arguments::serialize( + ContentSerializer::toInitialProperty(propertyName, stringProperty), + ContentSerializer::toInitialProperty("property2", someIntValue), + ContentSerializer::toInitialProperty("property3", someBoolValue)); + + ApiGear::ObjectLink::OLinkContent exampleArguments = ContentSerializer::Arguments::serialize(stringProperty, 9, false); } TEST_CASE("Remote Node") { - auto linkMessageSource1 = converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(source1Id)); - auto linkMessageSource2 = converter.toString(ApiGear::ObjectLink::Protocol::linkMessage(source2Id)); - auto unlinkMessageSource1 = converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(source1Id)); - auto unlinkMessageSource2 = converter.toString(ApiGear::ObjectLink::Protocol::unlinkMessage(source2Id)); - + auto linkMessageSource1 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), source1Id)); + auto linkMessageSource2 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::linkMessage(*(serializer->createWriter()), source2Id)); + auto unlinkMessageSource1 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), source1Id)); + auto unlinkMessageSource2 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::unlinkMessage(*(serializer->createWriter()), source2Id)); + ApiGear::ObjectLink::OLinkMessage initMsg = ApiGear::ObjectLink::Protocol::initMessage(*(serializer->createWriter()), source1Id, exampleInitProperties); // Objects required for most tests // Sink mocks that always return associated with them ids. @@ -50,7 +60,7 @@ TEST_CASE("Remote Node") // Test node kept as ptr to destroy before going out of scope of test. Allows testing destructor. auto testedNode = ApiGear::ObjectLink::RemoteNode::createRemoteNode(registry); // Setting up an output for sending messages. This function must be set by user. - testedNode->onWrite([&outputMock](const auto& msg){outputMock.writeMessage(msg); }); + testedNode->onWrite([&outputMock](const auto& msg){outputMock.writeMessage(msg); }, serializer); SECTION("Typical setup and tear down scenario - the node ends life before the source") { @@ -64,8 +74,10 @@ TEST_CASE("Remote Node") REQUIRE_CALL(*source1, olinkLinked(source1Id, ANY(ApiGear::ObjectLink::IRemoteNode*))); REQUIRE_CALL(*source2, olinkCollectProperties()).RETURN(exampleInitProperties); REQUIRE_CALL(*source2, olinkLinked(source2Id, ANY(ApiGear::ObjectLink::IRemoteNode*))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::initMessage(source1Id, exampleInitProperties)))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::initMessage(source2Id, exampleInitProperties)))); + ApiGear::ObjectLink::OLinkMessage initMessage1 = ApiGear::ObjectLink::Protocol::initMessage(*(serializer->createWriter()), source1Id, exampleInitProperties); + ApiGear::ObjectLink::OLinkMessage initMessage2 = ApiGear::ObjectLink::Protocol::initMessage(*(serializer->createWriter()), source2Id, exampleInitProperties); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(initMessage1))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(initMessage2))); testedNode->handleMessage(linkMessageSource1); testedNode->handleMessage(linkMessageSource2); @@ -96,7 +108,7 @@ TEST_CASE("Remote Node") // Second linking works REQUIRE_CALL(*source1, olinkCollectProperties()).RETURN(exampleInitProperties); REQUIRE_CALL(*source1, olinkLinked(source1Id, ANY(ApiGear::ObjectLink::IRemoteNode*))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::initMessage(source1Id, exampleInitProperties)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(initMsg))); testedNode->handleMessage(linkMessageSource1); REQUIRE(registry.getNodes(source1Id).size() == 1); @@ -109,7 +121,7 @@ TEST_CASE("Remote Node") registry.addSource(source1); REQUIRE_CALL(*source1, olinkCollectProperties()).RETURN(exampleInitProperties); REQUIRE_CALL(*source1, olinkLinked(source1Id, ANY(ApiGear::ObjectLink::IRemoteNode*))); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::initMessage(source1Id, exampleInitProperties)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(initMsg))); testedNode->handleMessage(linkMessageSource1); REQUIRE(registry.getNodes(source1Id).size() == 1); @@ -123,15 +135,15 @@ TEST_CASE("Remote Node") registry.addSource(source1); REQUIRE_CALL(*source1, olinkLinked(source1Id, ANY(ApiGear::ObjectLink::IRemoteNode*))); REQUIRE_CALL(*source1, olinkCollectProperties()).RETURN(exampleInitProperties); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::initMessage(source1Id, exampleInitProperties)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(initMsg))); testedNode->handleMessage(linkMessageSource1); REQUIRE(registry.getNodes(source1Id).size() == 1); auto propertyId1 = ApiGear::ObjectLink::Name::createMemberId(source1Id, propertyName); auto propertyId2 = ApiGear::ObjectLink::Name::createMemberId(source2Id, propertyName); - auto propertyChangeRequest1 = converter.toString(ApiGear::ObjectLink::Protocol::setPropertyMessage(propertyId1, propertyValue)); - auto propertyChangeRequest2 = converter.toString(ApiGear::ObjectLink::Protocol::setPropertyMessage(propertyId2, propertyValue)); + auto propertyChangeRequest1 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::setPropertyMessage(*(serializer->createWriter()), propertyId1, propertyValue)); + auto propertyChangeRequest2 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::setPropertyMessage(*(serializer->createWriter()), propertyId2, propertyValue)); REQUIRE_CALL(*source1, olinkSetProperty(propertyId1, propertyValue)); @@ -146,7 +158,7 @@ TEST_CASE("Remote Node") registry.addSource(source1); REQUIRE_CALL(*source1, olinkLinked(source1Id, ANY(ApiGear::ObjectLink::IRemoteNode*))); REQUIRE_CALL(*source1, olinkCollectProperties()).RETURN(exampleInitProperties); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::initMessage(source1Id, exampleInitProperties)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(initMsg))); testedNode->handleMessage(linkMessageSource1); REQUIRE(registry.getNodes(source1Id).size() == 1); @@ -156,12 +168,13 @@ TEST_CASE("Remote Node") int requestId1 = 189; int requestId2 = 32; - auto invokeMethod1 = converter.toString(ApiGear::ObjectLink::Protocol::invokeMessage(requestId1, methodId1, exampleArguments)); - auto invokeMethod2 = converter.toString(ApiGear::ObjectLink::Protocol::invokeMessage(requestId2, methodId2, exampleArguments)); + auto invokeMethod1 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::invokeMessage(*(serializer->createWriter()), requestId1, methodId1, exampleArguments)); + auto invokeMethod2 = serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::invokeMessage(*(serializer->createWriter()), requestId2, methodId2, exampleArguments)); - nlohmann::json result = {{123}}; + auto result = ContentSerializer::Value::serialize(123); REQUIRE_CALL(*source1, olinkInvoke(methodId1, exampleArguments)).RETURN(result); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::invokeReplyMessage(requestId1, methodId1, result)))); + ApiGear::ObjectLink::OLinkMessage invokeReply = ApiGear::ObjectLink::Protocol::invokeReplyMessage(*(serializer->createWriter()), requestId1, methodId1, result); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(invokeReply))); testedNode->handleMessage(invokeMethod1); // Won't have any effect, no source 2 added testedNode->handleMessage(invokeMethod2); @@ -173,14 +186,14 @@ TEST_CASE("Remote Node") SECTION("sending signal message") { auto signalId = ApiGear::ObjectLink::Name::createMemberId(source1Id, signalName); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::signalMessage(signalId, exampleArguments)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::signalMessage(*(serializer->createWriter()), signalId, exampleArguments)))); testedNode->notifySignal(signalId, exampleArguments); } SECTION("sending property") { auto propertyId = ApiGear::ObjectLink::Name::createMemberId(source1Id, propertyName); - REQUIRE_CALL(outputMock, writeMessage(converter.toString(ApiGear::ObjectLink::Protocol::propertyChangeMessage(propertyId, propertyValue)))); + REQUIRE_CALL(outputMock, writeMessage(serializer->toNetworkFormat(ApiGear::ObjectLink::Protocol::propertyChangeMessage(*(serializer->createWriter()), propertyId, propertyValue)))); testedNode->notifyPropertyChange(propertyId, propertyValue); } @@ -190,6 +203,7 @@ TEST_CASE("Remote Node") auto nodeWithoutSetWriteFunction = ApiGear::ObjectLink::RemoteNode::createRemoteNode(registry); nodeWithoutSetWriteFunction->onLog([&outputMock](auto level, const auto& msg){outputMock.logMessage(level, msg); }); + nodeWithoutSetWriteFunction->onWrite(nullptr, serializer); nodeWithoutSetWriteFunction->setLogLevel(ApiGear::ObjectLink::LogLevel::Info); auto signalId = ApiGear::ObjectLink::Name::createMemberId(source1Id, "any");