diff --git a/CMakeLists.txt b/CMakeLists.txt index 451ba6926aa223c032d03b658800bce517b0dcf6..94751f0097484d76721ade2063c523952da07e9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,13 +13,13 @@ if(BUILD_DEV) enable_testing() message("Building dev version") # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -O0 -fprofile-arcs -ftest-coverage") -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ -# -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -lpq -lpqxx") + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ + # -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align -lpq -lpqxx") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage -lgcov") if(SANITIZE_BUILD) message("Sanitizers ON") -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined -fno-sanitize-recover=all -fsanitize-undefined-trap-on-error") + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined -fno-sanitize-recover=all -fsanitize-undefined-trap-on-error") endif(SANITIZE_BUILD) endif(BUILD_DEV) @@ -29,7 +29,7 @@ set(BUILD_ALL "BUILD_ALL") message("${BUILD_DEV} ${SANITIZE_BUILD} ${CMAKE_EXE_LINKER_FLAGS}") -set(BUILD_MODE ${BUILD_SERVER}) +set(BUILD_MODE ${BUILD_ALL}) if((BUILD_MODE STREQUAL ${BUILD_CLIENT}) OR (BUILD_MODE STREQUAL ${BUILD_ALL})) add_subdirectory(client) @@ -37,4 +37,4 @@ endif() if((BUILD_MODE STREQUAL ${BUILD_SERVER}) OR (BUILD_MODE STREQUAL ${BUILD_ALL})) add_subdirectory(server) -endif() +endif() \ No newline at end of file diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4147519820083ff249ac5749021a75b22602430d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -0,0 +1,15 @@ +set(CMAKE_PREFIX_PATH build) +project(SourcedOut_client CXX) + +set(CMAKE_CXX_STANDARD 20) + +find_package(Boost 1.8.1 REQUIRED) +find_package(GTest REQUIRED) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +add_subdirectory(internal) +add_subdirectory(cmd) + +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-lpthread -pthread") \ No newline at end of file diff --git a/client/cmd/CMakeLists.txt b/client/cmd/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/client/internal/CMakeLists.txt b/client/internal/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b5403669e61b8f7e3b480e846cbb51aa358cf07e --- /dev/null +++ b/client/internal/CMakeLists.txt @@ -0,0 +1,16 @@ +add_subdirectory(entities) +add_subdirectory(httpClient) +add_subdirectory(core) +add_subdirectory(gui) + +set(libClientEntities_LIB ${libClientEntities_LIB} PARENT_SCOPE) +set(libClientEntities_INCLUDE_DIRS ${libClientEntities_INCLUDE_DIRS} PARENT_SCOPE) + +set(HTTPCLIENT_lib_LIB ${HTTPCLIENT_lib_LIB} PARENT_SCOPE) +set(HTTPCLIENT_lib_INCLUDE_DIRS ${HTTPCLIENT_lib_INCLUDE_DIRS} PARENT_SCOPE) + +set(CORE_lib_LIB ${CORE_lib_LIB} PARENT_SCOPE) +set(CORE_lib_INCLUDE_DIRS ${CORE_lib_INCLUDE_DIRS} PARENT_SCOPE) + +set(GUI_lib_LIB ${GUI_lib_LIB} PARENT_SCOPE) +set(GUI_lib_INCLUDE_DIRS ${GUI_lib_INCLUDE_DIRS} PARENT_SCOPE) \ No newline at end of file diff --git a/client/internal/core/CMakeLists.txt b/client/internal/core/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a493bebffe9d6c3c3afdda10485001c78a04876d --- /dev/null +++ b/client/internal/core/CMakeLists.txt @@ -0,0 +1,18 @@ +project("CoreLib") + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) +file(GLOB INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +include_directories(${INCLUDE_DIRS} ${libClientEntities_INCLUDE_DIRS} ${HTTPCLIENT_lib_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}) + +add_library(${PROJECT_NAME} ${SOURCES} ${INCLUDES}) +target_link_libraries(${PROJECT_NAME} ${libClientEntities_LIB} ${HTTPCLIENT_lib_LIB} ${Boost_LIBRARIES}) + +set(CORE_lib_LIB ${PROJECT_NAME}) +set(CORE_lib_LIB ${CORE_lib_LIB} PARENT_SCOPE) + +set(CORE_lib_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(CORE_lib_INCLUDE_DIRS ${CORE_lib_INCLUDE_DIRS} PARENT_SCOPE) \ No newline at end of file diff --git a/client/internal/core/include/Core.h b/client/internal/core/include/Core.h new file mode 100644 index 0000000000000000000000000000000000000000..f28be4a31b44bf597b2a95ea750674361a5f6feb --- /dev/null +++ b/client/internal/core/include/Core.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include "Task.h" +#include "Solution.h" + +class Core { +public: + static unsigned signUp(const std::string &login, const std::string &username, const std::string &pass); + + static unsigned login(const std::string &login, const std::string &pass); + + static std::vector getAllTasks(); + + static Solution submitSolution(const int& task_id, const std::string& path_to_file); + + static unsigned int createTask(const std::string &desc); + + static void logout(); + +private: + static std::size_t user_id; +}; diff --git a/client/internal/core/src/Core.cpp b/client/internal/core/src/Core.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52baa40f99bdad30d68747c29654a45101f44542 --- /dev/null +++ b/client/internal/core/src/Core.cpp @@ -0,0 +1,40 @@ +// +// Created by Николай Степанов on 11.05.2023. +// +#include "Core.h" +#include "HttpClientManager.h" + +const std::string CLIENT_IP = "0.0.0.0"; +const std::string CLIENT_PORT = "8080"; +HttpClientManager client(CLIENT_IP, CLIENT_PORT); + +std::size_t Core::user_id = -1; + +unsigned Core::signUp(const std::string &login, const std::string &username, const std::string &pass) { + auto res = client.registerUser(login, username, pass); + Core::user_id = res.second.id; + return res.first; +} + +unsigned Core::login(const std::string &login, const std::string &pass) { + auto res = client.loginUser(login, pass); + Core::user_id = res.second.id; + return res.first; +} + +void Core::logout() { + Core::user_id = -1; +} + +std::vector Core::getAllTasks() { + return client.getAllTasks(); +} + +Solution Core::submitSolution(const int &task_id, const std::string &path_to_file) { + return client.submitSolution(user_id, task_id, path_to_file); +} + +unsigned int Core::createTask(const std::string &desc) { + return client.createTask(desc); +} + diff --git a/client/internal/entities/CMakeLists.txt b/client/internal/entities/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..40875190d755991243c507b304a1b64c3d67d28e --- /dev/null +++ b/client/internal/entities/CMakeLists.txt @@ -0,0 +1,19 @@ +project("ClientEntitiesLib") + +set(LIB_NAME libClientEntities) + +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -O2 -pedantic -Wformat=2 -Wfloat-equal -Wconversion \ +-Wlogical-op -Wshift-overflow=2 -Wduplicated-cond -Wcast-qual -Wcast-align") + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lboost_filesystem") + +add_library(${LIB_NAME} ${SOURCES} ${HEADERS}) +target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set(libClientEntities_LIB ${LIB_NAME}) +set(libClientEntities_LIB ${libClientEntities_LIB} PARENT_SCOPE) +set(libClientEntities_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(libClientEntities_INCLUDE_DIRS ${libClientEntities_INCLUDE_DIRS} PARENT_SCOPE) \ No newline at end of file diff --git a/client/internal/entities/include/Metric.h b/client/internal/entities/include/Metric.h new file mode 100644 index 0000000000000000000000000000000000000000..db320cf9732f1a3b1031e4ff370f717ae99f4ff0 --- /dev/null +++ b/client/internal/entities/include/Metric.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct Metric { + std::string name; + unsigned int value; +}; diff --git a/client/internal/entities/include/Solution.h b/client/internal/entities/include/Solution.h new file mode 100644 index 0000000000000000000000000000000000000000..4cf7061a36531ecf61daf2c070eed8a569958042 --- /dev/null +++ b/client/internal/entities/include/Solution.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +struct Solution { + size_t id; + std::string source; + std::string result; +}; \ No newline at end of file diff --git a/client/internal/entities/include/Task.h b/client/internal/entities/include/Task.h new file mode 100644 index 0000000000000000000000000000000000000000..de9c48848fffdc53ce7675dcf5112077f6ecffe9 --- /dev/null +++ b/client/internal/entities/include/Task.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +struct Task{ + std::size_t id; + std::string description; + + Task(std::size_t id, std::string_view desc); +}; diff --git a/client/internal/entities/include/User.h b/client/internal/entities/include/User.h new file mode 100644 index 0000000000000000000000000000000000000000..94825402abfefa1f9518fd38f9f35389aef7eae7 --- /dev/null +++ b/client/internal/entities/include/User.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +struct User { + size_t id; + std::string login; + std::string password; + std::string username; +}; \ No newline at end of file diff --git a/client/internal/entities/src/Task.cpp b/client/internal/entities/src/Task.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e1de440dac9588b853a6a748a7b5fc68c5db1848 --- /dev/null +++ b/client/internal/entities/src/Task.cpp @@ -0,0 +1,3 @@ +#include "Task.h" + +Task::Task(std::size_t id, std::string_view desc) : id(id), description(desc) {}; \ No newline at end of file diff --git a/client/internal/gui/CMakeLists.txt b/client/internal/gui/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..419c4dd5e925504b3455a42a991af42ab5c7bb12 --- /dev/null +++ b/client/internal/gui/CMakeLists.txt @@ -0,0 +1,30 @@ +project("GuiLib") + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) +file(GLOB INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + +set(CMAKE_PREFIX_PATH "~/Qt/6.5.0/macos/lib/cmake") +find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui) + +include_directories(${INCLUDE_DIRS} ${libClientEntities_INCLUDE_DIRS} ${CORE_lib_INCLUDE_DIRS}) + +add_library(${PROJECT_NAME} ${SOURCES} ${INCLUDES}) +target_link_libraries(${PROJECT_NAME} ${libClientEntities_LIB} ${CORE_lib_LIB} Qt6::Widgets Qt6::Core Qt6::Gui) + +set(GUI_lib_LIB ${PROJECT_NAME}) +set(GUI_lib_LIB ${GUI_lib_LIB} PARENT_SCOPE) + +set(GUI_lib_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(GUI_lib_INCLUDE_DIRS ${GUI_lib_INCLUDE_DIRS} PARENT_SCOPE) + +add_executable(gui_run main.cpp) +target_link_libraries(gui_run ${GUI_lib_LIB}) + +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/client/internal/gui/include/AddTaskDialog.h b/client/internal/gui/include/AddTaskDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..c5b6547938e1d78a2b6b6e47afa745dcd32702ab --- /dev/null +++ b/client/internal/gui/include/AddTaskDialog.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class AddTaskDialog : public QDialog { +Q_OBJECT + +public: + explicit AddTaskDialog(QWidget *parent = nullptr); + +public slots: + void on_createButton_clicked(); + void on_backButton_clicked(); + +private: + QVBoxLayout* verticalLayout = nullptr; + QLabel* label = nullptr; + QTextEdit* editor = nullptr; + QPushButton *createButton = nullptr; + QPushButton *backButton = nullptr; + + void setupUi(QDialog *AddTaskDialog); +}; diff --git a/client/internal/gui/include/AuthDialog.h b/client/internal/gui/include/AuthDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..b8917b88db833d72a7d6fe91250dd5f809bac228 --- /dev/null +++ b/client/internal/gui/include/AuthDialog.h @@ -0,0 +1,37 @@ +#ifndef AUTH_DIALOG_H +#define AUTH_DIALOG_H + +#include +#include +#include +#include +#include +#include +#include + +class AuthDialog : public QDialog { + Q_OBJECT + +public: + explicit AuthDialog(QWidget *parent = nullptr); + +public slots: + void on_loginButton_clicked(); + void on_backButton_clicked(); + +private: + QVBoxLayout *verticalLayout = nullptr; + QWidget *fields = nullptr; + QFormLayout* fieldsLayout = nullptr; + QLabel *loginLabel = nullptr; + QLineEdit *login = nullptr; + QLabel *passwordLabel = nullptr; + QLineEdit *password = nullptr; + QPushButton *loginButton = nullptr; + QPushButton *backButton = nullptr; + + void setupUi(QDialog *AuthDialog); + void retranslateUi(QDialog *AuthDialog); +}; + +#endif // AUTH_DIALOG_H diff --git a/client/internal/gui/include/EntryWindow.h b/client/internal/gui/include/EntryWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..4bc4f111548a879970b81fdf6a855b1a64f9bb2d --- /dev/null +++ b/client/internal/gui/include/EntryWindow.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include + +#include "AuthDialog.h" +#include "SignUpDialog.h" + +class EntryWindow : public QMainWindow { +Q_OBJECT + +public: + EntryWindow(QWidget *parent = nullptr); + +signals: + void showTasksWindow(); + +public slots: + void on_exitButton_clicked(); + void on_loginButton_clicked(); + void on_signUpButton_clicked(); + +private: + + QWidget *centralwidget = nullptr; + QVBoxLayout *verticalLayout = nullptr; + QLabel *introLabel = nullptr; + QPushButton *loginButton = nullptr; + QSpacerItem *horizontalSpacer = nullptr; + QPushButton *signUpButton = nullptr; + QSpacerItem *horizontalSpacer_2 = nullptr; + QPushButton *exitButton = nullptr; + QMenuBar *menubar = nullptr; + QStatusBar *statusbar = nullptr; + + void setupUi(QMainWindow *EntryWindow); + void retranslateUi(QMainWindow *EntryWindow); +}; + +#endif // MAINWINDOW_H diff --git a/client/internal/gui/include/SignUpDialog.h b/client/internal/gui/include/SignUpDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..f9da4f50d8f2438ddce831cb3c4b9ce949ce1061 --- /dev/null +++ b/client/internal/gui/include/SignUpDialog.h @@ -0,0 +1,40 @@ +#ifndef SIGN_UP_DIALOG_H +#define SIGN_UP_DIALOG_H + +#include +#include +#include +#include +#include +#include + +class SignUpDialog : public QDialog { +Q_OBJECT + +public: + explicit SignUpDialog(QWidget *parent = nullptr); + +private slots: + void on_signUpButton_clicked(); + void on_backButton_clicked(); + +private: + QVBoxLayout *verticalLayout = nullptr; + QWidget *fields = nullptr; + QFormLayout* fieldsLayout = nullptr; + QLabel *loginLabel = nullptr; + QLineEdit *login = nullptr; + QLabel* usernameLabel = nullptr; + QLineEdit *username = nullptr; + QLabel *passwordLabel = nullptr; + QLineEdit *password = nullptr; + QLabel *passwordRepeatLabel = nullptr; + QLineEdit *passwordRepeat = nullptr; + QPushButton *signUpButton = nullptr; + QPushButton *backButton = nullptr; + + void setupUi(QDialog *SignUpDialog); + void retranslateUi(QDialog *SignUpDialog); +}; + +#endif // SIGN_UP_DIALOG_H diff --git a/client/internal/gui/include/SolutionsWindow.h b/client/internal/gui/include/SolutionsWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..80544e56dbaf0462fb120dd46b6a0d04c850e5c3 --- /dev/null +++ b/client/internal/gui/include/SolutionsWindow.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Task.h" +#include "TasksWindow.h" + +class SolutionsWindow : public QMainWindow { +Q_OBJECT + +public: + explicit SolutionsWindow(Task task, QWidget *parent = nullptr); + +private slots: + void on_backButton_clicked(); + void on_chooseFileButton_clicked(); + void on_sendButton_clicked(); + +private: + + Task task; + std::string path_to_file; + + QWidget *centralwidget = nullptr; + QGroupBox* taskBox = nullptr; + QVBoxLayout* taskLayout = nullptr; + QVBoxLayout* verticalLayout = nullptr; + QLabel* taskDescription = nullptr; + QPushButton* chooseFileButton = nullptr; + QLabel* filename = nullptr; + QPushButton* sendButton = nullptr; + QLabel* result = nullptr; + QPushButton* backButton = nullptr; + + void setupUi(QMainWindow *UserWindow); +}; diff --git a/client/internal/gui/include/TasksWindow.h b/client/internal/gui/include/TasksWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..1a897fd8a86d06d0eaada4bb4c46dfe8fbb4d237 --- /dev/null +++ b/client/internal/gui/include/TasksWindow.h @@ -0,0 +1,48 @@ +#ifndef USERWINDOW_H +#define USERWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Task.h" + +class TasksWindow : public QMainWindow { +Q_OBJECT + +public: + explicit TasksWindow(QWidget *parent = nullptr); + +public slots: + void on_backButton_clicked(); + void on_addTaskButton_clicked(); + void on_goToTaskButton_clicked(); + void indexChanged(); + +private: + + QWidget *centralwidget = nullptr; + QVBoxLayout* verticalLayout = nullptr; + QGroupBox *taskChooseGroupBox = nullptr; + QVBoxLayout* taskVerticalLayout = nullptr; + QLabel* label = nullptr; + QComboBox* tasks = nullptr; + QLabel* taskDescription = nullptr; + QPushButton* goToTaskButton = nullptr; + QPushButton* addTaskButton = nullptr; + QPushButton* backButton = nullptr; + QWidget* buttonsWidget = nullptr; + QHBoxLayout* buttonsLayout = nullptr; + + std::vector tasks_vector; + + void setupUi(QMainWindow *UserWindow); + void updateTasks(); +}; + +#endif // USERWINDOW_H diff --git a/client/internal/gui/include/UIManager.h b/client/internal/gui/include/UIManager.h new file mode 100644 index 0000000000000000000000000000000000000000..bc6e071114c739df3519b60134fe6528dfbcfdc5 --- /dev/null +++ b/client/internal/gui/include/UIManager.h @@ -0,0 +1,22 @@ +#ifndef INCLUDE_UIMANAGER_H_ +#define INCLUDE_UIMANAGER_H_ + +#include +#include "EntryWindow.h" +#include "TasksWindow.h" + +class UIManager : public QWidget { +Q_OBJECT +public: + UIManager(QWidget *parent = nullptr); + +public slots: + void showEntryWindow(); + void showTasksWindow(); + +private: + EntryWindow* entryWindow = nullptr; + TasksWindow* userWindow = nullptr; +}; + +#endif // INCLUDE_UIMANAGER_H_ diff --git a/client/internal/gui/main.cpp b/client/internal/gui/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b348568625fca7832c6ed4625f30bc4129db0e10 --- /dev/null +++ b/client/internal/gui/main.cpp @@ -0,0 +1,13 @@ +#include "EntryWindow.h" +#include "TasksWindow.h" + +#include + +#include "UIManager.h" + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + UIManager uiManager; + uiManager.showEntryWindow(); + return a.exec(); +} diff --git a/client/internal/gui/src/AddTaskDialog.cpp b/client/internal/gui/src/AddTaskDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3fefa3190ba17f0bcbbf1fb965a6113686ef6e73 --- /dev/null +++ b/client/internal/gui/src/AddTaskDialog.cpp @@ -0,0 +1,60 @@ +#include "AddTaskDialog.h" + +#include +#include + +#include "Core.h" + +AddTaskDialog::AddTaskDialog(QWidget *parent) : QDialog(parent) { + setupUi(this); + + connect(createButton, &QPushButton::clicked, this, &AddTaskDialog::on_createButton_clicked); + connect(backButton, &QPushButton::clicked, this, &AddTaskDialog::on_backButton_clicked); +} + +void AddTaskDialog::setupUi(QDialog *AddTaskDialog) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(AddTaskDialog->sizePolicy().hasHeightForWidth()); + AddTaskDialog->setSizePolicy(sizePolicy); + + verticalLayout = new QVBoxLayout(AddTaskDialog); + + label = new QLabel(AddTaskDialog); + label->setText(QString::fromUtf8("Введите текст задания")); + verticalLayout->addWidget(label); + + editor = new QTextEdit(AddTaskDialog); + verticalLayout->addWidget(editor); + + createButton = new QPushButton(AddTaskDialog); + createButton->setText(QString::fromUtf8("Создать задание")); + verticalLayout->addWidget(createButton); + + backButton = new QPushButton(AddTaskDialog); + backButton->setText(QString::fromUtf8("Назад")); + verticalLayout->addWidget(backButton); +} + +void AddTaskDialog::on_createButton_clicked() { + std::string desc = editor->toPlainText().toUtf8().constData(); + if (desc.empty()) { + QMessageBox::warning(this, "Ошибка отправки", "Описание не может быть пустыми"); + return; + } + unsigned result = Core::createTask(desc); + switch (result) { + case 200: + accept(); + close(); + break; + default: + QMessageBox::critical(this, "Отправка невозможна", "Нет соединения с сервером"); + } +} + +void AddTaskDialog::on_backButton_clicked() { + reject(); + close(); +} diff --git a/client/internal/gui/src/AuthDialog.cpp b/client/internal/gui/src/AuthDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f3576806f3565779ea82020f1d1e301aad59098 --- /dev/null +++ b/client/internal/gui/src/AuthDialog.cpp @@ -0,0 +1,86 @@ +#include "AuthDialog.h" + +#include +#include + +#include "Core.h" + +AuthDialog::AuthDialog(QWidget *parent) : QDialog(parent) { + setupUi(this); +} + +void AuthDialog::setupUi(QDialog *AuthDialog) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(AuthDialog->sizePolicy().hasHeightForWidth()); + AuthDialog->setSizePolicy(sizePolicy); + + verticalLayout = new QVBoxLayout(AuthDialog); + + fields = new QWidget(AuthDialog); + fields->setEnabled(true); + + fieldsLayout = new QFormLayout(fields); + + loginLabel = new QLabel(this); + login = new QLineEdit(this); + + fieldsLayout->addRow(loginLabel, login); + + passwordLabel = new QLabel(this); + password = new QLineEdit(this); + password->setEchoMode(QLineEdit::Password); + + fieldsLayout->addRow(passwordLabel, password); + + verticalLayout->addWidget(fields); + + loginButton = new QPushButton(AuthDialog); + loginButton->setDefault(true); + + verticalLayout->addWidget(loginButton); + + backButton = new QPushButton(AuthDialog); + + verticalLayout->addWidget(backButton); + + retranslateUi(AuthDialog); + + connect(loginButton, &QPushButton::clicked, this, &AuthDialog::on_loginButton_clicked); + connect(backButton, &QPushButton::clicked, this, &AuthDialog::on_backButton_clicked); +} + +void AuthDialog::retranslateUi(QDialog *AuthDialog) { + AuthDialog->setWindowTitle("Вход"); + loginLabel->setText("Логин"); + passwordLabel->setText("Пароль"); + loginButton->setText("Войти"); + backButton->setText("Назад"); +} + +void AuthDialog::on_loginButton_clicked() { + std::string log = login->text().toUtf8().constData(); + std::string pass = password->text().toUtf8().constData(); + if (log.empty() || pass.empty()) { + QMessageBox::warning(this, "Ошибка авторизации", "Логин и пароль не могут быть пустыми"); + return; + } + unsigned result = Core::login(std::string(log), std::string(pass)); + switch (result) { + case 200: + accept(); + close(); + break; + case 404: + QMessageBox::warning(this, "Ошибка авторизации","Неправильный логин или пароль"); + break; + default: + QMessageBox::critical(this, "Авторизация невозможна", "Нет соединения с сервером"); + } +} + +void AuthDialog::on_backButton_clicked() { + reject(); + close(); +} diff --git a/client/internal/gui/src/EntryWindow.cpp b/client/internal/gui/src/EntryWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c5d246610c74c1dd3a60b174daac225890efbb6 --- /dev/null +++ b/client/internal/gui/src/EntryWindow.cpp @@ -0,0 +1,95 @@ +#include "EntryWindow.h" + +#include +#include +#include +#include +#include "AuthDialog.h" +#include "SignUpDialog.h" +#include "UIManager.h" + +EntryWindow::EntryWindow(QWidget *parent) : QMainWindow(parent) { + setupUi(this); +} + +void EntryWindow::setupUi(QMainWindow *EntryWindow) { + EntryWindow->resize(600, 600); + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(EntryWindow->sizePolicy().hasHeightForWidth()); + EntryWindow->setSizePolicy(sizePolicy); + + + centralwidget = new QWidget(EntryWindow); + verticalLayout = new QVBoxLayout(centralwidget); + + introLabel = new QLabel(centralwidget); + introLabel->setText("Добро пожаловать в Noiseground!"); + introLabel->setGeometry(QRect(140, 200, 331, 31)); + + verticalLayout->addWidget(introLabel, 0, Qt::AlignHCenter); + + loginButton = new QPushButton(centralwidget); + QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(loginButton->sizePolicy().hasHeightForWidth()); + loginButton->setSizePolicy(sizePolicy1); + verticalLayout->addWidget(loginButton); + + horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + verticalLayout->addItem(horizontalSpacer); + + signUpButton = new QPushButton(centralwidget); + sizePolicy1.setHeightForWidth(signUpButton->sizePolicy().hasHeightForWidth()); + signUpButton->setSizePolicy(sizePolicy1); + + verticalLayout->addWidget(signUpButton); + + horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + verticalLayout->addItem(horizontalSpacer_2); + + exitButton = new QPushButton(centralwidget); + sizePolicy1.setHeightForWidth(exitButton->sizePolicy().hasHeightForWidth()); + exitButton->setSizePolicy(sizePolicy1); + + verticalLayout->addWidget(exitButton); + + EntryWindow->setCentralWidget(centralwidget); + + connect(signUpButton, &QPushButton::clicked, this, &EntryWindow::on_signUpButton_clicked); + connect(loginButton, &QPushButton::clicked, this, &EntryWindow::on_loginButton_clicked); + connect(exitButton, &QPushButton::clicked, this, &EntryWindow::on_exitButton_clicked); + + retranslateUi(EntryWindow); +} + +void EntryWindow::retranslateUi(QMainWindow *EntryWindow) { + EntryWindow->setWindowTitle(QCoreApplication::translate("EntryWindow", "Главное окно", nullptr)); + loginButton->setText(QCoreApplication::translate("EntryWindow", "Войти", nullptr)); + signUpButton->setText(QCoreApplication::translate("EntryWindow", "Зарегистрироваться", nullptr)); + exitButton->setText(QCoreApplication::translate("EntryWindow", "Выйти", nullptr)); +} + +void EntryWindow::on_exitButton_clicked() { + QApplication::quit(); +} + +void EntryWindow::on_loginButton_clicked() { + AuthDialog authDialog(this); + if (authDialog.exec() == QDialog::Accepted) { + hide(); + qobject_cast(parent())->showTasksWindow(); + } +} + +void EntryWindow::on_signUpButton_clicked() { + SignUpDialog signupDialog( this ); + if (signupDialog.exec() == QDialog::Accepted) { + hide(); + qobject_cast(parent())->showTasksWindow(); + } +} diff --git a/client/internal/gui/src/SignUpDialog.cpp b/client/internal/gui/src/SignUpDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cdc2a31a2432c3fc72eb885c5153fdc999325f1 --- /dev/null +++ b/client/internal/gui/src/SignUpDialog.cpp @@ -0,0 +1,110 @@ +#include "SignUpDialog.h" + +#include +#include +#include + +#include "Core.h" + +SignUpDialog::SignUpDialog(QWidget *parent) : QDialog(parent) { + setupUi(this); +} + +void SignUpDialog::setupUi(QDialog *SignUpDialog) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(SignUpDialog->sizePolicy().hasHeightForWidth()); + SignUpDialog->setSizePolicy(sizePolicy); + + verticalLayout = new QVBoxLayout(SignUpDialog); + + fields = new QWidget(SignUpDialog); + fields->setEnabled(true); + + fieldsLayout = new QFormLayout(fields); + + loginLabel = new QLabel(this); + login = new QLineEdit(this); + + fieldsLayout->addRow(loginLabel, login); + + usernameLabel = new QLabel(this); + username = new QLineEdit(this); + + fieldsLayout->addRow(usernameLabel, username); + + passwordLabel = new QLabel(this); + password = new QLineEdit(this); + password->setEchoMode(QLineEdit::Password); + + fieldsLayout->addRow(passwordLabel, password); + + passwordRepeatLabel = new QLabel(this); + passwordRepeat = new QLineEdit(this); + passwordRepeat->setEchoMode(QLineEdit::Password); + + fieldsLayout->addRow(passwordRepeatLabel, passwordRepeat); + + verticalLayout->addWidget(fields); + + signUpButton = new QPushButton(SignUpDialog); + signUpButton->setDefault(true); + + verticalLayout->addWidget(signUpButton); + + backButton = new QPushButton(SignUpDialog); + + verticalLayout->addWidget(backButton); + + retranslateUi(SignUpDialog); + + connect(signUpButton, &QPushButton::clicked, this, &SignUpDialog::on_signUpButton_clicked); + connect(backButton, &QPushButton::clicked, this, &SignUpDialog::on_backButton_clicked); +} + +void SignUpDialog::retranslateUi(QDialog *SignUpDialog) { + SignUpDialog->setWindowTitle("Регистрация"); + loginLabel->setText("Введите логин"); + usernameLabel->setText("Введите имя"); + passwordLabel->setText("Введите пароль"); + passwordRepeatLabel->setText("Повторите пароль"); + signUpButton->setText("Зарегистрироваться"); + backButton->setText("Назад"); +} + +void SignUpDialog::on_signUpButton_clicked() { + std::string log = this->login->text().toUtf8().constData(); + std::string usr = this->username->text().toUtf8().constData(); + std::string pass = this->password->text().toUtf8().constData(); + std::string passRepeat = this->passwordRepeat->text().toUtf8().constData(); + if (pass == passRepeat && !log.empty() && !usr.empty()) { + unsigned response = Core::signUp(log, usr, pass); + switch (response) { + case 200: + QMessageBox::information(this, "Успех!", "Вы успешно зарегистрированы!"); + accept(); + close(); + break; + case 403: + QMessageBox::warning(this, "Регистрация невозможна", "Логин уже занят!"); + break; + default: + QMessageBox::critical(this, "Регистрация невозможна!", "Нет соединения с сервером!"); + } + + } else if (log.empty()) { + QMessageBox::warning(this, "Ошибка!", "Логин не может быть пустым!"); + } else if (usr.empty()) { + QMessageBox::warning(this, "Ошибка!", "Имя не может быть пустым!"); + } else { + QMessageBox::warning(this, "Ошибка!", "Пароли не совпадают!"); + } +} + +void SignUpDialog::on_backButton_clicked() { + reject(); + close(); +} + +// setupUi diff --git a/client/internal/gui/src/SolutionsWindow.cpp b/client/internal/gui/src/SolutionsWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17832a3d99d5a33d902696b66c46db0b36f9442d --- /dev/null +++ b/client/internal/gui/src/SolutionsWindow.cpp @@ -0,0 +1,77 @@ +#include "SolutionsWindow.h" + +#include +#include +#include +#include + +#include "Solution.h" +#include "Core.h" + +SolutionsWindow::SolutionsWindow(Task task, QWidget *parent) : QMainWindow(parent), task(std::move(task)) { + setupUi(this); + connect(backButton, &QPushButton::clicked, this, &SolutionsWindow::on_backButton_clicked); + connect(chooseFileButton, &QPushButton::clicked, this, &SolutionsWindow::on_chooseFileButton_clicked); + connect(sendButton, &QPushButton::clicked, this, &SolutionsWindow::on_sendButton_clicked); +} + +void SolutionsWindow::setupUi(QMainWindow *SolutionsWindow) { + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(SolutionsWindow->sizePolicy().hasHeightForWidth()); + SolutionsWindow->setSizePolicy(sizePolicy); + + centralwidget = new QWidget(SolutionsWindow); + verticalLayout = new QVBoxLayout(centralwidget); + + taskBox = new QGroupBox(centralwidget); + taskLayout = new QVBoxLayout(taskBox); + + taskDescription = new QLabel(taskBox); + std::string description = task.description; + taskDescription->setText(QString(description.c_str())); + + taskLayout->addWidget(taskDescription); + + filename = new QLabel(this); + + chooseFileButton = new QPushButton(this); + chooseFileButton->setText(QString::fromUtf8("Выбирите файл")); + + sendButton = new QPushButton(this); + sendButton->setText(QString::fromUtf8("Отправить")); + + result = new QLabel(this); + result->setText(QString::fromUtf8("Отправьте для принятия решения")); + + backButton = new QPushButton(this); + backButton->setText(QString::fromUtf8("Назад")); + + verticalLayout->addWidget(taskBox); + verticalLayout->addWidget(filename); + verticalLayout->addWidget(chooseFileButton); + verticalLayout->addWidget(sendButton); + verticalLayout->addWidget(result); + verticalLayout->addWidget(backButton); + + SolutionsWindow->setCentralWidget(centralwidget); +} + +void SolutionsWindow::on_backButton_clicked() { + close(); + qobject_cast(parent())->show(); +} + +void SolutionsWindow::on_chooseFileButton_clicked() { + QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a file to send"), QDir::homePath()); + filename->setText(QFileInfo(fileName).fileName()); + path_to_file = fileName.toUtf8().constData(); +} + +void SolutionsWindow::on_sendButton_clicked() { + Solution sol = Core::submitSolution(task.id, path_to_file); + result->setText(QString::fromStdString(sol.result)); +} + + diff --git a/client/internal/gui/src/TasksWindow.cpp b/client/internal/gui/src/TasksWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04cebc72dd2ac2632797f1d98e095c77d98fa1bf --- /dev/null +++ b/client/internal/gui/src/TasksWindow.cpp @@ -0,0 +1,117 @@ +#include "TasksWindow.h" + +#include +#include +#include + +#include "SolutionsWindow.h" +#include "AddTaskDialog.h" +#include "UIManager.h" +#include "Core.h" + +TasksWindow::TasksWindow(QWidget *parent) : QMainWindow(parent) { + setupUi(this); + connect(tasks, &QComboBox::currentIndexChanged, this, &TasksWindow::indexChanged); + connect(goToTaskButton, &QPushButton::clicked, this, &TasksWindow::on_goToTaskButton_clicked); + connect(backButton, &QPushButton::clicked, this, &TasksWindow::on_backButton_clicked); + connect(addTaskButton, &QPushButton::clicked, this, &TasksWindow::on_addTaskButton_clicked); +} + + + +void TasksWindow::setupUi(QMainWindow *UserWindow) { + tasks_vector = Core::getAllTasks(); + + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(UserWindow->sizePolicy().hasHeightForWidth()); + UserWindow->setSizePolicy(sizePolicy); + + centralwidget = new QWidget(UserWindow); + verticalLayout = new QVBoxLayout(centralwidget); + + taskChooseGroupBox = new QGroupBox(centralwidget); + sizePolicy.setHeightForWidth(taskChooseGroupBox->sizePolicy().hasHeightForWidth()); + taskChooseGroupBox->setFixedHeight(85); + taskChooseGroupBox->setSizePolicy(sizePolicy); + + taskVerticalLayout = new QVBoxLayout(taskChooseGroupBox); + + label = new QLabel(this); + label->setText(QString::fromUtf8("Выберите номер задания:")); + + tasks = new QComboBox(this); + for (int i = 0; i < tasks_vector.size(); i++) { + tasks->insertItem(i, QString::number(tasks_vector[i].id)); + } + tasks->setCurrentIndex(0); + + taskVerticalLayout->addWidget(label); + taskVerticalLayout->addWidget(tasks); + + taskDescription = new QLabel(this); + std::string description = tasks_vector[0].description; + taskDescription->setText(QString(description.c_str())); + + buttonsWidget = new QWidget(centralwidget); + sizePolicy.setHeightForWidth(buttonsWidget->sizePolicy().hasHeightForWidth()); + buttonsWidget->setFixedHeight(45); + + buttonsLayout = new QHBoxLayout(buttonsWidget); + + goToTaskButton = new QPushButton(this); + goToTaskButton->setText(QString::fromUtf8("Перейти к сдаче")); + + addTaskButton = new QPushButton(this); + addTaskButton->setText(QString::fromUtf8("Добавить задание")); + + backButton = new QPushButton(this); + backButton->setText(QString::fromUtf8("Выйти")); + + buttonsLayout->addWidget(goToTaskButton); + buttonsLayout->addWidget(addTaskButton); + buttonsLayout->addWidget(backButton); + + verticalLayout->addWidget(taskChooseGroupBox); + verticalLayout->addWidget(taskDescription); + verticalLayout->addWidget(buttonsWidget); + + UserWindow->setCentralWidget(centralwidget); +} + +void TasksWindow::indexChanged() { + std::string description = tasks_vector[tasks->currentIndex()].description; + taskDescription->setText(QString(description.c_str())); +} + +void TasksWindow::on_backButton_clicked() { + Core::logout(); + close(); + qobject_cast(parent())->showEntryWindow(); +} + +void TasksWindow::on_goToTaskButton_clicked() { + hide(); + auto* solutionsWindow = new SolutionsWindow(tasks_vector[tasks->currentIndex()], this); + solutionsWindow->show(); +} + +void TasksWindow::on_addTaskButton_clicked() { + AddTaskDialog addTaskDialog(this); + if (addTaskDialog.exec() == QDialog::Accepted) { + updateTasks(); + } +} + +void TasksWindow::updateTasks() { + tasks_vector = Core::getAllTasks(); + tasks->clear(); + for (int i = 0; i < tasks_vector.size(); i++) { + tasks->insertItem(i, QString::number(tasks_vector[i].id)); + } + tasks->setCurrentIndex(0); + + std::string description = tasks_vector[0].description; + taskDescription->setText(QString(description.c_str())); +} diff --git a/client/internal/gui/src/UIManager.cpp b/client/internal/gui/src/UIManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..101e034b7ae11e86dea4f5e357bf4268ef5ee163 --- /dev/null +++ b/client/internal/gui/src/UIManager.cpp @@ -0,0 +1,19 @@ +#include "UIManager.h" + +#include "EntryWindow.h" +#include "TasksWindow.h" +#include "SignUpDialog.h" +#include "AuthDialog.h" + +void UIManager::showEntryWindow() { + entryWindow->show(); +} + +void UIManager::showTasksWindow() { + userWindow->show(); +} + +UIManager::UIManager(QWidget *parent) : QWidget(parent) { + userWindow = new TasksWindow(this); + entryWindow = new EntryWindow(this); +} diff --git a/client/internal/gui/tests/CMakeLists.txt b/client/internal/gui/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5877dc8f3540e9e3e1ab3fba04817c5a58af3ecb --- /dev/null +++ b/client/internal/gui/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +project(test_gui) + +set(CMAKE_CXX_STANDARD 20) +add_compile_options(-coverage) + +file(GLOB SOURCES *.cpp) + +enable_testing() +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${GUI_lib_LIB} GTest::GTest GTest::gmock) +target_include_directories(${PROJECT_NAME} PUBLIC ${GUI_lib_INCLUDE_DIRS}) + +add_test(test_gui test_gui) \ No newline at end of file diff --git a/client/internal/gui/tests/main.cpp b/client/internal/gui/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2348fac51843add819fb6d6ca628caa47193fa6f --- /dev/null +++ b/client/internal/gui/tests/main.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "UIManager.h" +#include "EntryWindow.h" +#include "TasksWindow.h" + +#include + +TEST(ConstructorTest, EntryWindow) { + int fake = 0; + QApplication a(fake, nullptr); + EntryWindow mw; + EXPECT_NO_FATAL_FAILURE(mw.show()); + a.exec(); +} + +TEST(ConstructorTest, UserWindow) { + int fake = 0; + QApplication a(fake, nullptr); + TasksWindow uw; + EXPECT_NO_FATAL_FAILURE(uw.show()); + a.exec(); +} + +TEST(ConstructorTest, UIManagerMW) { + int fake_argc = 0; + QApplication a(fake_argc, nullptr); + UIManager um; + EXPECT_NO_FATAL_FAILURE(um.showEntryWindow()); + a.exec(); +} + +TEST(ConstructorTest, UIManagerUW) { + int fake_argc = 0; + QApplication a(fake_argc, nullptr); + UIManager um; + EXPECT_NO_FATAL_FAILURE(um.showTasksWindow()); + a.exec(); +} + +TEST(ConstructorTest, MainWindow_AuthDialog) { + int fake_argc = 0; + QApplication a(fake_argc, nullptr); + EntryWindow mainWindow; + EXPECT_NO_FATAL_FAILURE(mainWindow.getAuthDialog()->show()); +} + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/client/internal/httpClient/CMakeLists.txt b/client/internal/httpClient/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..591e57ae1873d5c1f827a6621cb2dc7b8dce7179 --- /dev/null +++ b/client/internal/httpClient/CMakeLists.txt @@ -0,0 +1,21 @@ +project("HttpClientLib") + +set(CMAKE_CXX_STANDARD 20) + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) +file(GLOB INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/virtual/*.h) + +include_directories(${INCLUDE_DIRS} ${Boost_INCLUDE_DIR} ${libClientEntities_INCLUDE_DIRS}) + +add_library(${PROJECT_NAME} ${SOURCES} ${INCLUDES}) +target_link_libraries(${PROJECT_NAME} ${libClientEntities_LIB} ${Boost_LIBRARIES}) + +set(HTTPCLIENT_lib_LIB ${PROJECT_NAME}) +set(HTTPCLIENT_lib_LIB ${HTTPCLIENT_lib_LIB} PARENT_SCOPE) + +set(HTTPCLIENT_lib_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(HTTPCLIENT_lib_INCLUDE_DIRS ${HTTPCLIENT_lib_INCLUDE_DIRS} PARENT_SCOPE) + +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/client/internal/httpClient/include/HttpClient.h b/client/internal/httpClient/include/HttpClient.h new file mode 100644 index 0000000000000000000000000000000000000000..42c5f6dcaf17be01c356a07040684fae932bb308 --- /dev/null +++ b/client/internal/httpClient/include/HttpClient.h @@ -0,0 +1,36 @@ +#ifndef APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ +#define APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ + +#include +#include +#include +#include +#include + +#include "IHttpClient.h" + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +using tcp = net::ip::tcp; + +class HttpClient : public IHttpClient { +public: + HttpClient(std::string_view host_, std::string_view port_); + http::response makeGetRequest(std::string_view target, std::string_view body) override; + http::response makePostRequest(std::string_view target, std::string_view body) override; + +private: + http::response makeRequest(std::string_view target, + http::verb method, + std::string_view body) override; + + net::io_context io_context{}; + tcp::resolver resolver; + beast::tcp_stream stream; + std::string host; + std::string port; +}; + + +#endif // APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ diff --git a/client/internal/httpClient/include/HttpClientManager.h b/client/internal/httpClient/include/HttpClientManager.h new file mode 100644 index 0000000000000000000000000000000000000000..80e73b9a25e2764ae812444499ea4ee24e20e9fb --- /dev/null +++ b/client/internal/httpClient/include/HttpClientManager.h @@ -0,0 +1,36 @@ +#ifndef APP_HTTPCLIENT_HTTPCLIENTMANAGER_HTTPCLIENTMANAGER_H_ +#define APP_HTTPCLIENT_HTTPCLIENTMANAGER_HTTPCLIENTMANAGER_H_ + +#include +#include +#include + +#include "IHttpClient.h" +#include "User.h" +#include "Solution.h" +#include "Task.h" +#include "Metric.h" +#include "Serializer.h" + +class HttpClientManager { +public: + HttpClientManager(std::string_view host_, std::string_view port_); + + std::pair loginUser(const std::string &login, const std::string &password); + std::pair registerUser(const std::string &login, const std::string &username, + const std::string &password); + Solution submitSolution(const int& user_id, const int &task_id, const std::string &path_to_solution); + unsigned int getAllSolutionsForTask(const int& user_id, const int& task_id); + std::vector getAllTasks(); + unsigned int createTask(const std::string& desc); +// std::vector getMetrics(const int& sol_id); + void setHttpClient(std::shared_ptr client_); +private: + std::string host; + std::string port; + std::shared_ptr client; + std::shared_ptr serializer; +}; + + +#endif // APP_HTTPCLIENT_HTTPCLIENTMANAGER_HTTPCLIENTMANAGER_H_ diff --git a/client/internal/httpClient/include/Serializer.h b/client/internal/httpClient/include/Serializer.h new file mode 100644 index 0000000000000000000000000000000000000000..80bdb423865f9b9e4dc6f8951518858df0109c07 --- /dev/null +++ b/client/internal/httpClient/include/Serializer.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include "User.h" +#include "Task.h" +#include "Solution.h" + +class Serializer { +public: + std::string serialLoginData(std::string_view login, std::string_view password); + std::string serialRegisterData(std::string_view login, std::string_view username, std::string_view password); + std::string serialSolutionData(const int& user_id, const int& task_id, const std::string& path_to_file); + std::string serialNewTaskData(std::string_view desc); + + User deserialUserData(std::string_view body); + Solution deserialSolutionData(std::string_view body); + std::vector deserialAllTasks(std::string_view body); +}; + diff --git a/client/internal/httpClient/src/HttpClient.cpp b/client/internal/httpClient/src/HttpClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2dd02453ad3ba76c2e309a8e637f7eadee6339db --- /dev/null +++ b/client/internal/httpClient/src/HttpClient.cpp @@ -0,0 +1,41 @@ +#include "httpClient.h" + +#include +#include + +HttpClient::HttpClient(std::string_view host_, std::string_view port_) : + host(host_), + port(port_), + resolver(io_context), + stream(io_context) {} + +http::response HttpClient::makeRequest(std::string_view target, + http::verb method, + std::string_view body) { + auto const results = resolver.resolve(host, port); + stream.connect(results); + http::request req{http::verb::get, target, 11}; + req.set(http::field::host, host); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + req.set(http::field::content_type, "text/plain"); + req.content_length(body.size()); + req.body() = body; + + http::write(stream, req); + + beast::flat_buffer buffer; + http::response res; + http::read(stream, buffer, res); + + return res; +} + +http::response HttpClient::makeGetRequest(std::string_view target, + std::string_view body) { + return makeRequest(target, http::verb::get, body); +} + +http::response HttpClient::makePostRequest(std::string_view target, + std::string_view body) { + return makeRequest(target, http::verb::post, body); +} diff --git a/client/internal/httpClient/src/HttpClientManager.cpp b/client/internal/httpClient/src/HttpClientManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d6da4fb5b6c8c06aa9e88a0046cb999d403a86d --- /dev/null +++ b/client/internal/httpClient/src/HttpClientManager.cpp @@ -0,0 +1,80 @@ +#include "HttpClientManager.h" + +#include +#include +#include + +#include "HttpClient.h" + +HttpClientManager::HttpClientManager(std::string_view host_, std::string_view port_) : + host(host_), port(port_), + client(std::make_shared(host_, port_)), + serializer(std::make_shared()) {} + +std::pair HttpClientManager::loginUser(const std::string &login, const std::string &password) { + std::string body = serializer->serialLoginData(login, password); + http::response res = client->makeGetRequest("/user/login", body); + unsigned status = res.result_int(); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + User user{0, "", "", ""}; + if (status == 200) + user = serializer->deserialUserData(res_body); + return {status, user}; +} + +std::pair HttpClientManager::registerUser(const std::string &login, const std::string &username, + const std::string &password) { + std::string body = serializer->serialRegisterData(login, username, password); + http::response res = client->makeGetRequest("/user/register", body); + unsigned status = res.result_int(); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + User user = serializer->deserialUserData(res_body); + return {status, user}; +} + +Solution HttpClientManager::submitSolution(const int &user_id, const int &task_id, const std::string &path_to_sound) { + std::string body = serializer->serialSolutionData(user_id, task_id, path_to_sound); + http::response res = client->makeGetRequest("/solution/submit", body); + unsigned status = res.result_int(); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + Solution sol = serializer->deserialSolutionData(res_body); + return sol; +} + +unsigned int HttpClientManager::getAllSolutionsForTask(const int &user_id, const int &task_id) { + return 0; +} + +void HttpClientManager::setHttpClient(std::shared_ptr client_) { + client = client_; +} + +std::vector HttpClientManager::getAllTasks() { + http::response res = client->makeGetRequest("/task/all", ""); + std::string res_body; + for (auto seq : res.body().data()) { + auto* cbuf = boost::asio::buffer_cast(seq); + res_body.append(cbuf, boost::asio::buffer_size(seq)); + } + std::vector tasks = serializer->deserialAllTasks(res_body); + return tasks; +} + +unsigned int HttpClientManager::createTask(const std::string &desc) { + std::string body = serializer->serialNewTaskData(desc); + http::response res = client->makeGetRequest("/task/create", body); + unsigned int result = res.result_int(); + return result; +} diff --git a/client/internal/httpClient/src/Serializer.cpp b/client/internal/httpClient/src/Serializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e76eca476e2955f77143f404990258b322516c8e --- /dev/null +++ b/client/internal/httpClient/src/Serializer.cpp @@ -0,0 +1,88 @@ +#include "Serializer.h" + +#include +#include +#include +#include + +std::string Serializer::serialLoginData(std::string_view login, std::string_view password) { + boost::property_tree::ptree json; + json.put("login", login); + json.put("password", password); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +User Serializer::deserialUserData(std::string_view body) { + std::stringstream ss; + ss << body; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + User res = { + json.get("user_id"), + json.get("login"), + json.get("password"), + json.get("username") + }; + return res; +} + +std::string +Serializer::serialRegisterData(std::string_view login, std::string_view username, std::string_view password) { + boost::property_tree::ptree json; + json.put("login", login); + json.put("username", username); + json.put("password", password); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::vector Serializer::deserialAllTasks(std::string_view body) { + std::stringstream ss; + ss << body; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::vector tasks; + for (auto &sound : json.get_child("tasks")) { + Task new_task(sound.second.get("task_id"), sound.second.get("description")); + tasks.push_back(new_task); + } + return tasks; +} + +std::string Serializer::serialSolutionData(const int &user_id, const int &task_id, const std::string& path_to_file) { + boost::property_tree::ptree json; + json.put("user_id", user_id); + json.put("task_id", task_id); + std::ifstream file(path_to_file); + std::ostringstream sstr; + sstr << file.rdbuf(); + std::string source = sstr.str(); + json.put("source", source); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +Solution Serializer::deserialSolutionData(std::string_view body) { + std::stringstream ss; + ss << body; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + Solution res = { + json.get("sol_id"), + json.get("source"), + json.get("result"), + }; + return res; +} + +std::string Serializer::serialNewTaskData(std::string_view desc) { + boost::property_tree::ptree json; + json.put("description", desc); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} diff --git a/client/internal/httpClient/tests/CMakeLists.txt b/client/internal/httpClient/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b0e0c73a0dbcc7dabb6b70d9db49dcf860836fc --- /dev/null +++ b/client/internal/httpClient/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +project(test_httpclient) + +set(CMAKE_CXX_STANDARD 20) +add_compile_options(-coverage) + +file(GLOB SOURCES *.cpp) + +enable_testing() +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${HTTPCLIENT_lib_LIB} GTest::GTest GTest::gmock) +target_include_directories(${PROJECT_NAME} PUBLIC ${HTTPCLIENT_lib_INCLUDE_DIRS}) + +add_test(test_httpclient test_httpclient) \ No newline at end of file diff --git a/client/internal/httpClient/tests/HttpClientManagerSuite.cpp b/client/internal/httpClient/tests/HttpClientManagerSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44de182f5798f53c44eca248703f5d8b6c7b9223 --- /dev/null +++ b/client/internal/httpClient/tests/HttpClientManagerSuite.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include "IHttpClient.h" +#include "HttpClientManager.h" + +class HttpClientMock : public IHttpClient { +public: + HttpClientMock() = default; + MOCK_METHOD(ResponseStruct, makeGetRequest, (const Host& host, const std::string& target, + const std::shared_ptr& params, + const std::shared_ptr& headers), (override)); + MOCK_METHOD(ResponseStruct, makePostRequest, (const Host& host, const std::string& target, + const std::shared_ptr& body, + const std::shared_ptr& headers), (override)); + MOCK_METHOD(ResponseStruct, parseResponse, (Response response), (override)); + MOCK_METHOD(std::string, createURL, (const std::string& target, const std::shared_ptr& params), + (override)); + MOCK_METHOD(bool, connect, (unsigned short port), (override)); + MOCK_METHOD(ResponseStruct, makeRequest, (const Host& host, const std::string& target, boost::beast::http::verb method, + const std::shared_ptr& params, + const std::shared_ptr& body, + const std::shared_ptr& headers), (override)); +}; + +class HttpClientManagerSuite : public ::testing::Test { +protected: + void SetUp() override { + manager.setHttpClient(client); + } + + std::shared_ptr client = std::make_shared(); + HttpClientManager manager = HttpClientManager("1", "1.1.1.1", 1, "."); +}; + +TEST_F(HttpClientManagerSuite, LoginUserTest){ + EXPECT_CALL(*client, makeGetRequest); + manager.loginUser("1", "2"); +} + +TEST_F(HttpClientManagerSuite, RegisterUserTest){ + EXPECT_CALL(*client, makePostRequest); + manager.registerUser("1", "2", "3"); +} + +TEST_F(HttpClientManagerSuite, SubmitSolutionTest){ + EXPECT_CALL(*client, makePostRequest); + manager.submitSolution(1, "2234"); +} + +TEST_F(HttpClientManagerSuite, GetAllSolutionsForTaskTest){ + EXPECT_CALL(*client, makeGetRequest); + manager.getAllSolutionsForTask(1, 2); +} + +TEST_F(HttpClientManagerSuite, GetAllTasksTest){ + EXPECT_CALL(*client, makeGetRequest); + manager.getAllTasks(); +} + +TEST_F(HttpClientManagerSuite, GetMetricsTest){ + EXPECT_CALL(*client, makeGetRequest); + manager.getMetrics(1); +} diff --git a/client/internal/httpClient/tests/HttpClientSuite.cpp b/client/internal/httpClient/tests/HttpClientSuite.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ff26c2bf95faa46869ad78a06684c7696ab9b28 --- /dev/null +++ b/client/internal/httpClient/tests/HttpClientSuite.cpp @@ -0,0 +1,11 @@ +#include +#include + +#include "HttpClient.h" + +TEST(HttpClientSuite, WrongGetTest) { + HttpClient client; + Host h("1", "1.1.1.1", 80); + ResponseStruct res = client.makeGetRequest(h, "/ser/login"); + EXPECT_EQ(res.status, 500); +} \ No newline at end of file diff --git a/client/internal/httpClient/tests/main.cpp b/client/internal/httpClient/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..21bd133ab348315c2774736097e1a99620c3dc96 --- /dev/null +++ b/client/internal/httpClient/tests/main.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/client/internal/httpClient/virtual/IHttpClient.h b/client/internal/httpClient/virtual/IHttpClient.h new file mode 100644 index 0000000000000000000000000000000000000000..9de99fc020cdab8014deeedf8b44b79b7fc6fa1e --- /dev/null +++ b/client/internal/httpClient/virtual/IHttpClient.h @@ -0,0 +1,27 @@ +#ifndef APP_HTTPCLIENT_HTTPCLIENT_IHTTPCLIENT_H_ +#define APP_HTTPCLIENT_HTTPCLIENT_IHTTPCLIENT_H_ + +#include +#include +#include +#include +#include + +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +using tcp = net::ip::tcp; + +class IHttpClient { +public: + virtual http::response makeGetRequest(std::string_view target, std::string_view body) = 0; + virtual http::response makePostRequest(std::string_view target, std::string_view body) = 0; + +protected: + virtual http::response makeRequest(std::string_view target, + http::verb method, + std::string_view body) = 0; +}; + + +#endif // APP_HTTPCLIENT_HTTPCLIENT_HTTPCLIENT_H_ diff --git a/server/internal/httpServer/CMakeLists.txt b/server/internal/httpServer/CMakeLists.txt index 871e221a0c6a43ccdd91444bb38c366c751bb9cf..f9227f4f10e3482d9e7087e2833b4b4db998cbf8 100644 --- a/server/internal/httpServer/CMakeLists.txt +++ b/server/internal/httpServer/CMakeLists.txt @@ -8,11 +8,13 @@ file(GLOB INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURC include_directories(${INCLUDE_DIRS} ${Boost_INCLUDE_DIR} ${libEntities_INCLUDE_DIRS} ${SERVICE_lib_INCLUDE_DIRS}) add_library(${PROJECT_NAME} ${SOURCES} ${INCLUDES}) +add_executable(HttpServer_run main.cpp) message("SERVICE_lib_LIB = ${SERVICE_lib_LIB}") message("SERVICE_lib_INCLUDE_DIRS = ${SERVICE_lib_INCLUDE_DIRS}") target_link_libraries(${PROJECT_NAME} ${SERVICE_lib_LIB} ${libEntities_LIB} ${Boost_LIBRARIES}) +target_link_libraries(HttpServer_run ${PROJECT_NAME} ${Boost_LIBRARIES}) set(HTTPSERVER_lib_LIB ${PROJECT_NAME}) set(HTTPSERVER_lib_LIB ${HTTPSERVER_lib_LIB} PARENT_SCOPE) diff --git a/server/internal/httpServer/include/HttpConnection.h b/server/internal/httpServer/include/HttpConnection.h index cee5f56d4bb9da7c36c9b4d01932fc03780110ef..86d4bbb9cbbaaf33c9523fc1428d797e24ea50af 100644 --- a/server/internal/httpServer/include/HttpConnection.h +++ b/server/internal/httpServer/include/HttpConnection.h @@ -1,33 +1,35 @@ -#ifndef APP_HTTPSERVER_HTTPSERVER_HTTPCONNECTION_H_ -#define APP_HTTPSERVER_HTTPSERVER_HTTPCONNECTION_H_ +#ifndef SOURCEDOUT_HTTPSERVER_HTTPCONNECTION_H +#define SOURCEDOUT_HTTPSERVER_HTTPCONNECTION_H #include #include +#include -#include "Response.h" -#include "Request.h" #include "Router.h" -#include "Parser.h" -class HttpConnection : public std::enable_shared_from_this, - private boost::noncopyable { +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; +using tcp = boost::asio::ip::tcp; + +class HttpConnection : public std::enable_shared_from_this { public: - explicit HttpConnection(boost::asio::io_service& io_service); - boost::asio::ip::tcp::socket& getSocket(); - void start(); - protected: - void handleRead(const boost::system::error_code& e, - std::size_t bytes_transferred); - void handleWrite(const boost::system::error_code& e); + explicit HttpConnection(tcp::socket&& socket_, + std::shared_ptr const& doc_root_); + void run(); + void read(); + void handleRead(beast::error_code e, std::size_t bytes_transferred); + void sendResponse(http::message_generator&& msg); + void handleWrite(bool keep_alive, beast::error_code e, std::size_t bytes_transferred); + void close(); - boost::asio::io_service::strand strand; - boost::asio::ip::tcp::socket socket; + private: + beast::tcp_stream stream; + beast::flat_buffer buffer; + std::shared_ptr doc_root; + http::request request; std::shared_ptr router; - std::array buffer{}; - Request request; - Parser parser; - Response response; }; -#endif // APP_HTTPSERVER_HTTPSERVER_HTTPCONNECTION_H_ +#endif // SOURCEDOUT_HTTPSERVER_HTTPCONNECTION_H diff --git a/server/internal/httpServer/include/HttpServer.h b/server/internal/httpServer/include/HttpServer.h index 083d224d766b340a68bb21bca18ae5701596084a..422a2c838dbc25c4f92349a0130362f4f5d57136 100644 --- a/server/internal/httpServer/include/HttpServer.h +++ b/server/internal/httpServer/include/HttpServer.h @@ -1,30 +1,29 @@ -#ifndef APP_HTTPSERVER_HTTPSERVER_HTTPSERVER_H_ -#define APP_HTTPSERVER_HTTPSERVER_HTTPSERVER_H_ +#ifndef SOURCEDOUT_HTTPSERVER_HTTPSERVER_H +#define SOURCEDOUT_HTTPSERVER_HTTPSERVER_H #include #include #include -#include -#include #include "HttpConnection.h" #include "Router.h" -class HttpServer : private boost::noncopyable { +namespace net = boost::asio; +using tcp = boost::asio::ip::tcp; + +class HttpServer : public std::enable_shared_from_this { public: - HttpServer(const std::string& address, const std::string& port, std::size_t thread_pool_size); + HttpServer(net::io_context& io_context_, tcp::endpoint endpoint_, + std::shared_ptr const& doc_root_); void run(); private: void startAccept(); - void handleAccept(const boost::system::error_code& e); - void handleStop(); + void handleAccept(beast::error_code ec, tcp::socket socket); - std::size_t thread_pool_size; - boost::asio::io_service io_service; - boost::asio::signal_set signals; - boost::asio::ip::tcp::acceptor acceptor; - std::shared_ptr connection; + net::io_context& io_context; + tcp::acceptor acceptor; + std::shared_ptr doc_root; }; -#endif // APP_HTTPSERVER_HTTPSERVER_HTTPSERVER_H_ +#endif // SOURCEDOUT_HTTPSERVER_HTTPSERVER_H diff --git a/server/internal/httpServer/include/Parser.h b/server/internal/httpServer/include/Parser.h deleted file mode 100644 index 142f234549feb2d8dda44ddd630b142e877b0bbb..0000000000000000000000000000000000000000 --- a/server/internal/httpServer/include/Parser.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef APP_HTTPSERVER_HTTPSERVER_PARSER_H_ -#define APP_HTTPSERVER_HTTPSERVER_PARSER_H_ - -#include "Request.h" - -#include -#include - -class Parser { - public: - Parser(); - void reset(); - - template - boost::tuple parse(Request& req, - InputIterator begin, InputIterator end) { - while (begin != end) { - boost::tribool result = consume(req, *begin++); - if (result || !result) - return boost::make_tuple(result, begin); - } - boost::tribool result = boost::indeterminate; - return boost::make_tuple(result, begin); - } - - private: - boost::tribool consume(Request& req, char input); - static bool isChar(int c); - static bool isCtl(int c); - static bool isTspecial(int c); - static bool isDigit(int c); - - enum state { - method_start, - method, - uri, - http_version_h, - http_version_t_1, - http_version_t_2, - http_version_p, - http_version_slash, - http_version_major_start, - http_version_major, - http_version_minor_start, - http_version_minor, - expecting_newline_1, - header_line_start, - header_lws, - header_name, - space_before_header_value, - header_value, - expecting_newline_2, - expecting_newline_3 - } state; -}; - -#endif // APP_HTTPSERVER_HTTPSERVER_PARSER_H_ diff --git a/server/internal/httpServer/include/Request.h b/server/internal/httpServer/include/Request.h deleted file mode 100644 index 386f9b3fe8a1a62c8cb50dd8221d3cf48998910d..0000000000000000000000000000000000000000 --- a/server/internal/httpServer/include/Request.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef APP_HTTPSERVER_HTTPSERVER_REQUEST_H_ -#define APP_HTTPSERVER_HTTPSERVER_REQUEST_H_ - -#include -#include - -#include "Header.h" - -struct Request { - std::string method; - std::string uri; - int http_version_major; - int http_version_minor; - std::vector
headers; -}; - -#endif // APP_HTTPSERVER_HTTPSERVER_REQUEST_H_ diff --git a/server/internal/httpServer/include/Response.h b/server/internal/httpServer/include/Response.h deleted file mode 100644 index 6dd3a1d01b5461b45d4205bc1996bec8945fc69d..0000000000000000000000000000000000000000 --- a/server/internal/httpServer/include/Response.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef APP_HTTPSERVER_HTTPSERVER_REPLY_H_ -#define APP_HTTPSERVER_HTTPSERVER_REPLY_H_ - -#include -#include -#include - -#include "Header.h" - -struct Response { - enum status_type { - ok = 200, - created = 201, - accepted = 202, - no_content = 204, - multiple_choices = 300, - moved_permanently = 301, - moved_temporarily = 302, - not_modified = 304, - bad_request = 400, - unauthorized = 401, - forbidden = 403, - not_found = 404, - internal_server_error = 500, - not_implemented = 501, - bad_gateway = 502, - service_unavailable = 503 - } status; - - std::vector
headers; - std::string content; - - std::vector toBuffers(); - static Response stockResponse(status_type status); -}; - - -#endif // APP_HTTPSERVER_HTTPSERVER_REPLY_H_ diff --git a/server/internal/httpServer/include/Router.h b/server/internal/httpServer/include/Router.h index 50267ea8583471a3e005aed4832ae791fd451a71..404b44dacc74474ab92e4849be99d2e0ea06d848 100644 --- a/server/internal/httpServer/include/Router.h +++ b/server/internal/httpServer/include/Router.h @@ -1,20 +1,25 @@ -#ifndef APP_HTTPSERVER_HTTPSERVER_ROUTER_H_ -#define APP_HTTPSERVER_HTTPSERVER_ROUTER_H_ +#ifndef SOURCEDOUT_HTTPSERVER_ROUTER_UTILS_H +#define SOURCEDOUT_HTTPSERVER_ROUTER_UTILS_H #include #include #include +#include #include "SolutionManager.h" #include "UserManager.h" #include "TaskManager.h" -#include "Response.h" -#include "Request.h" -class Router : private boost::noncopyable { +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; + +class Router : public std::enable_shared_from_this { public: - Router(); - void handleRequest(const Request& req, Response& res); + explicit Router(std::string_view doc_root_); + + http::message_generator handleRequest(http::request&& req); + void setSolutionManager(std::shared_ptr mng) { solutionManager = mng; } @@ -28,8 +33,11 @@ class Router : private boost::noncopyable { std::shared_ptr solutionManager; std::shared_ptr userManager; std::shared_ptr taskManager; - static bool decodeUrl(const std::string& in, std::string& out); + std::string doc_root; + + http::response getBadRequest(const http::request& request, + beast::string_view why); }; -#endif // APP_HTTPSERVER_HTTPSERVER_ROUTER_H_ +#endif // SOURCEDOUT_HTTPSERVER_ROUTER_UTILS_H diff --git a/server/internal/httpServer/include/Serializer.h b/server/internal/httpServer/include/Serializer.h index a8a68be2b2411b67bcfed9f20150dcfc0f1afd80..19120d1b9e17e5235ed21824df76f08fe47c8743 100644 --- a/server/internal/httpServer/include/Serializer.h +++ b/server/internal/httpServer/include/Serializer.h @@ -7,8 +7,22 @@ #include "Solution.hpp" #include "User.hpp" +#include "Task.hpp" -class Serializer {}; +class Serializer { +public: + std::tuple deserialNewSolutionData(const std::string& val); + std::tuple deserialTaskData(const std::string &val); + std::string deserialNewTaskData(const std::string &val); + size_t deserialSolutionData(const std::string& val); + std::tuple deserialUserData(const std::string& val); + std::tuple deserialNewUserData(const std::string &val); + + std::string serialSolutions(const std::vector& solutions); + std::string serialAllTasks(const std::vector& tasks); + std::string serialUserData(const User& user); + std::string serialSolution(const Solution& sol); +}; #endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_SERIALIZER_H_ diff --git a/server/internal/httpServer/include/SolutionManager.h b/server/internal/httpServer/include/SolutionManager.h index 5a110652b507d7992ff09b8d7692e68055ec7208..166aee3bc623dc02905b81a5a8da7eb34e78ef7f 100644 --- a/server/internal/httpServer/include/SolutionManager.h +++ b/server/internal/httpServer/include/SolutionManager.h @@ -3,25 +3,25 @@ #include #include +#include -#include "Response.h" -#include "Request.h" #include "Serializer.h" #include "ISolutionManager.h" #include "ISolutionService.h" +namespace beast = boost::beast; +namespace http = boost::beast::http; + class SolutionManager : public ISolutionManager { public: SolutionManager(); - Response getAllSolutions(const Request &req) override; - Response createSolution(const Request &req) override; - Response getMetrics(const Request &req) override; + http::message_generator getAllSolutions(http::request&& req) override; + http::message_generator createSolution(http::request&& req) override; void setService(std::shared_ptr service); private: std::shared_ptr solutionService; std::shared_ptr serializer; - static std::string getParam(const std::string& path, const std::string& name); }; #endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_SolutionMANAGER_H_ diff --git a/server/internal/httpServer/include/TaskManager.h b/server/internal/httpServer/include/TaskManager.h index c49b08b60cac92e3cb909d52d463000faff48d96..361c7a82572e101f2653e9ba224470c2ba359eef 100644 --- a/server/internal/httpServer/include/TaskManager.h +++ b/server/internal/httpServer/include/TaskManager.h @@ -1,11 +1,8 @@ -#ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_TaskMANAGER_H_ -#define APP_HTTPSERVER_HTTPSERVER_MANAGERS_TaskMANAGER_H_ +#pragma once #include #include -#include "Response.h" -#include "Request.h" #include "Serializer.h" #include "ITaskManager.h" #include "ITaskService.h" @@ -13,13 +10,10 @@ class TaskManager : public ITaskManager { public: TaskManager(); - Response createTask(const Request &req) override; - Response getAllTasks(const Request &req) override; + http::message_generator createTask(http::request&& req) override; + http::message_generator getAllTasks(http::request&& req) override; void setService(std::shared_ptr service); private: std::shared_ptr taskService; std::shared_ptr serializer; - static std::string getParam(const std::string& path, const std::string& name); }; - -#endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_TaskMANAGER_H_ diff --git a/server/internal/httpServer/include/TmpSolutionService.h b/server/internal/httpServer/include/TmpSolutionService.h new file mode 100644 index 0000000000000000000000000000000000000000..63cab8dae66e67ff0a6d5e2312e939127c575829 --- /dev/null +++ b/server/internal/httpServer/include/TmpSolutionService.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "Solution.hpp" + +class TmpSolutionService { +public: + static Solution createSolution(std::size_t user_id, std::size_t task_id, std::string source) { + count++; + Solution sol(count, "", user_id, source, "", "", task_id, "ok"); + for (auto& i: solutions) { + if (i.getSource() == source && i.getTaskId() == task_id && i.getSenderId() != user_id) + sol.setResult("plagiat"); + } + solutions.push_back(sol); + return sol; + } + + static std::size_t count; + static std::vector solutions; +}; + +std::vector TmpSolutionService::solutions = {}; +std::size_t TmpSolutionService::count = 3; diff --git a/server/internal/httpServer/include/TmpTaskService.h b/server/internal/httpServer/include/TmpTaskService.h new file mode 100644 index 0000000000000000000000000000000000000000..90f2f76515e4e853092057d2a69224afbe78bd5a --- /dev/null +++ b/server/internal/httpServer/include/TmpTaskService.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include "Task.hpp" + +class TmpTaskService { +public: + static void createTask(std::string description) { + count++; + Task task(count, description); + tasks.push_back(task); + } + + static std::vector getAllTasks() { + return tasks; + } + + static std::size_t count; + static std::vector tasks; +}; + +std::vector TmpTaskService::tasks = {Task(1, "1 description 1\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "14\n"), + Task(2, "2 description"), + Task(3, "3 description")}; +std::size_t TmpTaskService::count = 3; \ No newline at end of file diff --git a/server/internal/httpServer/include/TmpUserService.h b/server/internal/httpServer/include/TmpUserService.h new file mode 100644 index 0000000000000000000000000000000000000000..cd60903829065b6f9b3d87a895903c7fbe228e87 --- /dev/null +++ b/server/internal/httpServer/include/TmpUserService.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include "User.hpp" + +class TmpUserService { +public: + static std::pair login(std::string_view login, std::string_view password) { + bool found = false; + User user; + for (const auto& i : tasks) { + if (i.getPassword() == password && i.getLogin() == login && !found) { + user = i; + found = true; + } + } + return {user, found}; + } + + static User registerUser(std::string login, std::string password, std::string username) { + count++; + User user(count, login, password, username); + std::cout << user << std::endl; + tasks.push_back(user); + return user; + } + + static std::size_t count; + static std::vector tasks; +}; + +std::vector TmpUserService::tasks = {}; +std::size_t TmpUserService::count = 0; diff --git a/server/internal/httpServer/include/UserManager.h b/server/internal/httpServer/include/UserManager.h index 9043ada99edf4501c174b9f66c289406f9bb6c13..9cf090021b93b1bdf9299399deb0a04957ea0007 100644 --- a/server/internal/httpServer/include/UserManager.h +++ b/server/internal/httpServer/include/UserManager.h @@ -4,8 +4,6 @@ #include #include -#include "Response.h" -#include "Request.h" #include "Serializer.h" #include "IUserManager.h" #include "IUserService.h" @@ -13,13 +11,16 @@ class UserManager : public IUserManager { public: UserManager(); - Response loginUser(const Request &req) override; - Response registerUser(const Request &req) override; + http::message_generator loginUser(http::request&& req) override; + http::message_generator registerUser(http::request&& req) override; void setService(std::shared_ptr service); private: std::shared_ptr userService; std::shared_ptr serializer; - static std::string getParam(const std::string& path, const std::string& name); + + // for tests with client + int count = 0; + std::vector users; }; #endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_USERMANAGER_H_ diff --git a/server/internal/httpServer/include/Utils.h b/server/internal/httpServer/include/Utils.h new file mode 100644 index 0000000000000000000000000000000000000000..8311dc7172ac0c810c0a109e8f2e423e56aa689f --- /dev/null +++ b/server/internal/httpServer/include/Utils.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +namespace beast = boost::beast; + +void fail(beast::error_code ec, char const* what); diff --git a/server/internal/httpServer/main.cpp b/server/internal/httpServer/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d48c12c795b6fae1de30075cb7c49c8bf057ac4 --- /dev/null +++ b/server/internal/httpServer/main.cpp @@ -0,0 +1,48 @@ +#include + +#include + +#include "HttpServer.h" + +namespace net = boost::asio; +using tcp = boost::asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-async
\n" << + "Example:\n" << + " http-server-async 0.0.0.0 8080 . 1\n"; + return EXIT_FAILURE; + } + auto const address = net::ip::make_address(argv[1]); + auto const port = static_cast(std::atoi(argv[2])); + auto const doc_root = std::make_shared(argv[3]); + auto const threads = std::max(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create and launch a listening port + std::make_shared( + ioc, + tcp::endpoint{address, port}, + doc_root + )->run(); + + // Run the I/O service on the requested number of threads + std::vector v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/server/internal/httpServer/src/HttpConnection.cpp b/server/internal/httpServer/src/HttpConnection.cpp index 412f67067450d546bcc719fed3edad9ffc741a82..084d91a82a14269cfd02c636ea5fca3431de7eb8 100644 --- a/server/internal/httpServer/src/HttpConnection.cpp +++ b/server/internal/httpServer/src/HttpConnection.cpp @@ -2,15 +2,73 @@ #include -HttpConnection::HttpConnection(boost::asio::io_service& io_service) - : strand(io_service), - socket(io_service) {} +#include "Utils.h" -boost::asio::ip::tcp::socket& HttpConnection::getSocket() {} +HttpConnection::HttpConnection(tcp::socket&& socket_, + std::shared_ptr const& doc_root_) + : stream(std::move(socket_)), doc_root(doc_root_) { + router = std::make_shared(*doc_root_); +} -void HttpConnection::start() {} +void HttpConnection::run() { + net::dispatch(stream.get_executor(), + beast::bind_front_handler(&HttpConnection::read, shared_from_this()) + ); +} -void HttpConnection::handleRead(const boost::system::error_code& e, - std::size_t bytes_transferred) {} +void HttpConnection::read() { + request = {}; + stream.expires_after(std::chrono::seconds(30)); + http::async_read(stream, buffer, request, + beast::bind_front_handler( + &HttpConnection::handleRead, + shared_from_this() + ) + ); +} -void HttpConnection::handleWrite(const boost::system::error_code& e) {} +void HttpConnection::handleRead(beast::error_code e, std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + + if(e == http::error::end_of_stream) + return close(); + + if(e) + return fail(e, "read"); + + sendResponse( + router->handleRequest(std::move(request)) + ); +} + +void HttpConnection::sendResponse(http::message_generator &&msg) { + bool keep_alive = msg.keep_alive(); + + beast::async_write( + stream, + std::move(msg), + beast::bind_front_handler( + &HttpConnection::handleWrite, + shared_from_this(), + keep_alive + ) + ); +} + +void HttpConnection::handleWrite(bool keep_alive, beast::error_code e, std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + + if(e) + return fail(e, "write"); + + if(!keep_alive) { + return close(); + } + + read(); +} + +void HttpConnection::close() { + beast::error_code e; + stream.socket().shutdown(tcp::socket::shutdown_send, e); +} \ No newline at end of file diff --git a/server/internal/httpServer/src/HttpServer.cpp b/server/internal/httpServer/src/HttpServer.cpp index 246b4f085e17659efdfd58af64359dd3b2d39638..e00df2a1f6ae77237dfb69c83166cf9068eeb1d5 100644 --- a/server/internal/httpServer/src/HttpServer.cpp +++ b/server/internal/httpServer/src/HttpServer.cpp @@ -1,20 +1,58 @@ #include "HttpServer.h" #include -#include #include -#include -HttpServer::HttpServer(const std::string& address, const std::string& port, std::size_t thread_pool_size) - : thread_pool_size(thread_pool_size), - signals(io_service), - acceptor(io_service), - connection() {} +#include "Utils.h" -void HttpServer::run() {} +HttpServer::HttpServer(net::io_context& io_context_, tcp::endpoint endpoint_, + std::shared_ptr const& doc_root_) + : io_context(io_context_), acceptor(net::make_strand(io_context_)), doc_root(doc_root_) { -void HttpServer::startAccept() {} + beast::error_code ec; + acceptor.open(endpoint_.protocol(), ec); + if(ec) { + fail(ec, "open"); + return; + } -void HttpServer::handleAccept(const boost::system::error_code& e) {} + acceptor.set_option(net::socket_base::reuse_address(true), ec); + if(ec) { + fail(ec, "set_option"); + return; + } -void HttpServer::handleStop() {} + acceptor.bind(endpoint_, ec); + if(ec) { + fail(ec, "bind"); + return; + } + + acceptor.listen(net::socket_base::max_listen_connections, ec); + if(ec) { + fail(ec, "listen"); + return; + } +} + +void HttpServer::run() { + startAccept(); +} + +void HttpServer::startAccept() { + acceptor.async_accept( + net::make_strand(io_context), + beast::bind_front_handler(&HttpServer::handleAccept, shared_from_this()) + ); +} + +void HttpServer::handleAccept(beast::error_code ec, tcp::socket socket) { + if(ec) { + fail(ec, "accept"); + return; + } else { + std::make_shared(std::move(socket), doc_root)->run(); + } + + startAccept(); +} \ No newline at end of file diff --git a/server/internal/httpServer/src/Parser.cpp b/server/internal/httpServer/src/Parser.cpp deleted file mode 100644 index 86b95414a5f8a365d589d50fb846bec5406cbb14..0000000000000000000000000000000000000000 --- a/server/internal/httpServer/src/Parser.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "Parser.h" -#include "Request.h" - -Parser::Parser() - : state(method_start) {} - -void Parser::reset() {} - -boost::tribool Parser::consume(Request& req, char input) {} - -bool Parser::isChar(int c) {} - -bool Parser::isCtl(int c) {} - -bool Parser::isTspecial(int c) {} - -bool Parser::isDigit(int c) {} diff --git a/server/internal/httpServer/src/Response.cpp b/server/internal/httpServer/src/Response.cpp deleted file mode 100644 index f688c347c2a65fe4b6e92bcf2d37f37aff0a5f82..0000000000000000000000000000000000000000 --- a/server/internal/httpServer/src/Response.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "Response.h" - -std::vector Response::toBuffers() {} - -Response Response::stockResponse(Response::status_type status) {} diff --git a/server/internal/httpServer/src/Router.cpp b/server/internal/httpServer/src/Router.cpp index e235deef01ebe59b22c4908c4ad2207c406e6623..88d83c2175245ba374229f4a7145a5a28f864800 100644 --- a/server/internal/httpServer/src/Router.cpp +++ b/server/internal/httpServer/src/Router.cpp @@ -2,14 +2,50 @@ #include #include -#include -#include "Response.h" +#include "Utils.h" -Router::Router() : +http::response Router::getBadRequest(const http::request& request, + beast::string_view why) { + http::response res{http::status::bad_request, request.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(request.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; +} + +Router::Router(std::string_view doc_root_) : solutionManager(std::make_shared()), - userManager(std::make_shared()) {} + userManager(std::make_shared()), + taskManager(std::make_shared()), + doc_root(doc_root_) {} + + +http::message_generator Router::handleRequest(http::request &&req) { + + if(req.method() != http::verb::get && req.method() != http::verb::post) + return getBadRequest(req, "Unknown HTTP-method"); + + if(req.target().empty() || req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) { + return getBadRequest(req, "Illegal request-target"); + } -void Router::handleRequest(const Request& req, Response& res) {} + if (req.target() == "/solution/submit") { + return solutionManager->createSolution(std::move(req)); + } else if (req.target() == "/solution/all") { + return solutionManager->getAllSolutions(std::move(req)); + } else if (req.target() == "/user/register") { + return userManager->registerUser(std::move(req)); + } else if (req.target() == "/user/login") { + return userManager->loginUser(std::move(req)); + } else if (req.target() == "/task/create") { + return taskManager->createTask(std::move(req)); + } else if (req.target() == "/task/all") { + return taskManager->getAllTasks(std::move(req)); + } -bool Router::decodeUrl(const std::string& in, std::string& out) {} + return getBadRequest(req, "Unknown request-target"); +} \ No newline at end of file diff --git a/server/internal/httpServer/src/Serializer.cpp b/server/internal/httpServer/src/Serializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d1905c37a37a252ccdf1410c45f747f76ee7a82 --- /dev/null +++ b/server/internal/httpServer/src/Serializer.cpp @@ -0,0 +1,124 @@ +#include "Serializer.h" + +#include +#include +#include + +std::tuple Serializer::deserialNewSolutionData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = { + json.get("user_id"), + json.get("source"), + json.get("task_id") + }; + return res; +} + +size_t Serializer::deserialSolutionData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + return json.get("sol_id"); +} + +std::tuple Serializer::deserialTaskData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = { + json.get("user_id"), + json.get("task_id") + }; + return res; +} + +std::string Serializer::deserialNewTaskData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + return json.get("description"); +} + +std::tuple Serializer::deserialUserData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = { + json.get("login"), + json.get("password") + }; + return res; +} + +std::tuple Serializer::deserialNewUserData(const std::string &val) { + std::stringstream ss; + ss << val; + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + std::tuple res = { + json.get("login"), + json.get("password"), + json.get("username") + }; + return res; +} + +std::string Serializer::serialSolutions(const std::vector &solutions) { + boost::property_tree::ptree json; + json.put("count", solutions.size()); + boost::property_tree::ptree solutions_nodes; + for (auto &i : solutions) { + boost::property_tree::ptree node; + node.put("sol_id", i.getId()); + node.put("task_id", i.getTaskId()); + solutions_nodes.push_back(std::make_pair("", node)); + } + json.add_child("solutions", solutions_nodes); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::string Serializer::serialAllTasks(const std::vector &tasks) { + boost::property_tree::ptree json; + json.put("count", tasks.size()); + boost::property_tree::ptree tasks_nodes; + for (auto &i : tasks) { + boost::property_tree::ptree node; + node.put("task_id", i.getId()); + node.put("description", i.getDescription()); + tasks_nodes.push_back(std::make_pair("", node)); + } + json.add_child("tasks", tasks_nodes); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::string Serializer::serialUserData(const User &user) { + boost::property_tree::ptree json; + json.put("user_id", user.getId()); + json.put("login", user.getLogin()); + json.put("username", user.getUsername()); + json.put("password", user.getPassword()); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} + +std::string Serializer::serialSolution(const Solution &sol) { + boost::property_tree::ptree json; + json.put("sol_id", sol.getId()); + json.put("source", sol.getSource()); + json.put("result", sol.getResult()); + std::stringstream out; + boost::property_tree::write_json(out, json); + return out.str(); +} diff --git a/server/internal/httpServer/src/SolutionManager.cpp b/server/internal/httpServer/src/SolutionManager.cpp index 3a93e4110f66c3e54e80a5f4fd42348009ccc203..c85dc980445b3549731f5358a1ba5bb12a8f97c4 100644 --- a/server/internal/httpServer/src/SolutionManager.cpp +++ b/server/internal/httpServer/src/SolutionManager.cpp @@ -3,24 +3,42 @@ // #include "SolutionManager.h" -SolutionManager::SolutionManager() {} +#include +#include "TmpSolutionService.h" -Response SolutionManager::getAllSolutions(const Request &req) { - return Response(); -} +SolutionManager::SolutionManager() : + serializer(std::make_shared()) {}; -Response SolutionManager::createSolution(const Request &req) { - return Response(); +void SolutionManager::setService(std::shared_ptr service) { + solutionService = std::move(service); } -Response SolutionManager::getMetrics(const Request &req) { - return Response(); +http::message_generator SolutionManager::createSolution(http::request&& req) { + size_t user_id, task_id; + std::string source; + std::tie(user_id, source, task_id) = serializer->deserialNewSolutionData(req.body()); + Solution sol = TmpSolutionService::createSolution(user_id, task_id, source); +// sol = solutionService->createSolution(user_id, task_id, source); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialSolution(sol); + res.prepare_payload(); + return res; } -std::string SolutionManager::getParam(const std::string &path, const std::string &name) { - return std::string(); +http::message_generator SolutionManager::getAllSolutions(http::request&& req) { + size_t user_id, task_id; + std::tie(user_id, task_id) = serializer->deserialTaskData(req.body()); + std::vector solutions; + solutions = solutionService->getSolutionsByUserAndTaskId(user_id, task_id); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialSolutions(solutions); + res.prepare_payload(); + return res; } -void SolutionManager::setService(std::shared_ptr service) { - solutionService = service; -} diff --git a/server/internal/httpServer/src/TaskManager.cpp b/server/internal/httpServer/src/TaskManager.cpp index ca3875d970e6cf86e421a6fb9aa3b1fa8e45a6ef..91ca64b5f6710388ed30b7569cce01361528ba81 100644 --- a/server/internal/httpServer/src/TaskManager.cpp +++ b/server/internal/httpServer/src/TaskManager.cpp @@ -2,23 +2,34 @@ // Created by Николай Степанов on 03.05.2023. // #include "TaskManager.h" +#include "TmpTaskService.h" -TaskManager::TaskManager() { +TaskManager::TaskManager() : serializer(std::make_shared()) {} +void TaskManager::setService(std::shared_ptr service) { + taskService = service; } -Response TaskManager::createTask(const Request &req) { - return Response(); -} - -Response TaskManager::getAllTasks(const Request &req) { - return Response(); -} - -std::string TaskManager::getParam(const std::string &path, const std::string &name) { - return std::string(); +http::message_generator TaskManager::createTask(http::request &&req) { + std::string description = serializer->deserialNewTaskData(req.body()); +// taskService->createTask(description); + TmpTaskService::createTask(description); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.prepare_payload(); + return res; } -void TaskManager::setService(std::shared_ptr service) { - taskService = service; +http::message_generator TaskManager::getAllTasks(http::request &&req) { +// std::vector tasks = taskService->getAllTasks(); + std::vector tasks = TmpTaskService::getAllTasks(); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialAllTasks(tasks); + res.prepare_payload(); + return res; } diff --git a/server/internal/httpServer/src/UserManager.cpp b/server/internal/httpServer/src/UserManager.cpp index 41377167c08ed5fa409c174fb00b58eee6561d55..dcff74b49ab76bc69f1d737cf8e88b64b60e1526 100644 --- a/server/internal/httpServer/src/UserManager.cpp +++ b/server/internal/httpServer/src/UserManager.cpp @@ -1,24 +1,47 @@ -// -// Created by Николай Степанов on 03.05.2023. -// #include "UserManager.h" -UserManager::UserManager() { +#include "TmpUserService.h" -} +UserManager::UserManager() : serializer(std::make_shared()) {} -Response UserManager::loginUser(const Request &req) { - return Response(); +void UserManager::setService(std::shared_ptr service) { + userService = service; } -Response UserManager::registerUser(const Request &req) { - return Response(); -} +http::message_generator UserManager::loginUser(http::request &&req) { -std::string UserManager::getParam(const std::string &path, const std::string &name) { - return std::string(); + std::string login, password; + std::tie(login, password) = serializer->deserialUserData(req.body()); + auto result = TmpUserService::login(login, password); + if (result.second) { + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialUserData(result.first); + res.prepare_payload(); + return res; + } else { + http::response res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialUserData(result.first); + res.prepare_payload(); + return res; + } } -void UserManager::setService(std::shared_ptr service) { - userService = service; +http::message_generator UserManager::registerUser(http::request &&req) { + std::string login, password, username; + std::tie(login, password, username) = serializer->deserialNewUserData(req.body()); +// User user = userService->createUser(login, username, password); + User user = TmpUserService::registerUser(login, password, username); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/plain"); + res.keep_alive(req.keep_alive()); + res.body() = serializer->serialUserData(user); + res.prepare_payload(); + return res; } diff --git a/server/internal/httpServer/src/Utils.cpp b/server/internal/httpServer/src/Utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..416b658cfe47ae312b7a59d5e2a29723171ba58d --- /dev/null +++ b/server/internal/httpServer/src/Utils.cpp @@ -0,0 +1,6 @@ +#include "Utils.h" + + +void fail(beast::error_code ec, const char *what) { + std::cerr << what << ": " << ec.message() << "\n"; +} diff --git a/server/internal/httpServer/virtual/ISolutionManager.h b/server/internal/httpServer/virtual/ISolutionManager.h index cd77215cdf02f436ad3ddb7829b26efc4f4ebc69..62f0f2a4ffa540e55621184b63394cff6aed0d8a 100644 --- a/server/internal/httpServer/virtual/ISolutionManager.h +++ b/server/internal/httpServer/virtual/ISolutionManager.h @@ -1,17 +1,15 @@ #ifndef APP_HTTPSERVER_HTTPSERVER_MANAGERS_ISolutionMANAGER_H_ #define APP_HTTPSERVER_HTTPSERVER_MANAGERS_ISolutionMANAGER_H_ -#include -#include +#include -#include "Response.h" -#include "Request.h" +namespace beast = boost::beast; +namespace http = boost::beast::http; class ISolutionManager { public: - virtual Response getAllSolutions(const Request &req) = 0; - virtual Response createSolution(const Request &req) = 0; - virtual Response getMetrics(const Request &req) = 0; + virtual http::message_generator getAllSolutions(http::request&& req) = 0; + virtual http::message_generator createSolution(http::request&& req) = 0; }; #endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_ISolutionMANAGER_H_ diff --git a/server/internal/httpServer/virtual/ITaskManager.h b/server/internal/httpServer/virtual/ITaskManager.h index 11747f7072c12aa138b1d6618ab53c8c13c7fa66..260e2a001a9b14b4b66bb5112d1c0d2e83d42401 100644 --- a/server/internal/httpServer/virtual/ITaskManager.h +++ b/server/internal/httpServer/virtual/ITaskManager.h @@ -4,14 +4,15 @@ #include #include -#include "Response.h" -#include "Request.h" -#include "Serializer.h" +#include + +namespace beast = boost::beast; +namespace http = boost::beast::http; class ITaskManager { public: - virtual Response createTask(const Request &req) = 0; - virtual Response getAllTasks(const Request &req) = 0; + virtual http::message_generator createTask(http::request&& req) = 0; + virtual http::message_generator getAllTasks(http::request&& req) = 0; }; #endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_ITaskMANAGER_H_ diff --git a/server/internal/httpServer/virtual/IUserManager.h b/server/internal/httpServer/virtual/IUserManager.h index 599a8ec9e85c342f11e0bd2e4808af9838cbff77..913e4e8263e9ad7a2b57c6d8ab7ae84f76f1129e 100644 --- a/server/internal/httpServer/virtual/IUserManager.h +++ b/server/internal/httpServer/virtual/IUserManager.h @@ -4,14 +4,15 @@ #include #include -#include "Response.h" -#include "Request.h" -#include "Serializer.h" +#include + +namespace beast = boost::beast; +namespace http = boost::beast::http; class IUserManager { public: - virtual Response loginUser(const Request &req) = 0; - virtual Response registerUser(const Request &req) = 0; + virtual http::message_generator loginUser(http::request&& req) = 0; + virtual http::message_generator registerUser(http::request&& req) = 0; }; #endif // APP_HTTPSERVER_HTTPSERVER_MANAGERS_IUserMANAGER_H_ diff --git a/server/internal/repository/include/SolutionRepository.hpp b/server/internal/repository/include/SolutionRepository.hpp index 04034d52ef8734bed542ca7d5626b5620404a7be..e7fb74f05f9d68a4d1682efd3e51372357700ebf 100644 --- a/server/internal/repository/include/SolutionRepository.hpp +++ b/server/internal/repository/include/SolutionRepository.hpp @@ -10,7 +10,7 @@ using namespace pqxx; -class SolutionRepository : ISolutionRepository { +class SolutionRepository : public ISolutionRepository { Solution getSolutionById(size_t id) override; std::vector getSolutionsBySenderId(size_t sender_id) override; diff --git a/server/internal/repository/include/UserRepository.hpp b/server/internal/repository/include/UserRepository.hpp index a8bd86d2ca453eefc9e72c97b6469c31decc36ad..7b67746290bf656ce5e8b2981bd8af8efc71d973 100644 --- a/server/internal/repository/include/UserRepository.hpp +++ b/server/internal/repository/include/UserRepository.hpp @@ -9,7 +9,7 @@ #include using namespace pqxx; -class UserRepository : IUserRepository { +class UserRepository : public IUserRepository { public: explicit UserRepository(); diff --git a/server/internal/service/include/SolutionService.h b/server/internal/service/include/SolutionService.h index 86e432bc31c68676e19247e59e063fb8bca7cade..2ebcf724d332221dc4f540a464db8b062dd8be26 100644 --- a/server/internal/service/include/SolutionService.h +++ b/server/internal/service/include/SolutionService.h @@ -10,7 +10,7 @@ class SolutionService : ISolutionService { std::unique_ptr solutionRepo; // std::unique_ptr antlr public: - explicit SolutionService(std::unique_ptr solutionRepo); + explicit SolutionService(); Solution createSolution(size_t userId, size_t taskId, std::string source) override; std::vector getSolutionsByUserAndTaskId(size_t userId, diff --git a/server/internal/service/include/TaskService.h b/server/internal/service/include/TaskService.h index bf04d3e472f736a8c24bd1d753cd5ed5a3577636..d2f67b93f816323c26c01ca9a31be5654e55096b 100644 --- a/server/internal/service/include/TaskService.h +++ b/server/internal/service/include/TaskService.h @@ -10,7 +10,7 @@ class TaskService : public ITaskService { std::unique_ptr taskRepo; public: - TaskService(std::unique_ptr taskRepo); + TaskService(); ~TaskService() override = default; Task createTask(std::string desc) override; Task getTask(size_t id) override; diff --git a/server/internal/service/include/UserService.h b/server/internal/service/include/UserService.h index 719aa02fbcc1f38035bf4cf134085fdcc11af00f..e5865c3653a703ccf1eac44b6cb53e56bd7c4e7a 100644 --- a/server/internal/service/include/UserService.h +++ b/server/internal/service/include/UserService.h @@ -7,11 +7,11 @@ class UserService : IUserService { private: - std::unique_ptr userRepo; + std::unique_ptr userRepo = std::make_unique(); public: ~UserService() override = default; - explicit UserService(std::unique_ptr userRepo); + explicit UserService(); User createUser(std::string login, std::string username, std::string password) override; User getUserById(size_t id) override; diff --git a/server/internal/service/src/SolutionService.cpp b/server/internal/service/src/SolutionService.cpp index ba707c39d249abaea16b00bf1cce7aff115bca3a..7e3701a4e8875967afe3e1eec001be8d3bcbe2a2 100644 --- a/server/internal/service/src/SolutionService.cpp +++ b/server/internal/service/src/SolutionService.cpp @@ -1,8 +1,9 @@ #include "SolutionService.h" -SolutionService::SolutionService( - std::unique_ptr solutionRepo) - : solutionRepo(std::move(solutionRepo)) {} +#include "SolutionRepository.hpp" + +SolutionService::SolutionService() + : solutionRepo(std::make_unique()) {} Solution SolutionService::createSolution(size_t userId, size_t taskId, std::string source) { diff --git a/server/internal/service/src/TaskService.cpp b/server/internal/service/src/TaskService.cpp index a3ce5ef9b3baadb7902fbdf0f2cca6f9d0229a7a..d083a79d258e751a4ef19eea65938f1511af4f4d 100644 --- a/server/internal/service/src/TaskService.cpp +++ b/server/internal/service/src/TaskService.cpp @@ -1,7 +1,10 @@ #include "TaskService.h" -TaskService::TaskService(std::unique_ptr taskRepo) - : taskRepo(std::move(taskRepo)) {} +#include "TaskRepository.hpp" + +TaskService::TaskService() + : taskRepo(std::make_unique()) { +} Task TaskService::createTask(std::string desc) { size_t id = taskRepo->storeTask(Task(desc)); diff --git a/server/internal/service/src/UserService.cpp b/server/internal/service/src/UserService.cpp index 270f80179a46a245f33586e947e8d57482a4bebb..c2cfa725550620cd45cba19f61845352cd46617a 100644 --- a/server/internal/service/src/UserService.cpp +++ b/server/internal/service/src/UserService.cpp @@ -1,9 +1,11 @@ #include "UserService.h" #include "Exeptions.h" +#include "UserRepository.hpp" -UserService::UserService(std::unique_ptr userRepo) - : userRepo(std::move(userRepo)) {} +UserService::UserService() + : userRepo(std::make_unique()) { +} User UserService::createUser(std::string login, std::string username, std::string password) {