aboutsummaryrefslogtreecommitdiff
path: root/server-v1/source/EpistmoolServer/Protocol
diff options
context:
space:
mode:
Diffstat (limited to 'server-v1/source/EpistmoolServer/Protocol')
-rw-r--r--server-v1/source/EpistmoolServer/Protocol/CMakeLists.txt6
-rw-r--r--server-v1/source/EpistmoolServer/Protocol/Command.cpp200
-rw-r--r--server-v1/source/EpistmoolServer/Protocol/Command.hpp124
-rw-r--r--server-v1/source/EpistmoolServer/Protocol/Error.cpp1
-rw-r--r--server-v1/source/EpistmoolServer/Protocol/Error.hpp21
-rw-r--r--server-v1/source/EpistmoolServer/Protocol/Version.hpp9
-rw-r--r--server-v1/source/EpistmoolServer/Protocol/fwd.hpp13
7 files changed, 374 insertions, 0 deletions
diff --git a/server-v1/source/EpistmoolServer/Protocol/CMakeLists.txt b/server-v1/source/EpistmoolServer/Protocol/CMakeLists.txt
new file mode 100644
index 0000000..54f1da7
--- /dev/null
+++ b/server-v1/source/EpistmoolServer/Protocol/CMakeLists.txt
@@ -0,0 +1,6 @@
+target_sources(EpistmoolServer PRIVATE
+ fwd.hpp
+ Command.hpp Command.cpp
+ Error.hpp Error.cpp
+ Version.hpp
+)
diff --git a/server-v1/source/EpistmoolServer/Protocol/Command.cpp b/server-v1/source/EpistmoolServer/Protocol/Command.cpp
new file mode 100644
index 0000000..a108e0e
--- /dev/null
+++ b/server-v1/source/EpistmoolServer/Protocol/Command.cpp
@@ -0,0 +1,200 @@
+#include "Command.hpp"
+
+#include <QJsonObject>
+#include <QMetaEnum>
+#include <array>
+
+using namespace Epistmool::Server;
+
+namespace {
+auto kKindTraits = []() {
+ using enum ProtocolMessage::Kind;
+ std::array<ProtocolMessage::KindTrait, static_cast<size_t>(KindCOUNT)> array;
+
+ array[SessionAuth] = { .isC2S = true, .hasReply = true };
+ array[SessionDestroy] = { .isC2S = true, .hasReply = true };
+
+ array[WorkspaceCreate] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceOpen] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceFetchIndex] = { .isC2S = true, .hasReply = true };
+
+ array[WorkspaceFetchKnowledge] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceUpdateKnowledge] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceCreateKnowledge] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceDeleteKnowledge] = { .isC2S = true, .hasReply = true };
+
+ array[WorkspaceFetchKeyword] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceUpdateKeyword] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceCreateKeyword] = { .isC2S = true, .hasReply = true };
+ array[WorkspaceDeleteKeyword] = { .isC2S = true, .hasReply = true };
+
+ array[NotificationWorkspaceUpdated] = { .isC2S = false, .hasReply = false };
+ array[NotificationKnowledgeUpdated] = { .isC2S = false, .hasReply = false };
+
+ return array;
+}();
+}
+
+std::unique_ptr<ProtocolRequest> ProtocolMessage::createRequest(Kind kind)
+{
+ switch(kind) {
+ case SessionAuth: return std::make_unique<ProtocolRequest_SessionAuth>();
+ case SessionDestroy: return nullptr;
+
+ case WorkspaceCreate: return nullptr;
+ case WorkspaceOpen: return nullptr;
+ case WorkspaceFetchIndex: return nullptr;
+
+ case WorkspaceFetchKnowledge: return nullptr;
+ case WorkspaceUpdateKnowledge: return nullptr;
+ case WorkspaceCreateKnowledge: return nullptr;
+ case WorkspaceDeleteKnowledge: return nullptr;
+
+ case WorkspaceFetchKeyword: return nullptr;
+ case WorkspaceUpdateKeyword: return nullptr;
+ case WorkspaceCreateKeyword: return nullptr;
+ case WorkspaceDeleteKeyword: return nullptr;
+
+ case NotificationWorkspaceUpdated: return nullptr;
+ case NotificationKnowledgeUpdated: return nullptr;
+
+ default: return nullptr;
+ }
+}
+
+std::unique_ptr<ProtocolReply> ProtocolMessage::createReply(Kind kind)
+{
+ switch(kind) {
+ case SessionAuth: return std::make_unique<ProtocolReply_SessionAuth>();
+ case SessionDestroy: return nullptr;
+
+ case WorkspaceCreate: return nullptr;
+ case WorkspaceOpen: return nullptr;
+ case WorkspaceFetchIndex: return nullptr;
+
+ case WorkspaceFetchKnowledge: return nullptr;
+ case WorkspaceUpdateKnowledge: return nullptr;
+ case WorkspaceCreateKnowledge: return nullptr;
+ case WorkspaceDeleteKnowledge: return nullptr;
+
+ case WorkspaceFetchKeyword: return nullptr;
+ case WorkspaceUpdateKeyword: return nullptr;
+ case WorkspaceCreateKeyword: return nullptr;
+ case WorkspaceDeleteKeyword: return nullptr;
+
+ case NotificationWorkspaceUpdated: return nullptr;
+ case NotificationKnowledgeUpdated: return nullptr;
+
+ default: return nullptr;
+ }
+}
+
+const ProtocolMessage::KindTrait& ProtocolMessage::getKindTrait(Kind kind)
+{
+ return kKindTraits[kind];
+}
+
+ProtocolMessage::ProtocolMessage(Kind kind, Variant variant)
+ : kind{ kind }
+ , variant{ variant }
+{
+}
+
+ProtocolRequest::ProtocolRequest(Kind kind)
+ : ProtocolMessage(kind, RequestVariant)
+{
+}
+
+QJsonObject ProtocolRequest::serialize(const ProtocolRequest& msg)
+{
+ QJsonObject object;
+ object.insert("method", QMetaEnum::fromType<Kind>().valueToKey(msg.kind));
+
+ QJsonObject fields;
+ msg.serializeFields(fields);
+
+ return object;
+}
+
+std::unique_ptr<ProtocolRequest> ProtocolRequest::deserialize(const QJsonObject& object)
+{
+ bool ok;
+ auto methodName = object.value("method").toString().toStdString();
+ auto method = static_cast<Kind>(QMetaEnum::fromType<Kind>().keysToValue(methodName.c_str(), &ok));
+ if (!ok) return nullptr;
+
+ auto msg = createRequest(method);
+
+ auto fieldsVal = object.value("fields");
+ if (!fieldsVal.isObject()) return nullptr;
+ msg->deserializeFields(fieldsVal.toObject());
+
+ return msg;
+}
+
+ProtocolReply::ProtocolReply(Kind kind)
+ : ProtocolMessage(kind, ReplyVariant)
+{
+}
+
+QJsonObject ProtocolReply::serialize(const ProtocolReply& msg)
+{
+ QJsonObject object;
+ object.insert("method", QMetaEnum::fromType<Kind>().valueToKey(msg.kind));
+ object.insert("sequence", msg.sequence);
+
+ QJsonObject fields;
+ msg.serializeFields(fields);
+
+ return object;
+}
+
+std::unique_ptr<ProtocolReply> ProtocolReply::deserialize(const QJsonObject& object)
+{
+ bool ok;
+ auto methodName = object.value("method").toString().toStdString();
+ auto method = static_cast<Kind>(QMetaEnum::fromType<Kind>().keysToValue(methodName.c_str(), &ok));
+ if (!ok) return nullptr;
+
+ auto msg = createReply(method);
+
+ msg->sequence = object.value("sequence").toInt(-1);
+
+ auto fieldsVal = object.value("fields");
+ if (!fieldsVal.isObject()) return nullptr;
+ msg->deserializeFields(fieldsVal.toObject());
+
+ return msg;
+}
+
+ProtocolRequest_SessionAuth::ProtocolRequest_SessionAuth()
+ : ProtocolRequest(SessionAuth) {}
+
+void ProtocolRequest_SessionAuth::serializeFields(QJsonObject& object) const
+{
+ ProtocolRequest::serializeFields(object);
+ object.insert("session", theSession);
+ object.insert("createIfInvalid", createIfInvalid);
+}
+
+void ProtocolRequest_SessionAuth::deserializeFields(const QJsonObject& object)
+{
+ ProtocolRequest::deserializeFields(object);
+ theSession = object.value("session").toInt();
+ createIfInvalid = object.value("createIfInvalid").toBool();
+}
+
+ProtocolReply_SessionAuth::ProtocolReply_SessionAuth()
+ : ProtocolReply(SessionAuth) {}
+
+void ProtocolReply_SessionAuth::serializeFields(QJsonObject& object) const
+{
+ ProtocolReply::serializeFields(object);
+ object.insert("session", theSession);
+}
+
+void ProtocolReply_SessionAuth::deserializeFields(const QJsonObject& object)
+{
+ ProtocolReply::deserializeFields(object);
+ theSession = object.value("session").toInt();
+}
diff --git a/server-v1/source/EpistmoolServer/Protocol/Command.hpp b/server-v1/source/EpistmoolServer/Protocol/Command.hpp
new file mode 100644
index 0000000..9ec9786
--- /dev/null
+++ b/server-v1/source/EpistmoolServer/Protocol/Command.hpp
@@ -0,0 +1,124 @@
+#pragma once
+
+#include "all_fwd.hpp"
+
+#include <QLatin1String>
+#include <QVersionNumber>
+#include <cstddef>
+#include <memory>
+
+class QJsonObject;
+
+namespace Epistmool::Server {
+
+// Note: not all message kinds have a corresponding reply format. In such case the Kind enum is prefixed with 'Notification'.
+class ProtocolMessage
+{
+ Q_GADGET
+
+public:
+ struct KindTrait
+ {
+ bool isC2S;
+ bool hasReply;
+ };
+
+ enum Kind
+ {
+ SessionAuth,
+ SessionDestroy,
+
+ WorkspaceCreate,
+ WorkspaceOpen,
+ WorkspaceFetchIndex,
+
+ WorkspaceFetchKnowledge,
+ WorkspaceUpdateKnowledge,
+ WorkspaceCreateKnowledge,
+ WorkspaceDeleteKnowledge,
+
+ WorkspaceFetchKeyword,
+ WorkspaceUpdateKeyword,
+ WorkspaceCreateKeyword,
+ WorkspaceDeleteKeyword,
+
+ NotificationWorkspaceUpdated,
+ NotificationKnowledgeUpdated,
+
+ KindCOUNT,
+ };
+ Q_ENUM(Kind)
+
+ static std::unique_ptr<ProtocolRequest> createRequest(Kind kind);
+ static std::unique_ptr<ProtocolReply> createReply(Kind kind);
+ static const KindTrait& getKindTrait(Kind kind);
+
+ enum Variant
+ {
+ RequestVariant,
+ ReplyVariant,
+ };
+ Q_ENUM(Variant)
+
+public:
+ Kind kind;
+ Variant variant;
+
+public:
+ ProtocolMessage(Kind kind, Variant variant);
+ virtual ~ProtocolMessage() = default;
+
+protected:
+ virtual void serializeFields(QJsonObject& object) const = 0;
+ virtual void deserializeFields(const QJsonObject& object) = 0;
+};
+
+struct ProtocolRequest : public ProtocolMessage
+{
+ ProtocolRequest(Kind kind);
+ static QJsonObject serialize(const ProtocolRequest& msg);
+ static std::unique_ptr<ProtocolRequest> deserialize(const QJsonObject& object);
+};
+
+struct ProtocolReply : public ProtocolMessage
+{
+ int sequence;
+
+ ProtocolReply(Kind kind);
+ static QJsonObject serialize(const ProtocolReply& msg);
+ static std::unique_ptr<ProtocolReply> deserialize(const QJsonObject& object);
+};
+
+// ===========================
+// Individual messages classes
+
+struct ProtocolRequest_SessionAuth : public ProtocolRequest
+{
+ int theSession;
+ bool createIfInvalid;
+
+ ProtocolRequest_SessionAuth();
+
+protected:
+ virtual void serializeFields(QJsonObject& object) const override;
+ virtual void deserializeFields(const QJsonObject& object) override;
+};
+
+struct ProtocolReply_SessionAuth : public ProtocolReply
+{
+ /// The same value as provided in the request message.
+ /// If \l ProcotolCommandSessionAuth::createIfValid is set and the given session is invalid, a new session is created and written here instead of the original value.
+ int theSession;
+
+ ProtocolReply_SessionAuth();
+
+protected:
+ virtual void serializeFields(QJsonObject& object) const override;
+ virtual void deserializeFields(const QJsonObject& object) override;
+};
+
+struct ProtocolNotification_WorkspaceUpdate : public ProtocolRequest
+{
+};
+
+} // namespace Epistmool::Server
diff --git a/server-v1/source/EpistmoolServer/Protocol/Error.cpp b/server-v1/source/EpistmoolServer/Protocol/Error.cpp
new file mode 100644
index 0000000..5bf4840
--- /dev/null
+++ b/server-v1/source/EpistmoolServer/Protocol/Error.cpp
@@ -0,0 +1 @@
+#include "Error.hpp"
diff --git a/server-v1/source/EpistmoolServer/Protocol/Error.hpp b/server-v1/source/EpistmoolServer/Protocol/Error.hpp
new file mode 100644
index 0000000..1ddedf9
--- /dev/null
+++ b/server-v1/source/EpistmoolServer/Protocol/Error.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "EpistmoolServer/Protocol/Version.hpp"
+
+#include <QLatin1String>
+#include <QVersionNumber>
+
+namespace Epistmool::Server {
+struct ProtocolError
+{
+ QVersionNumber since;
+ QLatin1String name;
+};
+
+namespace ProtocolErrors {
+ const ProtocolError kUnsupportedVersion{
+ .since = ProtocolVersions::v0_1,
+ .name = QLatin1String("unsupportedVersion"),
+ };
+}
+} // namespace Epistmool::Server
diff --git a/server-v1/source/EpistmoolServer/Protocol/Version.hpp b/server-v1/source/EpistmoolServer/Protocol/Version.hpp
new file mode 100644
index 0000000..aae72bd
--- /dev/null
+++ b/server-v1/source/EpistmoolServer/Protocol/Version.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <QVersionNumber>
+
+namespace Epistmool::Server {
+namespace ProtocolVersions {
+ const QVersionNumber v0_1(0, 1);
+}
+} // namespace Epistmool::Server
diff --git a/server-v1/source/EpistmoolServer/Protocol/fwd.hpp b/server-v1/source/EpistmoolServer/Protocol/fwd.hpp
new file mode 100644
index 0000000..adf8138
--- /dev/null
+++ b/server-v1/source/EpistmoolServer/Protocol/fwd.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+namespace Epistmool::Server{
+
+// Command.hpp
+class ProtocolMessage;
+struct ProtocolRequest;
+struct ProtocolReply;
+
+// Error.hpp
+struct ProtocolError;
+
+} // namespace Epistmool::Server