diff --git a/.gitignore b/.gitignore index d33340d..e5eb935 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ cmake-build-*/ .idea/ build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c615eb..a972816 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,8 +11,8 @@ execute_process(COMMAND ${CMAKE_COMMAND} --build . add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src ${CMAKE_BINARY_DIR}/googletest-build) -set(CMAKE_C_FLAGS "-pedantic -fprofile-arcs -ftest-coverage -Wall -Werror -Wpedantic") -set(CMAKE_CXX_FLAGS "-pedantic -fprofile-arcs -ftest-coverage -Wall -Werror -Wpedantic") +set(CMAKE_C_FLAGS "-pedantic -Wall") +set(CMAKE_CXX_FLAGS "-pedantic -Wall") enable_testing() @@ -24,7 +24,14 @@ set(INCLUDE ${PROJECT_SOURCE_DIR}/project/include) set(SOURCE ${PROJECT_SOURCE_DIR}/project/src) # adding libs - +add_library(Models STATIC + ${INCLUDE}/Models.hpp + ${SOURCE}/Models.cpp) + +add_library(MainDb STATIC + ${INCLUDE}/MainDb.hpp + ${INCLUDE}/IMainDb.hpp + ${SOURCE}/MainDb.cpp) # end libs file(GLOB prod_sources @@ -32,17 +39,18 @@ file(GLOB prod_sources "${SOURCE}/main.cpp") add_executable(main.out ${SOURCE}/main.cpp) +target_link_libraries(main.out MainDb Models cassandra) file(GLOB tests "${PROJECT_SOURCE_DIR}/project/tests/*.cpp") list(REMOVE_ITEM tests "${PROJECT_SOURCE_DIR}/project/tests/main.cpp") -foreach(file ${tests}) - set(name) - get_filename_component(name ${file} NAME_WE) - add_executable("${name}_tests" - ${SOURCE}/${name}.cpp - ${file} - "${PROJECT_SOURCE_DIR}/project/tests/main.cpp") - target_link_libraries("${name}_tests" gtest_main) - add_test(NAME ${name} COMMAND "${name}_tests") -endforeach() +# foreach(file ${tests}) +# set(name) +# get_filename_component(name ${file} NAME_WE) +# add_executable("${name}_tests" +# ${SOURCE}/${name}.cpp +# ${file} +# "${PROJECT_SOURCE_DIR}/project/tests/main.cpp") +# target_link_libraries("${name}_tests" gtest_main gmock_main cassandra) +# add_test(NAME ${name} COMMAND "${name}_tests") +# endforeach() diff --git a/project/include/IMainDb.hpp b/project/include/IMainDb.hpp new file mode 100644 index 0000000..0cdffcc --- /dev/null +++ b/project/include/IMainDb.hpp @@ -0,0 +1,30 @@ +#ifndef PROJECT_INCLUDE_IMAINDB_HPP_ +#define PROJECT_INCLUDE_IMAINDB_HPP_ + +#include +#include "Models.hpp" + +class IMainDb { + public: + virtual ~IMainDb() = default; + + virtual User* searchUserLogin(std::string login, std::string password) = 0; + virtual void writeUser(const User& user) = 0; + virtual void changePassword(const User& user) = 0; + + + virtual std::string getCodeFromMessage(std::string messageId) = 0; + virtual void writeMessage(Message& message) = 0; + + virtual std::vector getNLastMessagesFromDialogue(std::string dialogueId, long count) = 0; + virtual std::vector getParticipantsLoginsFromDialogue(std::string dialogueId) = 0; + virtual std::vector getAllDialoguesIdByLogin(std::string login) = 0; + virtual std::vector getLastNDialoguesIdByLogin(std::string login, long count) = 0; + virtual DialogueList getLastNDialoguesWithLastMessage(User user, long count) = 0; + + virtual Dialogue createDialogue(std::string firstId, std::string secondId) = 0; + virtual void deleteMessage(Message& message) = 0; + virtual void deleteDialogue(Dialogue& dialogue) = 0; +}; + +#endif // PROJECT_INCLUDE_IMAINDB_HPP_ diff --git a/project/include/MainDb.hpp b/project/include/MainDb.hpp new file mode 100644 index 0000000..aafb891 --- /dev/null +++ b/project/include/MainDb.hpp @@ -0,0 +1,44 @@ +#ifndef PROJECT_INCLUDE_MAINDB_HPP_ +#define PROJECT_INCLUDE_MAINDB_HPP_ + +#include +#include +#include "IMainDb.hpp" +#include "Models.hpp" + +// getAllMessagesFromDialogue with PAGINATION +// updateMessageIsRead, messageText, messageCode +class MainDb : public IMainDb { + public: + MainDb(); + ~MainDb(); + + User* searchUserLogin(std::string login, std::string password) override; + void writeUser(const User& user) override; + void changePassword(const User& user) override; + + std::string getCodeFromMessage(std::string messageId) override; + void writeMessage(Message& message) override; // проставить айдишник если не проставлен + std::vector getNLastMessagesFromDialogue(std::string dialogueId, long count) override; + DialogueList getLastNDialoguesWithLastMessage(User user, long count) override; + std::vector getParticipantsLoginsFromDialogue(std::string dialogueId) override; + + std::vector getAllDialoguesIdByLogin(std::string login) override; + std::vector getLastNDialoguesIdByLogin(std::string login, long count) override; + Dialogue createDialogue(std::string firstId, std::string secondId) override; + + void deleteMessage(Message& message) override; + void deleteDialogue(Dialogue& dialogue) override; + + void connectToDb(const char* contactPoints); + void disconnectFromDb(); + + void migrate(); + private: + CassFuture* connect_future_; + CassFuture* close_future_; + CassCluster* cluster_; + CassSession* session_; +}; + +#endif // PROJECT_INCLUDE_MAINDB_HPP_ diff --git a/project/include/Models.hpp b/project/include/Models.hpp index cdd9a5b..5affcd6 100644 --- a/project/include/Models.hpp +++ b/project/include/Models.hpp @@ -2,204 +2,152 @@ #define PROJECT_INCLUDE_MODELS_HPP_ #include +#include #include #include #include + class User { private: - uint32_t userId_; - std::wstring userLogin_; - std::wstring userPassword_; - std::vector userDialogueList_; - std::wstring userToken_; + std::string userLogin_; + std::string userPassword_; + std::string userToken_; + std::vector dialoguesList_; // do we need this?????? int userStatus_; public: - User() = default; - ~User() = default; - User(uint32_t userId, std::wstring userLogin, std::wstring userPassword, - std::vector dialogueList, - std::wstring userToken, int userStatus) : + User(std::string userLogin, std::string userPassword, + std::string userToken, std::vector dialoguesList_, int userStatus) : userLogin_(std::move(userLogin)), userPassword_(std::move(userPassword)), - userDialogueList_(std::move(dialogueList)), userToken_(std::move(userToken)) { - setUserId(userId); - setStatus(userStatus); + userToken_(std::move(userToken)), userStatus_(userStatus) { } - uint32_t getUserId() const { - return userId_; + User(std::string userLogin, std::string userPassword, + std::string userToken, int userStatus) : + userLogin_(std::move(userLogin)), userPassword_(std::move(userPassword)), + userToken_(std::move(userToken)), userStatus_(userStatus) { } - std::wstring getLogin() { - return userLogin_; - } - std::wstring getPassword() { - return userPassword_; - } + std::string getLogin() const; - std::vector getDialogues() { - return userDialogueList_; - } + std::string getPassword() const; - std::wstring getToken() { - return userToken_; - } + std::string getToken() const; - int getStatus() const { - return userStatus_; - } + int getStatus() const; - void setUserId(uint32_t userId) { - userId_ = userId; - } + std::vector getDialoguesList() const; - void setLogin(const std::wstring &userLogin) { - userLogin_ = userLogin; - } + void addDialogueToList(std::string dialogueId); - void setPassword(const std::wstring &userPassword) { - userPassword_ = userPassword; - } + void setPassword(const std::string &userPassword); - void setDialogues(const std::vector &dialogueList) { - userDialogueList_ = dialogueList; - } + void setToken(const std::string &userToken); - void setToken(const std::wstring &userToken) { - userToken_ = userToken; - } - - void setStatus(int userStatus) { - userStatus_ = userStatus; - } + void setStatus(int userStatus); }; class LoginData { public: LoginData() { - login_ = std::wstring(); - password_ = std::wstring(); + login_ = std::string(); + password_ = std::string(); loginType_ = -1; } - LoginData(const std::wstring &login, const std::wstring &password) { + LoginData(const std::string &login, const std::string &password) { login_ = login; password_ = password; loginType_ = 1; } - void setLogin(const std::wstring &login) { - login_ = login; - } + void setLogin(const std::string &login); - void setPassword(const std::wstring &password) { - password_ = password; - } + void setPassword(const std::string &password); - void setType(short type) { - loginType_ = type; - } + void setType(short type); - std::wstring getLogin() { - return login_; - } + std::string getLogin() const; - std::wstring getPassword() { - return password_; - } + std::string getPassword() const; - short get_type() const { - return loginType_; - } + short get_type() const; + bool operator== (const LoginData& ldt1) const; private: - std::wstring login_; - std::wstring password_; + std::string login_; + std::string password_; short loginType_; }; class Message { public: - Message(uint32_t messageId, uint32_t dialogueParentId, uint32_t senderId, - std::wstring messageText, std::wstring messageCode, time_t timeSent) : + Message(std::string messageId, std::string dialogueParentId, std::string senderId, + std::string messageText, std::string messageCode, time_t timeSent, bool isRead) : messageId_(messageId), dialogueParentId_(dialogueParentId), senderId_(senderId), - messageText_(std::move(messageText)), messageCode_(std::move(messageCode)), timeSent_(timeSent) { + messageText_(std::move(messageText)), messageCode_(std::move(messageCode)), + timeSent_(timeSent), isRead_(isRead) { } ~Message() = default; - uint32_t getMessageId() const { - return messageId_; - } + std::string getMessageId() const; - uint32_t getDialogueParentId() const { - return dialogueParentId_; - } + std::string getDialogueParentId() const; - uint32_t getSenderId() const { - return senderId_; - } + std::string getSenderId() const; - std::wstring getMessageText() { - return messageText_; - } + std::string getMessageText(); - std::wstring getMessageCode() { - return messageCode_; - } + std::string getMessageCode(); - time_t getTimeSent() const { - return timeSent_; - } + time_t getTimeSent() const; + + bool isRead(); private: - uint32_t messageId_; - uint32_t dialogueParentId_; - uint32_t senderId_; - std::wstring messageText_; - std::wstring messageCode_; + std::string messageId_; + std::string dialogueParentId_; + std::string senderId_; + std::string messageText_; + std::string messageCode_; time_t timeSent_; + bool isRead_; }; class Dialogue { public: - explicit Dialogue(uint32_t dialogueId) : dialogueId_(dialogueId) { + Dialogue(std::string dialogueId, + std::vector participantsList, + std::vector dialogueMessageList) : + dialogueId_(dialogueId), + dialogueMessageList_(dialogueMessageList) { } ~Dialogue() = default; - std::vector getParticipantsList() { - return participantsList_; - } + Message getLastMessage() const; - std::vector getDialogueMessageList() { - return dialogueMessageList_; - } + std::vector getParticipantsList(); - uint32_t getDialogueId() const { - return dialogueId_; - } + std::vector getDialogueMessageList(); - void setDialogueId(uint32_t id) { - dialogueId_ = id; - } + std::string getDialogueId() const; - void pushNewMessage(const Message& newMessage) { - dialogueMessageList_.push_back(newMessage); - } + void pushNewMessage(const Message& newMessage); - void pushNewParticipant(uint32_t newParticipantId) { - participantsList_.push_back(newParticipantId); - } + void pushNewParticipant(std::string newParticipantId); + + std::string getName(User& user) const; private: - uint32_t dialogueId_; + std::string dialogueId_; std::vector dialogueMessageList_; - std::vector participantsList_; + std::vector participantsList_; }; @@ -225,81 +173,89 @@ class Compilation { messageId_ = messageId; } - std::wstring getMessageCode() { + std::string getMessageCode() { return messageCode_; } - void setMessageCode(const std::wstring &messageCode) { + void setMessageCode(const std::string &messageCode) { messageCode_ = messageCode; } - std::wstring getCompilerStderr() { + std::string getCompilerStderr() { return compilerStderr_; } - void setCompilerStderr(const std::wstring &compilerStderr) { + void setCompilerStderr(const std::string &compilerStderr) { compilerStderr_ = compilerStderr; } - std::wstring getCompilerStdout() { + std::string getCompilerStdout() { return compilerStdout_; } - void setCompilerStdout(const std::wstring &compilerStdout) { + void setCompilerStdout(const std::string &compilerStdout) { compilerStdout_ = compilerStdout; } - std::wstring getExecutionStderr() { + std::string getExecutionStderr() { return executionStderr_; } - void setExecutionStderr(const std::wstring &executionStderr) { + void setExecutionStderr(const std::string &executionStderr) { executionStderr_ = executionStderr; } - std::wstring getExecutionStdin() { + std::string getExecutionStdin() { return executionStdin_; } - void setExecutionStdin(const std::wstring &executionStdin) { + void setExecutionStdin(const std::string &executionStdin) { executionStdin_ = executionStdin; } - std::wstring getExecutionStdout() { + std::string getExecutionStdout() { return executionStdout_; } - void setExecutionStdout(const std::wstring &executionStdout) { + void setExecutionStdout(const std::string &executionStdout) { executionStdout_ = executionStdout; } - std::wstring getExecutionUsedMemory() { + std::string getExecutionUsedMemory() { return executionUsedMemory_; } - void setExecutionUsedMemory(const std::wstring &executionUsedMemory) { + void setExecutionUsedMemory(const std::string &executionUsedMemory) { executionUsedMemory_ = executionUsedMemory; } - std::wstring getExecutionTime() { + std::string getExecutionTime() { return executionTime_; } - void setExecutionTime(const std::wstring &executionTime) { + void setExecutionTime(const std::string &executionTime) { executionTime_ = executionTime; } private: uint32_t compilationId_; uint32_t messageId_; - std::wstring messageCode_; - std::wstring compilerStderr_; - std::wstring compilerStdout_; - std::wstring executionStderr_; - std::wstring executionStdin_; - std::wstring executionStdout_; - std::wstring executionUsedMemory_; - std::wstring executionTime_; + std::string messageCode_; + std::string compilerStderr_; + std::string compilerStdout_; + std::string executionStderr_; + std::string executionStdin_; + std::string executionStdout_; + std::string executionUsedMemory_; + std::string executionTime_; }; +struct ComparatorDialogue { + bool operator()(const Dialogue &lhs, const Dialogue &rhs) { + return lhs.getLastMessage().getTimeSent() > rhs.getLastMessage().getTimeSent(); + } +}; + +typedef std::multiset DialogueList; + #endif // PROJECT_INCLUDE_MODELS_HPP_ diff --git a/project/src/MainDb.cpp b/project/src/MainDb.cpp new file mode 100644 index 0000000..09cc4f0 --- /dev/null +++ b/project/src/MainDb.cpp @@ -0,0 +1,640 @@ +#include +#include +#include "MainDb.hpp" + +MainDb::MainDb() { + connect_future_ = NULL; + close_future_ = NULL; + cluster_ = cass_cluster_new(); + session_ = cass_session_new(); +} + +MainDb::~MainDb() { + if (connect_future_) { + cass_future_free(connect_future_); + } + if (close_future_) { + cass_future_free(close_future_); + } + if (cluster_) { + cass_cluster_free(cluster_); + } + if (session_) { + cass_session_free(session_); + } +} + + +void MainDb::connectToDb(const char* contactPoints) { + cass_cluster_set_contact_points(cluster_, contactPoints); + connect_future_ = cass_session_connect(session_, cluster_); +// DEBUG + CassError rc = cass_future_error_code(connect_future_); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(connect_future_, &message, &message_length); + fprintf(stderr, "Connect error: '%.*s'\n", (int)message_length, message); + } +// DEBUG + + migrate(); +} + +void MainDb::migrate() { + + const char* keyspaceQ = "CREATE KEYSPACE IF NOT EXISTS mainDB " // Keyspace and tables + "WITH REPLICATION = { 'class' : 'SimpleStrategy'," + "'replication_factor' : 1 };"; + const char* usersTQ = "CREATE TABLE IF NOT EXISTS mainDB.users_by_login (" + "login text PRIMARY KEY," + "password text" + ")" + ";"; + const char* dialoguesByIdTQ = "CREATE TABLE IF NOT EXISTS mainDB.dialogues_by_id (" + "dialogue_id uuid PRIMARY KEY," + "participants_logins set" + ")" + ";"; + const char* userDialoguesTQ = "CREATE TABLE IF NOT EXISTS mainDB.user_dialogues (" + "login text," + "dialogue_id uuid," + "time_update timestamp," + "PRIMARY KEY(login, time_update)" + ") " + "WITH CLUSTERING ORDER BY (time_update DESC)" + ";"; + const char* messagesByIdTQ = "CREATE TABLE IF NOT EXISTS mainDB.messages_by_id (" + "message_id uuid," + "dialogue_id uuid," + "sender_id uuid," + "message_text text," + "message_code text," + "time_sent timestamp," + "is_read boolean," + "PRIMARY KEY (message_id, dialogue_id, time_sent)" + ") " + "WITH CLUSTERING ORDER BY (dialogue_id ASC, time_sent DESC)" + ";"; + const char* messageByIdIndForDialog = "CREATE INDEX IF NOT EXISTS ON mainDB.messages_by_id (dialogue_id);"; + + + CassStatement* keyspaceSt = cass_statement_new(keyspaceQ, 0); // made statement + CassFuture* ks_future = cass_session_execute(session_, keyspaceSt); + +// DEBUG + CassError rc = cass_future_error_code(ks_future); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(ks_future, &message, &message_length); + fprintf(stderr, "St error: '%.*s'\n", (int)message_length, message); + } +// DEBUG + + CassStatement* usersByIdSt = cass_statement_new(usersTQ, 0); // made statement + CassFuture* usersByIdSt_future = cass_session_execute(session_, usersByIdSt); + + CassStatement* dialoguesByIdSt = cass_statement_new(dialoguesByIdTQ, 0); // made statement + CassFuture* dialoguesByIdSt_future = cass_session_execute(session_, dialoguesByIdSt); + + CassStatement* userDialoguesSt = cass_statement_new(userDialoguesTQ, 0); // made statement + CassFuture* userDialoguesSt_future = cass_session_execute(session_, userDialoguesSt); + + CassStatement* messagesByIdSt = cass_statement_new(messagesByIdTQ, 0); // made statement + CassFuture* messagesByIdSt_future = cass_session_execute(session_, messagesByIdSt); + + CassStatement* messagesByIdIndForDialogSt = cass_statement_new(messageByIdIndForDialog, 0); // made statement + CassFuture* messagesByIdIndForDialogSt_future = cass_session_execute(session_, messagesByIdIndForDialogSt); + + + cass_statement_free(keyspaceSt); + cass_statement_free(messagesByIdSt); + cass_statement_free(dialoguesByIdSt); + cass_statement_free(userDialoguesSt); + cass_statement_free(usersByIdSt); + cass_statement_free(messagesByIdIndForDialogSt); + + cass_future_free(ks_future); + cass_future_free(messagesByIdSt_future); + cass_future_free(dialoguesByIdSt_future); + cass_future_free(userDialoguesSt_future); + cass_future_free(usersByIdSt_future); + cass_future_free(messagesByIdIndForDialogSt_future); +} + +void MainDb::disconnectFromDb() { + close_future_ = cass_session_close(session_); + cass_future_wait(close_future_); + cass_future_free(close_future_); +} + +User* MainDb::searchUserLogin(std::string login, std::string password) { // function pull dialogues_list + const char* searchUser = "SELECT * FROM maindb.users_by_login " + "WHERE login = ? LIMIT 1;"; + CassStatement* returnedUser = cass_statement_new(searchUser, 1); + cass_statement_bind_string(returnedUser, 0, login.data()); + CassFuture* returnedUser_future = cass_session_execute(session_, returnedUser); + + CassError rc = cass_future_error_code(returnedUser_future); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(returnedUser_future, &message, &message_length); + fprintf(stderr, "St error: '%.*s'\n", (int)message_length, message); + } + + const CassResult* result = cass_future_get_result(returnedUser_future); + if (result == NULL) { // throw exception + cass_statement_free(returnedUser); + cass_future_free(returnedUser_future); + return nullptr; + } + + const CassRow* row = cass_result_first_row(result); + if (row == NULL) { // user doesn't exist + cass_result_free(result); + cass_statement_free(returnedUser); + cass_future_free(returnedUser_future); + return nullptr; + } + const CassValue* passwordColumn = cass_row_get_column_by_name(row, "password"); + const char* passwordStr; + size_t passwordStrLength; + cass_value_get_string(passwordColumn, &passwordStr, &passwordStrLength); + if (passwordStr != login) { // password is incorrect + return nullptr; + } + + cass_result_free(result); + cass_statement_free(returnedUser); + cass_future_free(returnedUser_future); + + return new User(login, password, "", 1); +} + +void MainDb::writeUser(const User& user) { + const char* insertUser = "INSERT INTO maindb.users_by_login " + "(login, password) VALUES " + "(?, ?)" + ";"; + + CassStatement* newUser = cass_statement_new(insertUser, 2); // made statement + + cass_statement_bind_string(newUser, 1, user.getPassword().data()); + cass_statement_bind_string(newUser, 0, user.getLogin().data()); + + CassFuture* newUser_future = cass_session_execute(session_, newUser); + + cass_statement_free(newUser); + cass_future_free(newUser_future); +} + +void MainDb::changePassword(const User& user) { + const char* updateUserQ = "UPDATE maindb.users_by_login " + "SET password = ? " + "WHERE login = ?;"; + + CassStatement* newUser = cass_statement_new(updateUserQ, 2); // made statement + + cass_statement_bind_string(newUser, 1, user.getLogin().data()); + cass_statement_bind_string(newUser, 0, user.getPassword().data()); + + CassFuture* newUser_future = cass_session_execute(session_, newUser); + + cass_statement_free(newUser); + cass_future_free(newUser_future); +} + +std::string MainDb::getCodeFromMessage(std::string messageId) { + const char* getCode = "SELECT message_code FROM maindb.messages_by_id " + "WHERE message_id = ?;"; + CassUuid uuid; + cass_uuid_from_string(messageId.data(), &uuid); + CassStatement* returnedCode = cass_statement_new(getCode, 1); + cass_statement_bind_uuid(returnedCode, 0, uuid); + CassFuture* returnedCode_future = cass_session_execute(session_, returnedCode); + + const CassResult* result = cass_future_get_result(returnedCode_future); + if (result == NULL) { // throw exception + cass_statement_free(returnedCode); + cass_future_free(returnedCode_future); + return ""; + } + const CassRow* row = cass_result_first_row(result); + + if (row == NULL) { // user doesn't exist or password is incorrect + cass_result_free(result); + cass_statement_free(returnedCode); + cass_future_free(returnedCode_future); + return ""; + } + + const CassValue* code_column = cass_row_get_column(row, 0); + + const char* code_value; + size_t code_value_length; + cass_value_get_string(code_column, &code_value, &code_value_length); + + std::string code = code_value; + + + cass_result_free(result); + cass_statement_free(returnedCode); + cass_future_free(returnedCode_future); + + return code; +} + +void MainDb::writeMessage(Message& message) { + // exceptions if message is not full filled + // if (message.getDialogueParentId() == "" || message.getMessageId() == "" + // || message.getSenderLogin() == "" || message.getTimeSent() == 0) { + // throw(-2); + // } + const char* insertMessage = "INSERT INTO maindb.messages_by_id " + "(message_id, dialogue_id, sender_id, message_text, message_code, time_sent, is_read) VALUES " + "(?, ?, ?, ?, ?, ?, ?)" + ";"; + CassUuid uuid; + CassStatement* insertMessageSt = cass_statement_new(insertMessage, 7); + cass_uuid_from_string(message.getMessageId().data(), &uuid); + cass_statement_bind_uuid(insertMessageSt, 0, uuid); + + cass_uuid_from_string(message.getDialogueParentId().data(), &uuid); + cass_statement_bind_uuid(insertMessageSt, 1, uuid); + + cass_uuid_from_string(message.getSenderLogin().data(), &uuid); + cass_statement_bind_uuid(insertMessageSt, 2, uuid); + + cass_statement_bind_string(insertMessageSt, 3, message.getMessageText().data()); + cass_statement_bind_string(insertMessageSt, 4, message.getMessageCode().data()); + cass_statement_bind_int64(insertMessageSt, 5, message.getTimeSent()); + cass_statement_bind_bool(insertMessageSt, 6, (cass_bool_t)message.isRead()); + + CassFuture* insertMessageSt_future = cass_session_execute(session_, insertMessageSt); + + const char* getTimeUpdate = "SELECT time_update FROM mainDB.user_dialogues " + "WHERE dialogue_id = ?;"; + + CassStatement* getTimeUpdateSt = cass_statement_new(getTimeUpdate, 1); + cass_statement_bind_uuid(getTimeUpdateSt, 1, uuid); + CassFuture* getTimeUpdateSt_future = cass_session_execute(session_, getTimeUpdateSt); + + const CassResult* result = cass_future_get_result(getTimeUpdateSt_future); + if (result == NULL) { // throw exception + cass_statement_free(getTimeUpdateSt); + cass_future_free(getTimeUpdateSt_future); + return; + } + + if (cass_result_first_row(result) == NULL) { // exception + cass_result_free(result); + cass_statement_free(getTimeUpdateSt); + cass_future_free(getTimeUpdateSt_future); + return; + } + + const CassRow* row = cass_result_first_row(result); + const CassValue* timeSentValue = cass_row_get_column(row, 0); + time_t timeSent; + cass_value_get_int64(timeSentValue, &timeSent); + if (timeSent < message.getTimeSent()) { + const char* updateTime = "UPDATE mainDB.user_dialogues " + "SET time_update = ? " + "WHERE login = ? and dialogue_id = ?;"; + CassStatement* updateTimeSt = cass_statement_new(updateTime, 3); + cass_statement_bind_uuid(updateTimeSt, 2, uuid); + cass_statement_bind_string(updateTimeSt, 1, message.getSenderLogin().data()); + cass_statement_bind_int64(updateTimeSt, 0, message.getTimeSent()); + CassFuture* updateTimeSt_future = cass_session_execute(session_, updateTimeSt); + cass_statement_free(updateTimeSt); + cass_future_free(updateTimeSt_future); + } + + cass_statement_free(insertMessageSt); + cass_statement_free(getTimeUpdateSt); + cass_future_free(insertMessageSt_future); + cass_future_free(getTimeUpdateSt_future); +} + +std::vector MainDb::getNLastMessagesFromDialogue(std::string dialogueId, long count) { + std::vector messages; + char* getNMessagesQ = "SELECT * FROM maindb.messages_by_id " + "WHERE dialogue_id = ? LIMIT ?;"; + CassStatement* getNMessagesSt = cass_statement_new(getNMessagesQ, 2); + CassUuid uuid; + cass_uuid_from_string(dialogueId.data(), &uuid); + cass_statement_bind_uuid(getNMessagesSt, 0, uuid); + cass_statement_bind_int32(getNMessagesSt, 1, count); + CassFuture* getNMessagesSt_future = cass_session_execute(session_, getNMessagesSt); + + CassError rc = cass_future_error_code(getNMessagesSt_future); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(getNMessagesSt_future, &message, &message_length); + fprintf(stderr, "St error: '%.*s'\n", (int)message_length, message); + } + + const CassResult* result = cass_future_get_result(getNMessagesSt_future); + if (result == NULL) { // throw exception + cass_statement_free(getNMessagesSt); + cass_future_free(getNMessagesSt_future); + return messages; + } + + if (cass_result_first_row(result) == NULL) { // user doesn't exist or password is incorrect + cass_result_free(result); + cass_statement_free(getNMessagesSt); + cass_future_free(getNMessagesSt_future); + return messages; + } + + CassIterator* messages_iterator = cass_iterator_from_result(result); + + + while (cass_iterator_next(messages_iterator)) { + const CassRow* row = cass_iterator_get_row(messages_iterator); + + CassUuid messageUuid; + char messageUuidStr[CASS_UUID_STRING_LENGTH]; + const CassValue* messageId = cass_row_get_column_by_name(row, "message_id"); + cass_value_get_uuid(messageId, &messageUuid); + cass_uuid_string(messageUuid, messageUuidStr); + + CassUuid dialogueUuid; + char dialogueUuidStr[CASS_UUID_STRING_LENGTH]; + const CassValue* dialogueId = cass_row_get_column_by_name(row, "dialogue_id"); + cass_value_get_uuid(dialogueId, &dialogueUuid); + cass_uuid_string(dialogueUuid, dialogueUuidStr); + + CassUuid senderUuid; + char senderUuidStr[CASS_UUID_STRING_LENGTH]; + const CassValue* senderId = cass_row_get_column_by_name(row, "sender_id"); + cass_value_get_uuid(senderId, &senderUuid); + cass_uuid_string(senderUuid, senderUuidStr); + + const char* messageTextStr; + size_t messageTextLength; + const CassValue* messageText = cass_row_get_column_by_name(row, "message_text"); + cass_value_get_string(messageText, &messageTextStr, &messageTextLength); + + const char* messageCodeStr; + size_t messageCodeLength; + const CassValue* messageCode = cass_row_get_column_by_name(row, "message_code"); + cass_value_get_string(messageCode, &messageCodeStr, &messageCodeLength); + + time_t messageTimeT; + const CassValue* messageTime = cass_row_get_column_by_name(row, "time_sent"); + cass_value_get_int64(messageTime, &messageTimeT); + + bool isReadB; + cass_bool_t cassBool; + const CassValue* isRead = cass_row_get_column_by_name(row, "is_read"); + cass_value_get_bool(isRead, &cassBool); + isReadB = cassBool; + + Message newMessage(messageUuidStr, dialogueUuidStr, + senderUuidStr, messageTextStr, + messageCodeStr, messageTimeT, isReadB); + + + messages.push_back(newMessage); + } + + cass_result_free(result); + cass_iterator_free(messages_iterator); + cass_statement_free(getNMessagesSt); + cass_future_free(getNMessagesSt_future); + + return messages; +} + +std::vector MainDb::getAllDialoguesIdByLogin(std::string login) { + std::vector dialoguesId; + const char* searchDialogues = "SELECT dialogue_id FROM maindb.user_dialogues " + "WHERE login = ?;"; + CassStatement* searchDialoguesSt = cass_statement_new(searchDialogues, 1); + cass_statement_bind_string(searchDialoguesSt, 0, login.data()); + CassFuture* searchDialoguesSt_future = cass_session_execute(session_, searchDialoguesSt); + + CassError rc = cass_future_error_code(searchDialoguesSt_future); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(searchDialoguesSt_future, &message, &message_length); + fprintf(stderr, "St error: '%.*s'\n", (int)message_length, message); + } + + const CassResult* result = cass_future_get_result(searchDialoguesSt_future); + if (result == NULL) { + cass_statement_free(searchDialoguesSt); + cass_future_free(searchDialoguesSt_future); + return dialoguesId; // throw exception + } + + const CassRow* row = cass_result_first_row(result); + if (row == NULL) { // user doesn't exist or password is incorrect + cass_result_free(result); + cass_statement_free(searchDialoguesSt); + cass_future_free(searchDialoguesSt_future); + return dialoguesId; // throw exception + } + + + CassIterator* dialogues_iterator = cass_iterator_from_result(result); + while (cass_iterator_next(dialogues_iterator)) { + const CassRow* row = cass_iterator_get_row(dialogues_iterator); + + CassUuid dialogueUuid; + char dialogueUuidStr[CASS_UUID_STRING_LENGTH]; + const CassValue* dialogueId = cass_row_get_column_by_name(row, "dialogue_id"); + cass_value_get_uuid(dialogueId, &dialogueUuid); + cass_uuid_string(dialogueUuid, dialogueUuidStr); + + dialoguesId.push_back(dialogueUuidStr); + } + + cass_result_free(result); + cass_iterator_free(dialogues_iterator); + cass_statement_free(searchDialoguesSt); + cass_future_free(searchDialoguesSt_future); + + return dialoguesId; +} + +std::vector MainDb::getLastNDialoguesIdByLogin(std::string login, long count) { + std::vector dialoguesId; + const char* searchDialogues = "SELECT dialogue_id FROM maindb.user_dialogues " + "WHERE login = ? LIMIT = ?;"; + CassStatement* searchDialoguesSt = cass_statement_new(searchDialogues, 2); + cass_statement_bind_string(searchDialoguesSt, 0, login.data()); + cass_statement_bind_int32(searchDialoguesSt, 1, count); + CassFuture* searchDialoguesSt_future = cass_session_execute(session_, searchDialoguesSt); + + CassError rc = cass_future_error_code(searchDialoguesSt_future); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(searchDialoguesSt_future, &message, &message_length); + fprintf(stderr, "St error: '%.*s'\n", (int)message_length, message); + } + + const CassResult* result = cass_future_get_result(searchDialoguesSt_future); + if (result == NULL) { + cass_statement_free(searchDialoguesSt); + cass_future_free(searchDialoguesSt_future); + return dialoguesId; // throw exception + } + + const CassRow* row = cass_result_first_row(result); + if (row == NULL) { // user doesn't exist or password is incorrect + cass_result_free(result); + cass_statement_free(searchDialoguesSt); + cass_future_free(searchDialoguesSt_future); + return dialoguesId; // throw exception + } + + + CassIterator* dialogues_iterator = cass_iterator_from_result(result); + while (cass_iterator_next(dialogues_iterator)) { + const CassRow* row = cass_iterator_get_row(dialogues_iterator); + + CassUuid dialogueUuid; + char dialogueUuidStr[CASS_UUID_STRING_LENGTH]; + const CassValue* dialogueId = cass_row_get_column_by_name(row, "dialogue_id"); + cass_value_get_uuid(dialogueId, &dialogueUuid); + cass_uuid_string(dialogueUuid, dialogueUuidStr); + + dialoguesId.push_back(dialogueUuidStr); + } + + cass_result_free(result); + cass_iterator_free(dialogues_iterator); + cass_statement_free(searchDialoguesSt); + cass_future_free(searchDialoguesSt_future); + + return dialoguesId; +} + +std::vector MainDb::getParticipantsLoginsFromDialogue(std::string dialogueId) { + std::vector participantsLogins; + const char* searchUser = "SELECT participants_logins FROM maindb.dialogues_by_id " + "WHERE dialogue_id = ? LIMIT 1;"; + CassStatement* returnedUser = cass_statement_new(searchUser, 1); + cass_statement_bind_string(returnedUser, 0, dialogueId.data()); + CassFuture* returnedUser_future = cass_session_execute(session_, returnedUser); + + CassError rc = cass_future_error_code(returnedUser_future); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(returnedUser_future, &message, &message_length); + fprintf(stderr, "St error: '%.*s'\n", (int)message_length, message); + } + + const CassResult* result = cass_future_get_result(returnedUser_future); + if (result == NULL) { // throw exception + cass_statement_free(returnedUser); + cass_future_free(returnedUser_future); + return participantsLogins; + } + + const CassRow* row = cass_result_first_row(result); + if (row == NULL) { // exception + cass_result_free(result); + cass_statement_free(returnedUser); + cass_future_free(returnedUser_future); + return participantsLogins; + } + + const CassValue* dialoguesColumn = cass_row_get_column_by_name(row, "participants_logins"); + + CassIterator* dialoguesIterator = cass_iterator_from_collection(dialoguesColumn); + while (cass_iterator_next(dialoguesIterator)) { + const CassValue* currentDialogue = cass_iterator_get_value(dialoguesIterator); + + const char* dialogueStr; + size_t dialogueStrLength; + cass_value_get_string(currentDialogue, &dialogueStr, &dialogueStrLength); + + participantsLogins.push_back(dialogueStr); + } + return participantsLogins; +} + +DialogueList MainDb::getLastNDialoguesWithLastMessage(User user, long count) { + std::vector dialoguesId = getLastNDialoguesIdByLogin(user.getLogin(), count); + DialogueList dialogues; + + for (int i = 0; i < dialoguesId.size(); i++) { + std::vector messagesFromCurrDialogue = getNLastMessagesFromDialogue(dialoguesId[i], 1); + std::vector participantsList = getParticipantsLoginsFromDialogue(dialoguesId[i]); + Dialogue currDialogue(dialoguesId[i], participantsList, messagesFromCurrDialogue); + dialogues.emplace(); + } + + return dialogues; +} + + +Dialogue MainDb::createDialogue(std::string firstId, std::string secondId) { + const char* newDialogue = "INSERT INTO maindb.dialogues_by_id (dialogue_id, participants_logins) " + "VALUES (?, ?);"; + CassStatement* newDialogueSt = cass_statement_new(newDialogue, 2); + CassUuidGen* uuidGen = cass_uuid_gen_new(); + CassUuid uuid; + + char uuidStr[CASS_UUID_STRING_LENGTH]; + + cass_uuid_gen_random(uuidGen, &uuid); + cass_uuid_gen_free(uuidGen); + cass_uuid_string(uuid, uuidStr); + + cass_statement_bind_uuid(newDialogueSt, 0, uuid); + + CassCollection* set = cass_collection_new(CASS_COLLECTION_TYPE_SET, 2); + + cass_collection_append_string(set, firstId.data()); + cass_collection_append_string(set, secondId.data()); + cass_statement_bind_collection(newDialogueSt, 1, set); + + CassFuture* newDialogueSt_future = cass_session_execute(session_, newDialogueSt); + + CassError rc = cass_future_error_code(newDialogueSt_future); + + if (rc != CASS_OK) { + /* Display connection error message */ + const char* message; + size_t message_length; + cass_future_error_message(newDialogueSt_future, &message, &message_length); + fprintf(stderr, "St error: '%.*s'\n", (int)message_length, message); + } + std::vector messages; + std::vector participants; + participants.push_back(firstId); + participants.push_back(secondId); + + return Dialogue(uuidStr, participants, messages); +} + +void MainDb::deleteMessage(Message& message) { +} + +void MainDb::deleteDialogue(Dialogue& dialogue) { +} diff --git a/project/src/Models.cpp b/project/src/Models.cpp new file mode 100644 index 0000000..1959f41 --- /dev/null +++ b/project/src/Models.cpp @@ -0,0 +1,133 @@ +#include "Models.hpp" + +std::string User::getLogin() const { + return userLogin_; +} + +std::string User::getPassword() const { + return userPassword_; +} + +std::string User::getToken() const { + return userToken_; +} + +Message Dialogue::getLastMessage() const { + return *dialogueMessageList_.begin(); +} + +int User::getStatus() const { + return userStatus_; +} + +std::vector User::getDialoguesList() const { + return dialoguesList_; +} + +void User::addDialogueToList(std::string dialogueId) { + dialoguesList_.push_back(dialogueId); +} + +void User::setPassword(const std::string &userPassword) { + userPassword_ = userPassword; +} + +void User::setToken(const std::string &userToken) { + userToken_ = userToken; +} + +void User::setStatus(int userStatus) { + userStatus_ = userStatus; +} + +void LoginData::setLogin(const std::string &login) { + login_ = login; +} + +void LoginData::setPassword(const std::string &password) { + password_ = password; +} + +void LoginData::setType(short type) { + loginType_ = type; +} + +std::string LoginData::getLogin() const { + return login_; +} + +std::string LoginData::getPassword() const { + return password_; +} + +short LoginData::get_type() const { + return loginType_; +} + +bool LoginData::operator==(const LoginData &ldt1) const { + if (login_ == ldt1.getLogin()) { + if (password_ == ldt1.getPassword()) { + if (loginType_ == ldt1.get_type()) { + return true; + } + } + } + return false; +} + +std::string Message::getMessageId() const { + return messageId_; +} + +std::string Message::getDialogueParentId() const { + return dialogueParentId_; +} + +std::string Message::getSenderId() const { + return senderId_; +} + +std::string Message::getMessageText() { + return messageText_; +} + +std::string Message::getMessageCode() { + return messageCode_; +} + +time_t Message::getTimeSent() const { + return timeSent_; +} + +bool Message::isRead() { + return isRead_; +} + +std::vector Dialogue::getDialogueMessageList() { + return dialogueMessageList_; +} + +std::vector Dialogue::getParticipantsList() { + return participantsList_; +} + +std::string Dialogue::getDialogueId() const { + return dialogueId_; +} + +std::string Dialogue::getName(User& user) const { + if (participantsList_[0] == user.getLogin()) { + return participantsList_[1]; + } else { + return participantsList_[0]; + } +} + + +void Dialogue::pushNewMessage(const Message &newMessage) { + dialogueMessageList_.insert(dialogueMessageList_.begin(), newMessage); +} + +void Dialogue::pushNewParticipant(std::string newParticipantId) { + participantsList_.push_back(newParticipantId); +} diff --git a/project/src/main.cpp b/project/src/main.cpp index 9a8a752..81ea98f 100644 --- a/project/src/main.cpp +++ b/project/src/main.cpp @@ -1,5 +1,14 @@ +#include +#include #include "Models.hpp" +#include "MainDb.hpp" int main() { + MainDb db; + + db.connectToDb("127.0.0.1"); + + // std::cout << out << std::endl; + db.disconnectFromDb(); return 0; } diff --git a/project/tests/MainDb.cpp b/project/tests/MainDb.cpp new file mode 100644 index 0000000..eeb8c7e --- /dev/null +++ b/project/tests/MainDb.cpp @@ -0,0 +1,49 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "../include/MainDb.hpp" +#include "../include/IMainDb.hpp" +#include +#include +#include +#include +// using ::testing::Return; +// class MockDb : public IMainDb { +// public: +// MOCK_METHOD(User*, searchUserLogin, (std::string login, std::string password), (override)); +// MOCK_METHOD(void, writeUser, (User& user), (override)); +// MOCK_METHOD(int, updateUser, (User& user), (override)); +// MOCK_METHOD(std::string, getCodeFromMessage, (uint32_t messageId), (override)); +// MOCK_METHOD(void, writeMessageToDialogue, (Message message), (override)); +// MOCK_METHOD(std::vector *, getNMessagesFromDialogue, (uint32_t dialogueId, uint32_t senderId, +// uint32_t receiverId, long count), (override)); +// MOCK_METHOD(std::vector *, getDialoguessByUserId, (int userId), (override)); +// MOCK_METHOD(uint32_t, createDialogue, (uint32_t senderId, uint32_t receiverId), (override)); +// MOCK_METHOD(void, deleteMessageFromDialogue, (Message& message), (override)); +// MOCK_METHOD(void, deleteDialogue, (Dialogue& dialogue), (override)); +// }; + +// TEST(searchUserLogin, InCorrectData) { +// MockDb mock; +// EXPECT_CALL(mock, searchUserLogin(std::string("log"), std::string("pas"))).WillOnce(Return(nullptr)); +// EXPECT_EQ(mock.searchUserLogin(std::string("log"), std::string("pas")), nullptr); +// } + +// TEST(getCodeFromMessage, CorrectData) { +// MockDb mock; +// EXPECT_CALL(mock, getCodeFromMessage(23)).WillOnce(Return("")); +// EXPECT_EQ(mock.getCodeFromMessage(23), ""); +// } + +// TEST(writeUser, CorrectData) { +// MockDb mock; +// User user; +// EXPECT_CALL(mock, writeUser(user)).WillOnce(Return(921392)); +// EXPECT_EQ(mock.writeUser(user), 921392); +// } + +// TEST(updateUser, CorrectData) { +// MockDb mock; +// User user; +// EXPECT_CALL(mock, updateUser(user)).WillOnce(Return(0)); +// EXPECT_EQ(mock.updateUser(user), 0); +// } diff --git a/project/tests/main.cpp b/project/tests/main.cpp index dc42b1a..566168d 100644 --- a/project/tests/main.cpp +++ b/project/tests/main.cpp @@ -1,6 +1,6 @@ -#include "gtest/gtest.h" +// #include "gtest/gtest.h" -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} +// int main(int argc, char **argv) { +// ::testing::InitGoogleTest(&argc, argv); +// return RUN_ALL_TESTS(); +// }