From c8ea4caa439feac0285e4fb7338b61ded6a50219 Mon Sep 17 00:00:00 2001 From: Liiizak <144727441+Liiizak@users.noreply.github.com> Date: Sun, 8 Dec 2024 10:56:43 +0300 Subject: [PATCH] Added codegen by schema (#8) --- CMakeLists.txt | 20 +- docs/api/api.yaml | 225 ++++++++++++++++++ postgresql/schemas/db-1.sql | 2 +- src/dto/article.cpp | 26 +- src/dto/article.hpp | 19 +- src/dto/comment.cpp | 7 +- src/dto/comment.hpp | 9 +- src/dto/profile.cpp | 2 +- src/dto/profile.hpp | 2 +- src/dto/user.cpp | 33 --- src/dto/user.hpp | 37 --- src/handlers/articles/articles_post.cpp | 7 +- src/handlers/articles/articles_slug_put.cpp | 9 +- src/handlers/comments/comment_post.cpp | 6 +- src/handlers/profiles/profiles.cpp | 4 +- src/handlers/profiles/profiles_follow.cpp | 98 ++++---- .../profiles/profiles_follow_delete.cpp | 2 +- src/handlers/users/user_get.cpp | 1 - src/handlers/users/user_put.cpp | 6 +- src/handlers/users/users.cpp | 6 +- src/handlers/users/users_login.cpp | 4 +- src/models/article.cpp | 2 +- src/models/profile.cpp | 2 +- src/models/profile.hpp | 4 +- src/utils/random.cpp | 2 +- src/validators/article_validators.cpp | 2 +- src/validators/validator_test.cpp | 10 +- src/validators/validators.cpp | 13 +- src/validators/validators.hpp | 16 +- tests/CMakeLists.txt | 1 + tests/articles/test_list_articles.py | 8 +- tests/helpers/models.py | 2 +- tests/helpers/utils.py | 2 +- tests/helpers/validators.py | 8 +- tests/tags/test_get_tags.py | 4 +- 35 files changed, 360 insertions(+), 241 deletions(-) create mode 100644 docs/api/api.yaml delete mode 100644 src/dto/user.cpp delete mode 100644 src/dto/user.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b5d4954..98fe862 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(realmedium_sample CXX) # Adding userver dependency -find_package(userver COMPONENTS core postgresql QUIET) +find_package(userver COMPONENTS core postgresql chaotic QUIET) if(NOT userver_FOUND) # Fallback to subdirectory usage # Enable userver libraries that are needed in this project set(USERVER_FEATURE_POSTGRESQL ON CACHE BOOL "" FORCE) @@ -79,8 +79,6 @@ add_library(${PROJECT_NAME}_objs OBJECT src/dto/article.hpp src/dto/profile.cpp src/dto/profile.hpp - src/dto/user.hpp - src/dto/user.cpp src/dto/comment.hpp src/dto/comment.cpp src/db/sql.hpp @@ -120,6 +118,22 @@ add_library(${PROJECT_NAME}_objs OBJECT include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) target_link_libraries(${PROJECT_NAME}_objs PUBLIC userver::core userver::postgresql) +file(GLOB_RECURSE SCHEMAS ${CMAKE_CURRENT_SOURCE_DIR}/docs/*.yaml) +userver_target_generate_chaotic(${PROJECT_NAME}-chgen + ARGS + -n "/components/schemas/([^/]*)/=real_medium::handlers::{0}" + -f "(.*)={0}" + --clang-format= + --generate-serializers + OUTPUT_DIR + ${CMAKE_CURRENT_BINARY_DIR}/src + SCHEMAS + ${SCHEMAS} + RELATIVE_TO + ${CMAKE_CURRENT_SOURCE_DIR} +) +target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}-chgen) + target_include_directories(${PROJECT_NAME}_objs PUBLIC cpp-jwt) target_link_libraries(${PROJECT_NAME}_objs PUBLIC cpp-jwt) diff --git a/docs/api/api.yaml b/docs/api/api.yaml new file mode 100644 index 0000000..7108976 --- /dev/null +++ b/docs/api/api.yaml @@ -0,0 +1,225 @@ +openapi: 3.0.0 +info: + title: realmedium_sample 1.0 + description: a backend application built with userver framework + version: 1.0.0 +servers: + - url: localhost:8080 + description: local + +paths: +# /api/profiles/{username}: +# get: +# description: Get user profile + +# /api/profiles/{username}/follow: +# post: +# description: Follow user +# delete: +# description: Unfollow user + + /api/user: +# get: +# description: Get user profile + put: + description: Update user profile + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserUpdateDTO' + responses: + '200': + description: User profile successfully updated + + /api/users: + post: + description: Register user + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserRegistrationDTO' + responses: + '200': + description: Successfully registered + + /api/users/login: + post: + description: Login user + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserLoginDTO' + responses: + '200': + description: Successfully logged in + + /api/articles: +# get: +# description: Get list of articles + post: + description: Create article + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateArticleRequest' + responses: + '200': + description: Successfully created article + + /api/articles/{slug}: +# get: +# description: Get an article + put: + description: Update article + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateArticleRequest' + responses: + '200': + description: Successfully updated article +# delete: +# description: Delete article + + /api/articles/{slug}/favorite: +# post: +# description: Make an article favorite +# delete: +# description: Remove an article from favorites + + /api/articles/{slug}/comments: + post: + description: Add comment + parameters: + - name: slug + in: path + required: true + schema: + type: string + responses: + '200': + description: Comment successfully added + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AddComment' +# get: +# description: Get comments list + +# /api/articles/{slug}/comments/{id}: +# delete: +# description: Delete comment + +components: + schemas: + UserLoginDTO: + type: object + additionalProperties: false + properties: + email: + type: string + password: + type: string + + UserRegistrationDTO: + type: object + additionalProperties: false + properties: + username: + type: string + email: + type: string + password: + type: string + + UserUpdateDTO: + type: object + additionalProperties: false + properties: + email: + type: string + username: + type: string + password: + type: string + bio: + type: string + image: + type: string + + CreateArticleRequest: + type: object + additionalProperties: false + properties: + title: + type: string + description: + type: string + body: + type: string + tags: + type: array + items: + type: string + + UpdateArticleRequest: + type: object + additionalProperties: false + properties: + title: + type: string + description: + type: string + body: + type: string + + AddComment: + type: object + additionalProperties: false + properties: + body: + type: string + +# Comment: + +# Article: + +# Profile: +# type: object +# additionalProperties: false +# properties: +# username: +# type: string +# bio: +# type: string +# image: +# type: string +# following: +# type: boolean +# required: +# - username +# - following + +# securitySchemes: +# bearerAuth: +# type: http +# scheme: bearer +# bearerFormat: JWT + + responses: + UnauthorizedError: + description: User is not authorized + content: + application/json: + schema: + type: object + properties: + error: + type: string + example: "Unauthorized" diff --git a/postgresql/schemas/db-1.sql b/postgresql/schemas/db-1.sql index 8c49015..a79717b 100644 --- a/postgresql/schemas/db-1.sql +++ b/postgresql/schemas/db-1.sql @@ -122,7 +122,7 @@ CREATE TYPE real_medium.tagged_article_with_author_profile AS ( description TEXT, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, - tagList VARCHAR(255)[], + tags VARCHAR(255)[], favorited BOOL, favorites_count BIGINT, author real_medium.profile); diff --git a/src/dto/article.cpp b/src/dto/article.cpp index fb3c8d8..57e6755 100644 --- a/src/dto/article.cpp +++ b/src/dto/article.cpp @@ -17,7 +17,7 @@ Article Article::Parse(const models::TaggedArticleWithProfile& model) { article.profile.bio = model.authorProfile.bio; article.profile.image = model.authorProfile.image; article.profile.username = model.authorProfile.username; - article.profile.isFollowing = model.authorProfile.isFollowing; + article.profile.following = model.authorProfile.following; return article; } @@ -41,31 +41,13 @@ Article Article::Parse(const models::FullArticleInfo& model, article.profile.bio = model.authorInfo.bio; article.profile.image = model.authorInfo.image; article.profile.username = model.authorInfo.username; - article.profile.isFollowing = + article.profile.following = authUserId ? model.authorFollowedByUsersIds.find(authUserId.value()) != model.authorFollowedByUsersIds.end() : false; return article; } -CreateArticleRequest CreateArticleRequest::Parse( - const userver::formats::json::Value& json) { - return CreateArticleRequest{ - json["title"].As>(), - json["description"].As>(), - json["body"].As>(), - json["tagList"].As>>()}; -} - -UpdateArticleRequest UpdateArticleRequest::Parse( - const userver::formats::json::Value& json, - const userver::server::http::HttpRequest& request) { - return UpdateArticleRequest{ - json["title"].As>(), - json["description"].As>(), - json["body"].As>()}; -} - userver::formats::json::Value Serialize( const Article& article, userver::formats::serialize::To) { @@ -74,11 +56,11 @@ userver::formats::json::Value Serialize( builder["title"] = article.title; builder["description"] = article.description; builder["body"] = article.body; - builder["tagList"] = userver::formats::common::Type::kArray; + builder["tags"] = userver::formats::common::Type::kArray; if (article.tags) { std::for_each( article.tags->begin(), article.tags->end(), - [&builder](const auto& tag) { builder["tagList"].PushBack(tag); }); + [&builder](const auto& tag) { builder["tags"].PushBack(tag); }); } builder["createdAt"] = article.createdAt; builder["updatedAt"] = article.updatedAt; diff --git a/src/dto/article.hpp b/src/dto/article.hpp index 3d34514..ca0ad77 100644 --- a/src/dto/article.hpp +++ b/src/dto/article.hpp @@ -22,24 +22,7 @@ struct Article final { userver::storages::postgres::TimePointTz updatedAt; std::int64_t favoritesCount{}; bool isFavorited{false}; - Profile profile; -}; - -struct CreateArticleRequest final { - static CreateArticleRequest Parse(const userver::formats::json::Value& json); - std::optional title; - std::optional description; - std::optional body; - std::optional> tags; -}; - -struct UpdateArticleRequest final { - static UpdateArticleRequest Parse( - const userver::formats::json::Value& json, - const userver::server::http::HttpRequest& request); - std::optional title; - std::optional description; - std::optional body; + dto::Profile profile; }; userver::formats::json::Value Serialize( diff --git a/src/dto/comment.cpp b/src/dto/comment.cpp index 60d9ca5..c2126f8 100644 --- a/src/dto/comment.cpp +++ b/src/dto/comment.cpp @@ -12,7 +12,7 @@ Comment Comment::Parse(const real_medium::models::CachedComment& cachedComment, comment.author.username = cachedComment.author.username; comment.author.bio = cachedComment.author.bio; comment.author.image = cachedComment.author.image; - comment.author.isFollowing = + comment.author.following = !userId.has_value() ? false : cachedComment.following.count(*userId); return comment; } @@ -31,9 +31,4 @@ userver::formats::json::Value Serialize( return item.ExtractValue(); } -AddComment Parse(const userver::formats::json::Value& json, - userver::formats::parse::To) { - return AddComment{json["body"].As>()}; -} - } // namespace real_medium::dto diff --git a/src/dto/comment.hpp b/src/dto/comment.hpp index 92f4406..989a1ac 100644 --- a/src/dto/comment.hpp +++ b/src/dto/comment.hpp @@ -18,16 +18,9 @@ struct Comment final { userver::storages::postgres::TimePointTz createdAt; userver::storages::postgres::TimePointTz updatedAt; std::string body; - Profile author; + dto::Profile author; }; -struct AddComment { - std::optional body; -}; - -AddComment Parse(const userver::formats::json::Value& json, - userver::formats::parse::To); - userver::formats::json::Value Serialize( const Comment& comment, userver::formats::serialize::To); diff --git a/src/dto/profile.cpp b/src/dto/profile.cpp index 4e808f4..14c5bfa 100644 --- a/src/dto/profile.cpp +++ b/src/dto/profile.cpp @@ -16,7 +16,7 @@ userver::formats::json::Value Serialize( else builder["image"] = userver::formats::common::Type::kNull; ; - builder["following"] = data.isFollowing; + builder["following"] = data.following; return builder.ExtractValue(); } diff --git a/src/dto/profile.hpp b/src/dto/profile.hpp index 426cd35..3e3535d 100644 --- a/src/dto/profile.hpp +++ b/src/dto/profile.hpp @@ -11,7 +11,7 @@ struct Profile { std::string username; std::optional bio; std::optional image; - bool isFollowing{false}; + bool following{false}; }; userver::formats::json::Value Serialize( diff --git a/src/dto/user.cpp b/src/dto/user.cpp deleted file mode 100644 index 1b1f245..0000000 --- a/src/dto/user.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "user.hpp" - -namespace real_medium::dto { - -UserRegistrationDTO Parse(const userver::formats::json::Value& json, - userver::formats::parse::To) { - return UserRegistrationDTO{ - json["username"].As>(), - json["email"].As>(), - json["password"].As>(), - }; -} - -UserLoginDTO Parse(const userver::formats::json::Value& json, - userver::formats::parse::To) { - return UserLoginDTO{ - json["email"].As>(), - json["password"].As>(), - }; -} - -UserUpdateDTO Parse(const userver::formats::json::Value& json, - userver::formats::parse::To) { - return UserUpdateDTO{ - json["email"].As>(), - json["username"].As>(), - json["password"].As>(), - json["bio"].As>(), - json["image"].As>(), - }; -} - -} // namespace real_medium::dto diff --git a/src/dto/user.hpp b/src/dto/user.hpp deleted file mode 100644 index 01ba625..0000000 --- a/src/dto/user.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -namespace real_medium::dto { - -struct UserRegistrationDTO { - std::optional username; - std::optional email; - std::optional password; -}; - -struct UserLoginDTO { - std::optional email; - std::optional password; -}; - -struct UserUpdateDTO { - std::optional email; - std::optional username; - std::optional password; - std::optional bio; - std::optional image; -}; - -UserRegistrationDTO Parse(const userver::formats::json::Value& json, - userver::formats::parse::To); - -UserLoginDTO Parse(const userver::formats::json::Value& json, - userver::formats::parse::To); - -UserUpdateDTO Parse(const userver::formats::json::Value& json, - userver::formats::parse::To); -} // namespace real_medium::dto diff --git a/src/handlers/articles/articles_post.cpp b/src/handlers/articles/articles_post.cpp index aa6b5df..3ab487e 100644 --- a/src/handlers/articles/articles_post.cpp +++ b/src/handlers/articles/articles_post.cpp @@ -1,8 +1,8 @@ #include "articles_post.hpp" #include +#include #include "../../db/sql.hpp" -#include "../../dto/article.hpp" #include "../../models/article.hpp" #include "../../utils/errors.hpp" #include "../../utils/slugify.hpp" @@ -21,9 +21,8 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const userver::server::http::HttpRequest& request, const userver::formats::json::Value& request_json, userver::server::request::RequestContext& context) const { - dto::CreateArticleRequest createArticleRequest; - createArticleRequest = - dto::CreateArticleRequest::Parse(request_json["article"]); + handlers::CreateArticleRequest createArticleRequest = + request_json["article"].As(); try { validator::validate(createArticleRequest); } catch (const real_medium::utils::error::ValidationException& ex) { diff --git a/src/handlers/articles/articles_slug_put.cpp b/src/handlers/articles/articles_slug_put.cpp index b0b366d..ae77267 100644 --- a/src/handlers/articles/articles_slug_put.cpp +++ b/src/handlers/articles/articles_slug_put.cpp @@ -1,6 +1,7 @@ +#include + #include "articles_slug_put.hpp" #include "db/sql.hpp" -#include "dto/article.hpp" #include "models/article.hpp" #include "utils/errors.hpp" #include "utils/slugify.hpp" @@ -20,10 +21,10 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const userver::formats::json::Value& request_json, userver::server::request::RequestContext& context) const { auto slug = request.GetPathArg("slug"); - real_medium::dto::UpdateArticleRequest updateRequest; + handlers::UpdateArticleRequest updateRequest = + request_json["article"].As(); try { - updateRequest = real_medium::dto::UpdateArticleRequest::Parse( - request_json["article"], request); + validator::validate(updateRequest); } catch (const real_medium::utils::error::ValidationException& ex) { request.SetResponseStatus( userver::server::http::HttpStatus::kUnprocessableEntity); diff --git a/src/handlers/comments/comment_post.cpp b/src/handlers/comments/comment_post.cpp index db13a96..950c8c2 100644 --- a/src/handlers/comments/comment_post.cpp +++ b/src/handlers/comments/comment_post.cpp @@ -1,8 +1,9 @@ +#include #include "comment_post.hpp" #include "db/sql.hpp" -#include "dto/comment.hpp" #include "models/comment.hpp" +#include #include "utils/errors.hpp" #include "utils/make_error.hpp" @@ -23,10 +24,9 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const userver::formats::json::Value& request_json, userver::server::request::RequestContext& context) const { auto user_id = context.GetData>("id"); - const auto comment_json = userver::formats::json::FromString(request.RequestBody())["comment"] - .As(); + .As(); try { validator::validate(comment_json); diff --git a/src/handlers/profiles/profiles.cpp b/src/handlers/profiles/profiles.cpp index 191193a..93e836b 100644 --- a/src/handlers/profiles/profiles.cpp +++ b/src/handlers/profiles/profiles.cpp @@ -1,7 +1,7 @@ #include "profiles.hpp" #include +#include #include "db/sql.hpp" -#include "dto/profile.hpp" #include "models/profile.hpp" #include "utils/make_error.hpp" @@ -14,7 +14,6 @@ using namespace userver::formats; using namespace userver::server::http; using namespace userver::server::request; using namespace userver::storages::postgres; -using namespace real_medium::dto; namespace real_medium::handlers::profiles::get { @@ -31,7 +30,6 @@ json::Value Handler::HandleRequestJsonThrow(const HttpRequest& request, RequestContext& context) const { auto user_id = context.GetData>("id"); const auto& username = request.GetPathArg("username"); - auto res = cluster_->Execute(ClusterHostType::kMaster, sql::kGetProfileByUsername.data(), username, user_id); diff --git a/src/handlers/profiles/profiles_follow.cpp b/src/handlers/profiles/profiles_follow.cpp index 86aeb45..2c5e07a 100644 --- a/src/handlers/profiles/profiles_follow.cpp +++ b/src/handlers/profiles/profiles_follow.cpp @@ -16,61 +16,61 @@ using namespace userver::storages::postgres; namespace real_medium::handlers::profiles::post { -Handler::Handler(const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& component_context) - : HttpHandlerJsonBase(config, component_context), - pg_cluster_(component_context - .FindComponent( - "realmedium-database") - .GetCluster()) {} + Handler::Handler(const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& component_context) + : HttpHandlerJsonBase(config, component_context), + pg_cluster_(component_context + .FindComponent( + "realmedium-database") + .GetCluster()) {} -userver::formats::json::Value Handler::HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& context) const { - auto user_id = context.GetData>("id"); - const auto& username = request.GetPathArg("username"); - if (username.empty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return utils::error::MakeError("username", "It is null."); - } + userver::formats::json::Value Handler::HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& context) const { + auto user_id = context.GetData>("id"); + const auto& username = request.GetPathArg("username"); + if (username.empty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return utils::error::MakeError("username", "It is null."); + } - const auto res_find_id_username = - pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, - sql::kFindUserIDByUsername.data(), username); - if (res_find_id_username.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return utils::error::MakeError("username", - "There is no user with this nickname."); - } + const auto res_find_id_username = + pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, + sql::kFindUserIDByUsername.data(), username); + if (res_find_id_username.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return utils::error::MakeError("username", + "There is no user with this nickname."); + } - std::string username_id = res_find_id_username.AsSingleRow(); + std::string username_id = res_find_id_username.AsSingleRow(); - if (username_id == user_id) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kBadRequest); - return utils::error::MakeError("username", - "Username is author of the request."); - } + if (username_id == user_id) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kBadRequest); + return utils::error::MakeError("username", + "Username is author of the request."); + } - const auto res_following = - pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, - sql::KFollowingUser.data(), username_id, user_id); + const auto res_following = + pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, + sql::KFollowingUser.data(), username_id, user_id); - const auto profile = res_following.AsSingleRow( - userver::storages::postgres::kRowTag); + const auto profile = res_following.AsSingleRow( + userver::storages::postgres::kRowTag); - if (!profile.isFollowing) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kBadRequest); - return utils::error::MakeError("user_id", "has already followed"); - } + if (!profile.following) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kBadRequest); + return utils::error::MakeError("user_id", "has already followed"); + } - userver::formats::json::ValueBuilder builder; - builder["profile"] = profile; + userver::formats::json::ValueBuilder builder; + builder["profile"] = profile; - return builder.ExtractValue(); -} -} // namespace real_medium::handlers::profiles::post + return builder.ExtractValue(); + } +} // namespace real_medium::handlers::profiles::post \ No newline at end of file diff --git a/src/handlers/profiles/profiles_follow_delete.cpp b/src/handlers/profiles/profiles_follow_delete.cpp index 32182c1..cb81340 100644 --- a/src/handlers/profiles/profiles_follow_delete.cpp +++ b/src/handlers/profiles/profiles_follow_delete.cpp @@ -61,7 +61,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( res_unfollowing.AsSingleRow( userver::storages::postgres::kRowTag); - if (profile.isFollowing) { + if (profile.following) { auto& response = request.GetHttpResponse(); response.SetStatus(userver::server::http::HttpStatus::kBadRequest); return utils::error::MakeError("user_id", "has already unfollowed"); diff --git a/src/handlers/users/user_get.cpp b/src/handlers/users/user_get.cpp index 52cb3d4..9a5e8e5 100644 --- a/src/handlers/users/user_get.cpp +++ b/src/handlers/users/user_get.cpp @@ -1,7 +1,6 @@ #include "user_get.hpp" #include "db/sql.hpp" -#include "dto/user.hpp" #include "models/user.hpp" #include "utils/make_error.hpp" diff --git a/src/handlers/users/user_put.cpp b/src/handlers/users/user_put.cpp index f9b3881..4ed6e4a 100644 --- a/src/handlers/users/user_put.cpp +++ b/src/handlers/users/user_put.cpp @@ -1,7 +1,7 @@ #include "user_put.hpp" #include +#include #include "db/sql.hpp" -#include "dto/user.hpp" #include "models/user.hpp" #include "utils/errors.hpp" #include "utils/random.hpp" @@ -22,8 +22,8 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( userver::server::request::RequestContext& context) const { auto user_id = context.GetData>("id"); - dto::UserUpdateDTO user_change_data = - request_json["user"].As(); + handlers::UserUpdateDTO user_change_data = + request_json["user"].As(); try { validator::validate(user_change_data); diff --git a/src/handlers/users/users.cpp b/src/handlers/users/users.cpp index 1be8bf3..9e8c397 100644 --- a/src/handlers/users/users.cpp +++ b/src/handlers/users/users.cpp @@ -3,9 +3,9 @@ #include "users.hpp" #include +#include #include "db/sql.hpp" -#include "dto/user.hpp" #include "models/user.hpp" #include "utils/errors.hpp" #include "utils/make_error.hpp" @@ -27,8 +27,8 @@ userver::formats::json::Value RegisterUser::HandleRequestJsonThrow( const userver::server::http::HttpRequest& request, const userver::formats::json::Value& request_json, userver::server::request::RequestContext& context) const { - dto::UserRegistrationDTO user_register = - request_json["user"].As(); + handlers::UserRegistrationDTO user_register = + request_json["user"].As(); ; try { diff --git a/src/handlers/users/users_login.cpp b/src/handlers/users/users_login.cpp index f52a45c..05c5c69 100644 --- a/src/handlers/users/users_login.cpp +++ b/src/handlers/users/users_login.cpp @@ -7,9 +7,9 @@ #include #include #include +#include #include "db/sql.hpp" -#include "dto/user.hpp" #include "models/user.hpp" #include "utils/errors.hpp" #include "validators/validators.hpp" @@ -34,7 +34,7 @@ class LoginUser final : public userver::server::handlers::HttpHandlerJsonBase { const userver::server::http::HttpRequest& request, const userver::formats::json::Value& request_json, userver::server::request::RequestContext&) const override { - dto::UserLoginDTO user_login = request_json["user"].As(); + auto&& user_login = request_json["user"].As(); ; try { diff --git a/src/models/article.cpp b/src/models/article.cpp index 5c5d643..34ad07b 100644 --- a/src/models/article.cpp +++ b/src/models/article.cpp @@ -12,7 +12,7 @@ userver::formats::json::Value Serialize( item["title"] = article.title; item["description"] = article.description; item["body"] = article.body; - item["tagList"] = article.tags; + item["tags"] = article.tags; item["createdAt"] = article.createdAt; item["updatedAt"] = article.updatedAt; item["favorited"] = article.isFavorited; diff --git a/src/models/profile.cpp b/src/models/profile.cpp index 52287a9..2d40557 100644 --- a/src/models/profile.cpp +++ b/src/models/profile.cpp @@ -11,7 +11,7 @@ userver::formats::json::Value Serialize( item["username"] = profile.username; item["bio"] = profile.bio; item["image"] = profile.image; - item["following"] = profile.isFollowing; + item["following"] = profile.following; return item.ExtractValue(); } diff --git a/src/models/profile.hpp b/src/models/profile.hpp index 508faff..048552e 100644 --- a/src/models/profile.hpp +++ b/src/models/profile.hpp @@ -14,8 +14,8 @@ struct Profile final { std::string username; std::optional bio; std::optional image; - bool isFollowing{false}; - auto Introspect() { return std::tie(username, bio, image, isFollowing); } + bool following{false}; + auto Introspect() { return std::tie(username, bio, image, following); } }; userver::formats::json::Value Serialize( diff --git a/src/utils/random.cpp b/src/utils/random.cpp index 628a487..426893c 100644 --- a/src/utils/random.cpp +++ b/src/utils/random.cpp @@ -11,4 +11,4 @@ std::string GenerateSalt() { } return salt; } -} // namespace real_medium::utils::random +} // namespace real_medium::utils::random \ No newline at end of file diff --git a/src/validators/article_validators.cpp b/src/validators/article_validators.cpp index 24c6db1..5409437 100644 --- a/src/validators/article_validators.cpp +++ b/src/validators/article_validators.cpp @@ -26,7 +26,7 @@ void ValidateTags(const std::vector& tags) { static constexpr std::size_t MAX_TAG_NAME_LEN = 256; for (const auto& tag : tags) { - CheckLength(tag, "tagList", MIN_TAG_NAME_LEN, MAX_TAG_NAME_LEN); + CheckLength(tag, "tags", MIN_TAG_NAME_LEN, MAX_TAG_NAME_LEN); } } diff --git a/src/validators/validator_test.cpp b/src/validators/validator_test.cpp index 4b0d9d8..347fbf5 100644 --- a/src/validators/validator_test.cpp +++ b/src/validators/validator_test.cpp @@ -24,7 +24,7 @@ UTEST(UserValidation, UsernameValidation) { } UTEST(UserValidation, LoginValidation) { - using real_medium::dto::UserLoginDTO; + using real_medium::handlers::UserLoginDTO; UEXPECT_NO_THROW(validate(UserLoginDTO{"kek@lol.ru", "keklol"})); UEXPECT_THROW(validate(UserLoginDTO{std::nullopt, "keklol"}), @@ -34,7 +34,7 @@ UTEST(UserValidation, LoginValidation) { } UTEST(UserValidation, RegisterValidation) { - using real_medium::dto::UserRegistrationDTO; + using real_medium::handlers::UserRegistrationDTO; UEXPECT_NO_THROW( validate(UserRegistrationDTO{"kek", "kek@lol.ru", "keklol"})); @@ -49,7 +49,7 @@ UTEST(UserValidation, RegisterValidation) { } UTEST(CommentValidation, CreateCommentValidation) { - using real_medium::dto::AddComment; + using real_medium::handlers::AddComment; UEXPECT_NO_THROW(validate(AddComment{"some body"})); UEXPECT_THROW(validate(AddComment{std::nullopt}), ValidationException); @@ -57,7 +57,7 @@ UTEST(CommentValidation, CreateCommentValidation) { } UTEST(ArticleValidation, CreateArticleValidation) { - using real_medium::dto::CreateArticleRequest; + using real_medium::handlers::CreateArticleRequest; UEXPECT_NO_THROW(validate( CreateArticleRequest{"title", "description", "some body", std::nullopt})); @@ -86,7 +86,7 @@ UTEST(ArticleValidation, CreateArticleValidation) { } UTEST(ArticleValidation, UpdateArticleValidation) { - using real_medium::dto::UpdateArticleRequest; + using real_medium::handlers::UpdateArticleRequest; UEXPECT_NO_THROW( validate(UpdateArticleRequest{"title", "description", "some body"})); diff --git a/src/validators/validators.cpp b/src/validators/validators.cpp index fbf3025..c3d32f2 100644 --- a/src/validators/validators.cpp +++ b/src/validators/validators.cpp @@ -3,9 +3,10 @@ #include "user_validators.hpp" #include "utils/errors.hpp" + namespace real_medium::validator { -void validate(const dto::UserLoginDTO& dto) { +void validate(const handlers::UserLoginDTO& dto) { if (!dto.email) { throw utils::error::ValidationException("email", "Field is missing"); } else if (!ValidateEmail(dto.email.value())) { @@ -19,7 +20,7 @@ void validate(const dto::UserLoginDTO& dto) { } } -void validate(const dto::UserRegistrationDTO& dto) { +void validate(const handlers::UserRegistrationDTO& dto) { if (!dto.username) { throw utils::error::ValidationException("username", "Field is missing"); } else if (!ValidateUsername(dto.username.value())) { @@ -38,7 +39,7 @@ void validate(const dto::UserRegistrationDTO& dto) { throw utils::error::ValidationException("password", "Invalid value"); } } -void validate(const dto::UserUpdateDTO& dto) { +void validate(const handlers::UserUpdateDTO& dto) { if (dto.username && !ValidateUsername(dto.username.value())) { throw utils::error::ValidationException("username", "Invalid field"); } @@ -51,7 +52,7 @@ void validate(const dto::UserUpdateDTO& dto) { throw utils::error::ValidationException("password", "Invalid field"); } } -void validate(const dto::AddComment& dto) { +void validate(const handlers::AddComment& dto) { if (!dto.body) { throw utils::error::ValidationException("body", "Field is missing"); } @@ -61,7 +62,7 @@ void validate(const dto::AddComment& dto) { } } -void validate(const dto::CreateArticleRequest& dto) { +void validate(const handlers::CreateArticleRequest& dto) { if (!dto.title) { throw utils::error::ValidationException("title", "Field is missing"); } else { @@ -84,7 +85,7 @@ void validate(const dto::CreateArticleRequest& dto) { ValidateTags(dto.tags.value()); } } -void validate(const dto::UpdateArticleRequest& dto) { +void validate(const handlers::UpdateArticleRequest& dto) { if (dto.title) { ValidateTitle(dto.title.value()); } diff --git a/src/validators/validators.hpp b/src/validators/validators.hpp index 36ea9d5..7865d2b 100644 --- a/src/validators/validators.hpp +++ b/src/validators/validators.hpp @@ -1,21 +1,19 @@ #pragma once -#include "dto/article.hpp" -#include "dto/comment.hpp" -#include "dto/user.hpp" +#include namespace real_medium::validator { -void validate(const dto::UserLoginDTO& dto); +void validate(const handlers::UserLoginDTO& dto); -void validate(const dto::UserRegistrationDTO& dto); +void validate(const handlers::UserRegistrationDTO& dto); -void validate(const dto::UserUpdateDTO& dto); +void validate(const handlers::UserUpdateDTO& dto); -void validate(const dto::AddComment& dto); +void validate(const handlers::AddComment& dto); -void validate(const dto::CreateArticleRequest& dto); +void validate(const handlers::CreateArticleRequest& dto); -void validate(const dto::UpdateArticleRequest& dto); +void validate(const handlers::UpdateArticleRequest& dto); } // namespace real_medium::validator diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 893d1b1..0cbb204 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,4 @@ + set(CONFIG_VARS_PATH "${CMAKE_SOURCE_DIR}/configs/config_vars_testing.yaml") if (EXISTS "${CONFIG_VARS_PATH}") set(PYTEST_ARGS_CONFIG_VARS "--service-config-vars=${CONFIG_VARS_PATH}") diff --git a/tests/articles/test_list_articles.py b/tests/articles/test_list_articles.py index fa1c449..1fa8e0b 100644 --- a/tests/articles/test_list_articles.py +++ b/tests/articles/test_list_articles.py @@ -29,8 +29,8 @@ async def test_list_articles_unauthorized(service_client): tag = 'huba-buba' article_lst = ArticleList(5, author_profile) - article_lst.articles[3].tagList.append(tag) - article_lst.articles[4].tagList.append(tag) + article_lst.articles[3].tags.append(tag) + article_lst.articles[4].tags.append(tag) for article in article_lst.articles: response = await create_article(service_client, article, author_token) assert response.status == HTTPStatus.OK @@ -89,8 +89,8 @@ async def test_list_articles(service_client): tag = 'huba-buba' article_lst = ArticleList(5, author_profile) - article_lst.articles[3].tagList.append(tag) - article_lst.articles[4].tagList.append(tag) + article_lst.articles[3].tags.append(tag) + article_lst.articles[4].tags.append(tag) for article in article_lst.articles: response = await create_article(service_client, article, author_token) assert response.status == HTTPStatus.OK diff --git a/tests/helpers/models.py b/tests/helpers/models.py index ec80bf0..2a38785 100644 --- a/tests/helpers/models.py +++ b/tests/helpers/models.py @@ -40,7 +40,7 @@ class Article(BaseModel): title: str = Field(default_factory=generate_title) description: str = Field(default_factory=fake.sentence) body: str = Field(default_factory=fake.paragraph) - tagList: list = Field(default_factory=fake.words) + tags: list = Field(default_factory=fake.words) favorited: bool = False favoritesCount: int = 0 author: Optional[Profile] = None diff --git a/tests/helpers/utils.py b/tests/helpers/utils.py index 7de2717..e72c929 100644 --- a/tests/helpers/utils.py +++ b/tests/helpers/utils.py @@ -34,7 +34,7 @@ def __str__(self) -> str: class RequiredFields(tuple, Enum): LOGIN = 'email', 'password' REGISTRATION = 'username', 'email', 'password' - CREATE_ARTICLE = 'title', 'description', 'body', 'tagList' + CREATE_ARTICLE = 'title', 'description', 'body', 'tags' UPDATE_ARTICLE = 'title', 'description', 'body' ADD_COMMENT = 'body' diff --git a/tests/helpers/validators.py b/tests/helpers/validators.py index 2c65da5..5e26244 100644 --- a/tests/helpers/validators.py +++ b/tests/helpers/validators.py @@ -29,13 +29,13 @@ def validate_article(article, response): def validate_article_json(article, response_json): - response_json['tagList'] = set(response_json['tagList']) + response_json['tags'] = set(response_json['tags']) return response_json == { 'slug': article.slug, 'title': article.title, 'description': article.description, 'body': article.body, - 'tagList': set(article.tagList), + 'tags': set(article.tags), 'createdAt': matching.datetime_string, 'updatedAt': matching.datetime_string, 'favorited': article.favorited, @@ -81,5 +81,5 @@ def validate_comments(commentList, response): return True -def validate_tags(tagList, response): - return set(response.json()['tags']) == tagList +def validate_tags(tags, response): + return set(response.json()['tags']) == tags diff --git a/tests/tags/test_get_tags.py b/tests/tags/test_get_tags.py index f0ab8e5..5b9ab7d 100644 --- a/tests/tags/test_get_tags.py +++ b/tests/tags/test_get_tags.py @@ -23,8 +23,8 @@ async def test_get_tags(service_client): for article in articleList.articles: response = await create_article(service_client, article, user_token) assert response.status == HTTPStatus.OK - print(article.tagList) - tags |= set(article.tagList) + print(article.tags) + tags |= set(article.tags) response = await get_tags(service_client) assert response.status == HTTPStatus.OK