From b3b14cd3d82dc1620928b6fa59cb24fd5913b288 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 14 Jul 2025 12:54:42 +0300 Subject: [PATCH] Use the same formatting as the userver --- .clang-format | 14 +- src/cache/articles_cache.cpp | 124 +++++++-------- src/cache/articles_cache.hpp | 89 +++++------ src/cache/comments_cache.cpp | 40 ++--- src/cache/comments_cache.hpp | 36 ++--- src/dto/article.cpp | 114 +++++++------- src/dto/article.hpp | 30 ++-- src/dto/comment.cpp | 43 +++--- src/dto/comment.hpp | 18 +-- src/dto/filter.cpp | 64 ++++---- src/handlers/articles/articles_favorite.cpp | 69 +++++---- src/handlers/articles/articles_favorite.hpp | 15 +- src/handlers/articles/articles_get.cpp | 36 ++--- src/handlers/articles/articles_get.hpp | 15 +- src/handlers/articles/articles_post.cpp | 96 ++++++------ src/handlers/articles/articles_post.hpp | 15 +- .../articles/articles_slug_delete.cpp | 48 +++--- .../articles/articles_slug_delete.hpp | 19 +-- src/handlers/articles/articles_slug_get.cpp | 32 ++-- src/handlers/articles/articles_slug_get.hpp | 15 +- src/handlers/articles/articles_slug_put.cpp | 92 +++++------ src/handlers/articles/articles_slug_put.hpp | 16 +- src/handlers/articles/articles_unfavorite.cpp | 69 +++++---- src/handlers/articles/articles_unfavorite.hpp | 19 +-- src/handlers/articles/feed_articles.cpp | 36 ++--- src/handlers/articles/feed_articles.hpp | 15 +- src/handlers/auth/auth_bearer.cpp | 140 ++++++++--------- src/handlers/auth/auth_bearer.hpp | 17 +- src/handlers/comments/comment_delete.cpp | 55 ++++--- src/handlers/comments/comment_delete.hpp | 15 +- src/handlers/comments/comment_post.cpp | 79 +++++----- src/handlers/comments/comment_post.hpp | 15 +- src/handlers/comments/comments_get.cpp | 48 +++--- src/handlers/comments/comments_get.hpp | 23 +-- src/handlers/common.cpp | 14 +- src/handlers/common.hpp | 35 +++-- src/handlers/profiles/profiles.cpp | 47 +++--- src/handlers/profiles/profiles.hpp | 15 +- src/handlers/profiles/profiles_follow.cpp | 80 +++++----- src/handlers/profiles/profiles_follow.hpp | 15 +- .../profiles/profiles_follow_delete.cpp | 80 +++++----- .../profiles/profiles_follow_delete.hpp | 15 +- src/handlers/tags/tags.cpp | 30 ++-- src/handlers/tags/tags.hpp | 13 +- src/handlers/users/user_get.cpp | 29 ++-- src/handlers/users/user_get.hpp | 15 +- src/handlers/users/user_put.cpp | 73 ++++----- src/handlers/users/user_put.hpp | 15 +- src/handlers/users/users.cpp | 74 ++++----- src/handlers/users/users.hpp | 15 +- src/handlers/users/users_login.cpp | 101 ++++++------ src/main.cpp | 63 ++++---- src/models/article.cpp | 29 ++-- src/models/article.hpp | 98 +++++++----- src/models/comment.cpp | 19 ++- src/models/comment.hpp | 48 +++--- src/models/profile.hpp | 2 +- src/models/user.cpp | 19 ++- src/models/user.hpp | 27 ++-- src/utils/errors.hpp | 18 +-- src/utils/jwt.cpp | 10 +- src/utils/make_error.cpp | 9 +- src/utils/make_error.hpp | 5 +- src/utils/random.cpp | 12 +- src/utils/slugify.cpp | 49 +++--- src/utils/slugify_test.cpp | 28 ++-- src/validators/article_validators.cpp | 28 ++-- src/validators/length_validator.cpp | 16 +- src/validators/length_validator.hpp | 7 +- src/validators/user_validators.cpp | 4 +- src/validators/validator_test.cpp | 112 ++++++-------- src/validators/validators.cpp | 146 +++++++++--------- 72 files changed, 1470 insertions(+), 1516 deletions(-) diff --git a/.clang-format b/.clang-format index 276eaf0..3f91dfb 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,15 @@ -BasedOnStyle: google +BasedOnStyle: Google DerivePointerAlignment: false IncludeBlocks: Preserve +AttributeMacros: ["noexcept"] +IndentRequires: false +ColumnLimit: 120 +IndentWidth: 4 +TabWidth: 4 +AccessModifierOffset: -4 +BinPackParameters: false +BinPackArguments: false +AllowAllParametersOfDeclarationOnNextLine: false +AlignAfterOpenBracket: BlockIndent +AlwaysBreakAfterDefinitionReturnType: None +PenaltyReturnTypeOnItsOwnLine: 200 diff --git a/src/cache/articles_cache.cpp b/src/cache/articles_cache.cpp index 9efb1fb..bf5fc8e 100644 --- a/src/cache/articles_cache.cpp +++ b/src/cache/articles_cache.cpp @@ -5,94 +5,78 @@ namespace real_medium::cache::articles_cache { userver::storages::postgres::Query ArticlesCachePolicy::kQuery = - userver::storages::postgres::Query( - real_medium::sql::kSelectFullArticleInfo.data()); + userver::storages::postgres::Query(real_medium::sql::kSelectFullArticleInfo.data()); void ArticlesCacheContainer::insert_or_assign(Key&& key, Article&& article) { - auto articlePtr = std::make_shared(std::move(article)); - auto oldValue = articleByKey_.find(key); - if (oldValue != articleByKey_.end()) { - articleBySlug_.erase(oldValue->second->slug); - for (const auto& oldFollower : oldValue->second->authorFollowedByUsersIds) - if (articlePtr->authorFollowedByUsersIds.find(oldFollower) == - articlePtr->authorFollowedByUsersIds.end()) - articlesByFollower_[oldFollower].erase(articlePtr->articleId); - } + auto articlePtr = std::make_shared(std::move(article)); + auto oldValue = articleByKey_.find(key); + if (oldValue != articleByKey_.end()) { + articleBySlug_.erase(oldValue->second->slug); + for (const auto& oldFollower : oldValue->second->authorFollowedByUsersIds) + if (articlePtr->authorFollowedByUsersIds.find(oldFollower) == articlePtr->authorFollowedByUsersIds.end()) + articlesByFollower_[oldFollower].erase(articlePtr->articleId); + } - articleByKey_.insert_or_assign(key, articlePtr); - articleBySlug_.insert_or_assign(articlePtr->slug, articlePtr); - for (const auto& follower : articlePtr->authorFollowedByUsersIds) - articlesByFollower_[follower].insert_or_assign(articlePtr->articleId, - articlePtr); - recentArticles_.insert_or_assign( - {articlePtr->createdAt, articlePtr->articleId}, articlePtr); + articleByKey_.insert_or_assign(key, articlePtr); + articleBySlug_.insert_or_assign(articlePtr->slug, articlePtr); + for (const auto& follower : articlePtr->authorFollowedByUsersIds) + articlesByFollower_[follower].insert_or_assign(articlePtr->articleId, articlePtr); + recentArticles_.insert_or_assign({articlePtr->createdAt, articlePtr->articleId}, articlePtr); } size_t ArticlesCacheContainer::size() const { return articleByKey_.size(); } -ArticlesCacheContainer::ArticlePtr ArticlesCacheContainer::findArticleBySlug( - const Slug& slug) const { - auto it = articleBySlug_.find(slug); - if (it == articleBySlug_.end()) return nullptr; - return it->second; +ArticlesCacheContainer::ArticlePtr ArticlesCacheContainer::findArticleBySlug(const Slug& slug) const { + auto it = articleBySlug_.find(slug); + if (it == articleBySlug_.end()) return nullptr; + return it->second; } -std::vector -ArticlesCacheContainer::getRecent( - real_medium::handlers::ArticleFilterDTO& filter) const { - std::vector articles; - int offset = 0; - for (const auto& it : recentArticles_) { - if (filter.limit && - articles.size() >= - userver::utils::numeric_cast(filter.limit)) - break; +std::vector ArticlesCacheContainer::getRecent( + real_medium::handlers::ArticleFilterDTO& filter +) const { + std::vector articles; + int offset = 0; + for (const auto& it : recentArticles_) { + if (filter.limit && articles.size() >= userver::utils::numeric_cast(filter.limit)) break; - const auto& tags = it.second->tags; - if (filter.tag && it.second->tags.find(filter.tag.value()) == tags.end()) - continue; + const auto& tags = it.second->tags; + if (filter.tag && it.second->tags.find(filter.tag.value()) == tags.end()) continue; - if (filter.author && it.second->authorInfo.username != filter.author) - continue; + if (filter.author && it.second->authorInfo.username != filter.author) continue; - const auto& favorited = it.second->articleFavoritedByUsernames; - if (filter.favorited && - favorited.find(filter.favorited.value()) == favorited.end()) - continue; + const auto& favorited = it.second->articleFavoritedByUsernames; + if (filter.favorited && favorited.find(filter.favorited.value()) == favorited.end()) continue; - if (filter.offset && offset < filter.offset) { - ++offset; - continue; + if (filter.offset && offset < filter.offset) { + ++offset; + continue; + } + articles.push_back(it.second); } - articles.push_back(it.second); - } - return articles; + return articles; } -std::vector ArticlesCacheContainer::getFeed( - real_medium::handlers::FeedArticleFilterDTO& filter, UserId authId) const { - auto followedArticlesUMap = articlesByFollower_.find(authId); - if (followedArticlesUMap == articlesByFollower_.end()) return {}; +std::vector +ArticlesCacheContainer::getFeed(real_medium::handlers::FeedArticleFilterDTO& filter, UserId authId) const { + auto followedArticlesUMap = articlesByFollower_.find(authId); + if (followedArticlesUMap == articlesByFollower_.end()) return {}; - RecentArticlesMap followedArticlesOrdered; - for (const auto& it : followedArticlesUMap->second) - followedArticlesOrdered.insert_or_assign( - {it.second->createdAt, it.second->articleId}, it.second); + RecentArticlesMap followedArticlesOrdered; + for (const auto& it : followedArticlesUMap->second) + followedArticlesOrdered.insert_or_assign({it.second->createdAt, it.second->articleId}, it.second); - std::vector articles; - ; - int offset = 0; - for (const auto& it : followedArticlesOrdered) { - if (filter.limit && - articles.size() >= - userver::utils::numeric_cast(filter.limit)) - break; - if (filter.offset && offset < filter.offset) { - ++offset; - continue; + std::vector articles; + ; + int offset = 0; + for (const auto& it : followedArticlesOrdered) { + if (filter.limit && articles.size() >= userver::utils::numeric_cast(filter.limit)) break; + if (filter.offset && offset < filter.offset) { + ++offset; + continue; + } + articles.push_back(it.second); } - articles.push_back(it.second); - } - return articles; + return articles; } } // namespace real_medium::cache::articles_cache diff --git a/src/cache/articles_cache.hpp b/src/cache/articles_cache.hpp index 9f17e22..ec4af9f 100644 --- a/src/cache/articles_cache.hpp +++ b/src/cache/articles_cache.hpp @@ -4,65 +4,56 @@ #include #include #include -#include "../db/sql.hpp" -#include "../dto/filter.hpp" -#include "../models/article.hpp" +#include "db/sql.hpp" +#include "dto/filter.hpp" +#include "models/article.hpp" namespace real_medium::cache::articles_cache { class ArticlesCacheContainer { - using Timepoint = userver::storages::postgres::TimePointTz; - using Slug = std::string; - using UserId = std::string; + using Timepoint = userver::storages::postgres::TimePointTz; + using Slug = std::string; + using UserId = std::string; - public: - using Key = real_medium::models::ArticleId; - using Article = real_medium::models::FullArticleInfo; - using ArticlePtr = std::shared_ptr; - using AuthorName = std::string; - void insert_or_assign(Key&& key, Article&& config); - size_t size() const; +public: + using Key = real_medium::models::ArticleId; + using Article = real_medium::models::FullArticleInfo; + using ArticlePtr = std::shared_ptr; + using AuthorName = std::string; + void insert_or_assign(Key&& key, Article&& config); + size_t size() const; - ArticlePtr findArticleBySlug(const Slug& slug) const; - std::vector getRecent( - real_medium::handlers::ArticleFilterDTO& filter_) const; - std::vector getFeed( - real_medium::handlers::FeedArticleFilterDTO& filter_, - UserId authId_) const; + ArticlePtr findArticleBySlug(const Slug& slug) const; + std::vector getRecent(real_medium::handlers::ArticleFilterDTO& filter_) const; + std::vector getFeed(real_medium::handlers::FeedArticleFilterDTO& filter_, UserId authId_) const; - private: - struct TimepointedArticle { - Timepoint created; - Key articleId; - bool operator<(const TimepointedArticle& other) const { - return created != other.created ? created < other.created - : articleId < other.articleId; - } - bool operator==(const TimepointedArticle& other) const { - return created == other.created && articleId == other.articleId; - } - bool operator>(const TimepointedArticle& other) const { - return !(*this == other) && !(*this < other); - } - }; +private: + struct TimepointedArticle { + Timepoint created; + Key articleId; + bool operator<(const TimepointedArticle& other) const { + return created != other.created ? created < other.created : articleId < other.articleId; + } + bool operator==(const TimepointedArticle& other) const { + return created == other.created && articleId == other.articleId; + } + bool operator>(const TimepointedArticle& other) const { return !(*this == other) && !(*this < other); } + }; - using RecentArticlesMap = - std::map>; - std::unordered_map articleByKey_; - std::unordered_map articleBySlug_; - std::unordered_map> - articlesByFollower_; - RecentArticlesMap recentArticles_; + using RecentArticlesMap = std::map>; + std::unordered_map articleByKey_; + std::unordered_map articleBySlug_; + std::unordered_map> articlesByFollower_; + RecentArticlesMap recentArticles_; }; struct ArticlesCachePolicy { - static constexpr auto kName = "articles-cache"; - using ValueType = ArticlesCacheContainer::Article; - using CacheContainer = ArticlesCacheContainer; - static constexpr auto kKeyMember = &ValueType::articleId; - static userver::storages::postgres::Query kQuery; - static constexpr auto kUpdatedField = "updated_at"; - using UpdatedFieldType = userver::storages::postgres::TimePointTz; + static constexpr auto kName = "articles-cache"; + using ValueType = ArticlesCacheContainer::Article; + using CacheContainer = ArticlesCacheContainer; + static constexpr auto kKeyMember = &ValueType::articleId; + static userver::storages::postgres::Query kQuery; + static constexpr auto kUpdatedField = "updated_at"; + using UpdatedFieldType = userver::storages::postgres::TimePointTz; }; using ArticlesCache = ::userver::components::PostgreCache; } // namespace real_medium::cache::articles_cache diff --git a/src/cache/comments_cache.cpp b/src/cache/comments_cache.cpp index 3ba61eb..512fb4b 100644 --- a/src/cache/comments_cache.cpp +++ b/src/cache/comments_cache.cpp @@ -1,7 +1,7 @@ #include #include -#include "userver/storages/postgres/query.hpp" -#include "userver/utils/algo.hpp" +#include +#include #include "comments_cache.hpp" #include "db/sql.hpp" @@ -9,32 +9,32 @@ namespace real_medium::cache::comments_cache { userver::storages::postgres::Query CommentCachePolicy::kQuery = - userver::storages::postgres::Query( - real_medium::sql::kSelectCachedComments.data()); + userver::storages::postgres::Query(real_medium::sql::kSelectCachedComments.data()); void CommentsCacheContainer::insert_or_assign( real_medium::cache::comments_cache::CommentsCacheContainer::Key&& commentId, - real_medium::cache::comments_cache::CommentsCacheContainer::Comment&& - comment) { - auto commentPtr = std::make_shared(std::move(comment)); - if (comment_to_key_.count(commentId)) { - auto& oldSlug = comment_to_key_[commentId]->slug; - if (oldSlug != commentPtr->slug) { - auto& comments = comments_to_slug_[oldSlug]; - comments_to_slug_[commentPtr->slug] = comments; - comments_to_slug_.erase(oldSlug); + real_medium::cache::comments_cache::CommentsCacheContainer::Comment&& comment +) { + auto commentPtr = std::make_shared(std::move(comment)); + if (comment_to_key_.count(commentId)) { + auto& oldSlug = comment_to_key_[commentId]->slug; + if (oldSlug != commentPtr->slug) { + auto& comments = comments_to_slug_[oldSlug]; + comments_to_slug_[commentPtr->slug] = comments; + comments_to_slug_.erase(oldSlug); + } } - } - comment_to_key_.insert_or_assign(commentId, commentPtr); - comments_to_slug_[commentPtr->slug].insert_or_assign(commentId, commentPtr); + comment_to_key_.insert_or_assign(commentId, commentPtr); + comments_to_slug_[commentPtr->slug].insert_or_assign(commentId, commentPtr); }; size_t CommentsCacheContainer::size() const { return comment_to_key_.size(); } -std::map -CommentsCacheContainer::findComments(const Slug& slug) const { - if (!comments_to_slug_.count(slug)) return {}; - return comments_to_slug_.at(slug); +std::map CommentsCacheContainer::findComments( + const Slug& slug +) const { + if (!comments_to_slug_.count(slug)) return {}; + return comments_to_slug_.at(slug); } } // namespace real_medium::cache::comments_cache diff --git a/src/cache/comments_cache.hpp b/src/cache/comments_cache.hpp index 3d3463d..b92648d 100644 --- a/src/cache/comments_cache.hpp +++ b/src/cache/comments_cache.hpp @@ -12,29 +12,29 @@ namespace real_medium::cache::comments_cache { class CommentsCacheContainer { - public: - using Key = real_medium::models::CommentId; - using Comment = real_medium::models::CachedComment; - using CommentPtr = std::shared_ptr; - using Slug = std::string; - void insert_or_assign(Key&& key, Comment&& config); - size_t size() const; +public: + using Key = real_medium::models::CommentId; + using Comment = real_medium::models::CachedComment; + using CommentPtr = std::shared_ptr; + using Slug = std::string; + void insert_or_assign(Key&& key, Comment&& config); + size_t size() const; - std::map findComments(const Slug& key) const; + std::map findComments(const Slug& key) const; - private: - std::unordered_map> comments_to_slug_; - std::unordered_map comment_to_key_; +private: + std::unordered_map> comments_to_slug_; + std::unordered_map comment_to_key_; }; struct CommentCachePolicy { - static constexpr auto kName = "comments-cache"; - using ValueType = real_medium::models::CachedComment; - using CacheContainer = CommentsCacheContainer; - static constexpr auto kKeyMember = &real_medium::models::CachedComment::id; - static userver::storages::postgres::Query kQuery; - static constexpr auto kUpdatedField = "c.updated_at"; - using UpdatedFieldType = userver::storages::postgres::TimePointTz; + static constexpr auto kName = "comments-cache"; + using ValueType = real_medium::models::CachedComment; + using CacheContainer = CommentsCacheContainer; + static constexpr auto kKeyMember = &real_medium::models::CachedComment::id; + static userver::storages::postgres::Query kQuery; + static constexpr auto kUpdatedField = "c.updated_at"; + using UpdatedFieldType = userver::storages::postgres::TimePointTz; }; using CommentsCache = ::userver::components::PostgreCache; diff --git a/src/dto/article.cpp b/src/dto/article.cpp index 57e6755..4878491 100644 --- a/src/dto/article.cpp +++ b/src/dto/article.cpp @@ -4,70 +4,64 @@ namespace real_medium::dto { Article Article::Parse(const models::TaggedArticleWithProfile& model) { - Article article; - article.slug = model.slug; - article.title = model.title; - article.description = model.description; - article.body = model.body; - article.tags = model.tags; - article.createdAt = model.createdAt; - article.updatedAt = model.updatedAt; - article.favoritesCount = model.favoritesCount; - article.isFavorited = model.isFavorited; - article.profile.bio = model.authorProfile.bio; - article.profile.image = model.authorProfile.image; - article.profile.username = model.authorProfile.username; - article.profile.following = model.authorProfile.following; - return article; + Article article; + article.slug = model.slug; + article.title = model.title; + article.description = model.description; + article.body = model.body; + article.tags = model.tags; + article.createdAt = model.createdAt; + article.updatedAt = model.updatedAt; + article.favoritesCount = model.favoritesCount; + article.isFavorited = model.isFavorited; + article.profile.bio = model.authorProfile.bio; + article.profile.image = model.authorProfile.image; + article.profile.username = model.authorProfile.username; + article.profile.following = model.authorProfile.following; + return article; } -Article Article::Parse(const models::FullArticleInfo& model, - std::optional authUserId) { - Article article; - article.slug = model.slug; - article.title = model.title; - article.description = model.description; - article.body = model.body; - if (!model.tags.empty()) - article.tags = - std::vector(model.tags.begin(), model.tags.end()); - article.createdAt = model.createdAt; - article.updatedAt = model.updatedAt; - article.favoritesCount = model.articleFavoritedByUsernames.size(); - article.isFavorited = - authUserId ? model.articleFavoritedByUserIds.find(authUserId.value()) != - model.articleFavoritedByUserIds.end() - : false; - article.profile.bio = model.authorInfo.bio; - article.profile.image = model.authorInfo.image; - article.profile.username = model.authorInfo.username; - article.profile.following = - authUserId ? model.authorFollowedByUsersIds.find(authUserId.value()) != - model.authorFollowedByUsersIds.end() - : false; - return article; +Article Article::Parse(const models::FullArticleInfo& model, std::optional authUserId) { + Article article; + article.slug = model.slug; + article.title = model.title; + article.description = model.description; + article.body = model.body; + if (!model.tags.empty()) article.tags = std::vector(model.tags.begin(), model.tags.end()); + article.createdAt = model.createdAt; + article.updatedAt = model.updatedAt; + article.favoritesCount = model.articleFavoritedByUsernames.size(); + article.isFavorited = + authUserId ? model.articleFavoritedByUserIds.find(authUserId.value()) != model.articleFavoritedByUserIds.end() + : false; + article.profile.bio = model.authorInfo.bio; + article.profile.image = model.authorInfo.image; + article.profile.username = model.authorInfo.username; + article.profile.following = + authUserId ? model.authorFollowedByUsersIds.find(authUserId.value()) != model.authorFollowedByUsersIds.end() + : false; + return article; } -userver::formats::json::Value Serialize( - const Article& article, - userver::formats::serialize::To) { - userver::formats::json::ValueBuilder builder; - builder["slug"] = article.slug; - builder["title"] = article.title; - builder["description"] = article.description; - builder["body"] = article.body; - builder["tags"] = userver::formats::common::Type::kArray; - if (article.tags) { - std::for_each( - article.tags->begin(), article.tags->end(), - [&builder](const auto& tag) { builder["tags"].PushBack(tag); }); - } - builder["createdAt"] = article.createdAt; - builder["updatedAt"] = article.updatedAt; - builder["favoritesCount"] = article.favoritesCount; - builder["favorited"] = article.isFavorited; - builder["author"] = article.profile; - return builder.ExtractValue(); +userver::formats::json::Value +Serialize(const Article& article, userver::formats::serialize::To) { + userver::formats::json::ValueBuilder builder; + builder["slug"] = article.slug; + builder["title"] = article.title; + builder["description"] = article.description; + builder["body"] = article.body; + builder["tags"] = userver::formats::common::Type::kArray; + if (article.tags) { + std::for_each(article.tags->begin(), article.tags->end(), [&builder](const auto& tag) { + builder["tags"].PushBack(tag); + }); + } + builder["createdAt"] = article.createdAt; + builder["updatedAt"] = article.updatedAt; + builder["favoritesCount"] = article.favoritesCount; + builder["favorited"] = article.isFavorited; + builder["author"] = article.profile; + return builder.ExtractValue(); } } // namespace real_medium::dto diff --git a/src/dto/article.hpp b/src/dto/article.hpp index 30c98f4..8cb1eff 100644 --- a/src/dto/article.hpp +++ b/src/dto/article.hpp @@ -10,22 +10,20 @@ namespace real_medium::dto { struct Article final { - static Article Parse(const models::TaggedArticleWithProfile& model); - static Article Parse(const models::FullArticleInfo& info, - std::optional authUserId); - std::string slug; - std::string title; - std::string body; - std::string description; - std::optional> tags; - userver::storages::postgres::TimePointTz createdAt; - userver::storages::postgres::TimePointTz updatedAt; - std::int64_t favoritesCount{}; - bool isFavorited{false}; - handlers::Profile profile; + static Article Parse(const models::TaggedArticleWithProfile& model); + static Article Parse(const models::FullArticleInfo& info, std::optional authUserId); + std::string slug; + std::string title; + std::string body; + std::string description; + std::optional> tags; + userver::storages::postgres::TimePointTz createdAt; + userver::storages::postgres::TimePointTz updatedAt; + std::int64_t favoritesCount{}; + bool isFavorited{false}; + handlers::Profile profile; }; -userver::formats::json::Value Serialize( - const Article& data, - userver::formats::serialize::To); +userver::formats::json::Value +Serialize(const Article& data, userver::formats::serialize::To); } // namespace real_medium::dto diff --git a/src/dto/comment.cpp b/src/dto/comment.cpp index c2126f8..7d67916 100644 --- a/src/dto/comment.cpp +++ b/src/dto/comment.cpp @@ -2,33 +2,30 @@ namespace real_medium::dto { -Comment Comment::Parse(const real_medium::models::CachedComment& cachedComment, - std::optional userId) { - Comment comment; - comment.id = cachedComment.id; - comment.body = cachedComment.body; - comment.updatedAt = cachedComment.updated_at; - comment.createdAt = cachedComment.created_at; - comment.author.username = cachedComment.author.username; - comment.author.bio = cachedComment.author.bio; - comment.author.image = cachedComment.author.image; - comment.author.following = - !userId.has_value() ? false : cachedComment.following.count(*userId); - return comment; +Comment Comment::Parse(const real_medium::models::CachedComment& cachedComment, std::optional userId) { + Comment comment; + comment.id = cachedComment.id; + comment.body = cachedComment.body; + comment.updatedAt = cachedComment.updated_at; + comment.createdAt = cachedComment.created_at; + comment.author.username = cachedComment.author.username; + comment.author.bio = cachedComment.author.bio; + comment.author.image = cachedComment.author.image; + comment.author.following = !userId.has_value() ? false : cachedComment.following.count(*userId); + return comment; } -userver::formats::json::Value Serialize( - const Comment& comment, - userver::formats::serialize::To) { - userver::formats::json::ValueBuilder item; +userver::formats::json::Value +Serialize(const Comment& comment, userver::formats::serialize::To) { + userver::formats::json::ValueBuilder item; - item["id"] = comment.id; - item["createdAt"] = comment.createdAt; - item["updatedAt"] = comment.updatedAt; - item["body"] = comment.body; - item["author"] = comment.author; + item["id"] = comment.id; + item["createdAt"] = comment.createdAt; + item["updatedAt"] = comment.updatedAt; + item["body"] = comment.body; + item["author"] = comment.author; - return item.ExtractValue(); + return item.ExtractValue(); } } // namespace real_medium::dto diff --git a/src/dto/comment.hpp b/src/dto/comment.hpp index ba9f1a4..97e716c 100644 --- a/src/dto/comment.hpp +++ b/src/dto/comment.hpp @@ -12,17 +12,15 @@ namespace real_medium::dto { struct Comment final { - static Comment Parse(const real_medium::models::CachedComment& cachedComment, - std::optional userId); - int32_t id; - userver::storages::postgres::TimePointTz createdAt; - userver::storages::postgres::TimePointTz updatedAt; - std::string body; - handlers::Profile author; + static Comment Parse(const real_medium::models::CachedComment& cachedComment, std::optional userId); + int32_t id; + userver::storages::postgres::TimePointTz createdAt; + userver::storages::postgres::TimePointTz updatedAt; + std::string body; + handlers::Profile author; }; -userver::formats::json::Value Serialize( - const Comment& comment, - userver::formats::serialize::To); +userver::formats::json::Value +Serialize(const Comment& comment, userver::formats::serialize::To); } // namespace real_medium::dto diff --git a/src/dto/filter.cpp b/src/dto/filter.cpp index 7d9c006..829064c 100644 --- a/src/dto/filter.cpp +++ b/src/dto/filter.cpp @@ -5,41 +5,39 @@ namespace real_medium::dto { template <> -handlers::FeedArticleFilterDTO Parse( - const userver::server::http::HttpRequest& request) { - handlers::FeedArticleFilterDTO filter; - if (request.HasArg("limit")) { - filter.limit = boost::lexical_cast(request.GetArg("limit")); - filter.limit = std::max(0, filter.limit); - } - if (request.HasArg("offset")) { - filter.offset = boost::lexical_cast(request.GetArg("offset")); - filter.offset = std::max(0, filter.offset); - } - return filter; +handlers::FeedArticleFilterDTO Parse(const userver::server::http::HttpRequest& request) { + handlers::FeedArticleFilterDTO filter; + if (request.HasArg("limit")) { + filter.limit = boost::lexical_cast(request.GetArg("limit")); + filter.limit = std::max(0, filter.limit); + } + if (request.HasArg("offset")) { + filter.offset = boost::lexical_cast(request.GetArg("offset")); + filter.offset = std::max(0, filter.offset); + } + return filter; } template <> -handlers::ArticleFilterDTO Parse( - const userver::server::http::HttpRequest& request) { - handlers::ArticleFilterDTO filter; - if (request.HasArg("tag")) { - filter.tag = request.GetArg("tag"); - } - if (request.HasArg("author")) { - filter.author = request.GetArg("author"); - } - if (request.HasArg("favorited")) { - filter.favorited = request.GetArg("favorited"); - } - if (request.HasArg("limit")) { - filter.limit = boost::lexical_cast(request.GetArg("limit")); - filter.limit = std::max(0, filter.limit); - } - if (request.HasArg("offset")) { - filter.offset = boost::lexical_cast(request.GetArg("offset")); - filter.offset = std::max(0, filter.offset); - } - return filter; +handlers::ArticleFilterDTO Parse(const userver::server::http::HttpRequest& request) { + handlers::ArticleFilterDTO filter; + if (request.HasArg("tag")) { + filter.tag = request.GetArg("tag"); + } + if (request.HasArg("author")) { + filter.author = request.GetArg("author"); + } + if (request.HasArg("favorited")) { + filter.favorited = request.GetArg("favorited"); + } + if (request.HasArg("limit")) { + filter.limit = boost::lexical_cast(request.GetArg("limit")); + filter.limit = std::max(0, filter.limit); + } + if (request.HasArg("offset")) { + filter.offset = boost::lexical_cast(request.GetArg("offset")); + filter.offset = std::max(0, filter.offset); + } + return filter; } } // namespace real_medium::dto diff --git a/src/handlers/articles/articles_favorite.cpp b/src/handlers/articles/articles_favorite.cpp index 4674cbd..c580f9b 100644 --- a/src/handlers/articles/articles_favorite.cpp +++ b/src/handlers/articles/articles_favorite.cpp @@ -10,40 +10,41 @@ namespace real_medium::handlers::articles_favorite::post { 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"); - auto& slug = request.GetPathArg("slug"); - - auto transaction = - GetPg().Begin("favorite_article_transaction", - userver::storages::postgres::ClusterHostType::kMaster, - userver::storages::postgres::Transaction::RW); - - auto res = - transaction.Execute(sql::kInsertFavoritePair.data(), user_id, slug); - - if (!res.IsEmpty()) { - auto article_id = res.AsSingleRow(); - - transaction.Execute(sql::kIncrementFavoritesCount.data(), article_id); - transaction.Commit(); - } - - const auto get_article_res = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kSlave, - real_medium::sql::kGetArticleWithAuthorProfileBySlug.data(), slug, - user_id); - - if (get_article_res.IsEmpty()) { - request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); - return {}; - } - - userver::formats::json::ValueBuilder builder; - builder["article"] = - get_article_res - .AsSingleRow(); - return builder.ExtractValue(); + userver::server::request::RequestContext& context +) const { + auto& user_id = context.GetData>("id"); + auto& slug = request.GetPathArg("slug"); + + auto transaction = GetPg().Begin( + "favorite_article_transaction", + userver::storages::postgres::ClusterHostType::kMaster, + userver::storages::postgres::Transaction::RW + ); + + auto res = transaction.Execute(sql::kInsertFavoritePair.data(), user_id, slug); + + if (!res.IsEmpty()) { + auto article_id = res.AsSingleRow(); + + transaction.Execute(sql::kIncrementFavoritesCount.data(), article_id); + transaction.Commit(); + } + + const auto get_article_res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kSlave, + real_medium::sql::kGetArticleWithAuthorProfileBySlug.data(), + slug, + user_id + ); + + if (get_article_res.IsEmpty()) { + request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); + return {}; + } + + userver::formats::json::ValueBuilder builder; + builder["article"] = get_article_res.AsSingleRow(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::articles_favorite::post diff --git a/src/handlers/articles/articles_favorite.hpp b/src/handlers/articles/articles_favorite.hpp index d304aff..f9b2131 100644 --- a/src/handlers/articles/articles_favorite.hpp +++ b/src/handlers/articles/articles_favorite.hpp @@ -8,15 +8,16 @@ namespace real_medium::handlers::articles_favorite::post { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-articles-favorite-post"; +public: + static constexpr std::string_view kName = "handler-articles-favorite-post"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::articles_favorite::post diff --git a/src/handlers/articles/articles_get.cpp b/src/handlers/articles/articles_get.cpp index 25fa710..d4abdb9 100644 --- a/src/handlers/articles/articles_get.cpp +++ b/src/handlers/articles/articles_get.cpp @@ -12,26 +12,26 @@ namespace real_medium::handlers::articles::get { 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 { - handlers::ArticleFilterDTO filter; + userver::server::request::RequestContext& context +) const { + handlers::ArticleFilterDTO filter; - try { - filter = dto::Parse(request); - } catch (std::bad_cast& ex) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kUnprocessableEntity); - return utils::error::MakeError("filters", "invalid filters entered"); - } + try { + filter = dto::Parse(request); + } catch (std::bad_cast& ex) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return utils::error::MakeError("filters", "invalid filters entered"); + } - auto user_id = context.GetData>("id"); - auto data = GetArticlesCache().Get(); - auto recentArticles = data->getRecent(filter); - userver::formats::json::ValueBuilder builder; - builder["articles"] = userver::formats::common::Type::kArray; - for (auto& article : recentArticles) - builder["articles"].PushBack(dto::Article::Parse(*article, user_id)); - builder["articlesCount"] = recentArticles.size(); - return builder.ExtractValue(); + auto user_id = context.GetData>("id"); + auto data = GetArticlesCache().Get(); + auto recentArticles = data->getRecent(filter); + userver::formats::json::ValueBuilder builder; + builder["articles"] = userver::formats::common::Type::kArray; + for (auto& article : recentArticles) builder["articles"].PushBack(dto::Article::Parse(*article, user_id)); + builder["articlesCount"] = recentArticles.size(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::articles::get diff --git a/src/handlers/articles/articles_get.hpp b/src/handlers/articles/articles_get.hpp index 24cd837..47f42a7 100644 --- a/src/handlers/articles/articles_get.hpp +++ b/src/handlers/articles/articles_get.hpp @@ -7,13 +7,14 @@ namespace real_medium::handlers::articles::get { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-get-articles"; - using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; +public: + static constexpr std::string_view kName = "handler-get-articles"; + using Common::Common; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::articles::get diff --git a/src/handlers/articles/articles_post.cpp b/src/handlers/articles/articles_post.cpp index 1a98c25..21fb541 100644 --- a/src/handlers/articles/articles_post.cpp +++ b/src/handlers/articles/articles_post.cpp @@ -2,10 +2,10 @@ #include #include -#include "../../db/sql.hpp" -#include "../../models/article.hpp" -#include "../../utils/errors.hpp" -#include "../../utils/slugify.hpp" +#include "db/sql.hpp" +#include "models/article.hpp" +#include "utils/errors.hpp" +#include "utils/slugify.hpp" #include "validators/validators.hpp" namespace real_medium::handlers::articles::post { @@ -13,51 +13,55 @@ namespace real_medium::handlers::articles::post { 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 { - handlers::CreateArticleRequest createArticleRequest = - request_json["article"].As(); - try { - validator::validate(createArticleRequest); - } catch (const real_medium::utils::error::ValidationException& ex) { - // userver doesn't yet support 422 HTTP error code, so we handle the - // exception by ourselves. In general the exceptions are processed by the - // framework - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return ex.GetDetails(); - } - - const auto userId = context.GetData>("id"); - - std::string articleId; - try { - const auto slug = - real_medium::utils::slug::Slugify(createArticleRequest.title.value()); + userver::server::request::RequestContext& context +) const { + handlers::CreateArticleRequest createArticleRequest = request_json["article"].As(); + try { + validator::validate(createArticleRequest); + } catch (const real_medium::utils::error::ValidationException& ex) { + // userver doesn't yet support 422 HTTP error code, so we handle the + // exception by ourselves. In general the exceptions are processed by the + // framework + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return ex.GetDetails(); + } - const auto res = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kCreateArticle.data(), createArticleRequest.title, - slug, createArticleRequest.body, createArticleRequest.description, - userId, createArticleRequest.tags); - - articleId = res.AsSingleRow(); - } catch (const userver::storages::postgres::UniqueViolation& ex) { - const auto constraint = ex.GetServerMessage().GetConstraint(); - if (constraint == "uniq_slug") { - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return real_medium::utils::error::MakeError("slug", "already exists"); + const auto userId = context.GetData>("id"); + + std::string articleId; + try { + const auto slug = real_medium::utils::slug::Slugify(createArticleRequest.title.value()); + + const auto res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + real_medium::sql::kCreateArticle.data(), + createArticleRequest.title, + slug, + createArticleRequest.body, + createArticleRequest.description, + userId, + createArticleRequest.tags + ); + + articleId = res.AsSingleRow(); + } catch (const userver::storages::postgres::UniqueViolation& ex) { + const auto constraint = ex.GetServerMessage().GetConstraint(); + if (constraint == "uniq_slug") { + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return real_medium::utils::error::MakeError("slug", "already exists"); + } + throw; } - throw; - } - const auto res = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kGetArticleWithAuthorProfile.data(), articleId, userId); + const auto res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + real_medium::sql::kGetArticleWithAuthorProfile.data(), + articleId, + userId + ); - userver::formats::json::ValueBuilder builder; - builder["article"] = - res.AsSingleRow(); - return builder.ExtractValue(); + userver::formats::json::ValueBuilder builder; + builder["article"] = res.AsSingleRow(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::articles::post diff --git a/src/handlers/articles/articles_post.hpp b/src/handlers/articles/articles_post.hpp index 44d56f4..ac64749 100644 --- a/src/handlers/articles/articles_post.hpp +++ b/src/handlers/articles/articles_post.hpp @@ -7,15 +7,16 @@ namespace real_medium::handlers::articles::post { class Handler final : public Common { - public: - static constexpr std::string_view kName{"handler-create-article"}; +public: + static constexpr std::string_view kName{"handler-create-article"}; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& request_context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& request_context + ) const override; }; } // namespace real_medium::handlers::articles::post diff --git a/src/handlers/articles/articles_slug_delete.cpp b/src/handlers/articles/articles_slug_delete.cpp index a76c3bf..a9e01e2 100644 --- a/src/handlers/articles/articles_slug_delete.cpp +++ b/src/handlers/articles/articles_slug_delete.cpp @@ -1,34 +1,38 @@ #include "articles_slug_delete.hpp" -#include "../../db/sql.hpp" -#include "../../dto/article.hpp" -#include "../../models/article.hpp" -#include "../../utils/slugify.hpp" +#include "db/sql.hpp" +#include "dto/article.hpp" +#include "models/article.hpp" +#include "utils/slugify.hpp" namespace real_medium::handlers::articles_slug::del { userver::formats::json::Value Handler::HandleRequestJsonThrow( const userver::server::http::HttpRequest& request, const userver::formats::json::Value&, - userver::server::request::RequestContext& context) const { - const auto& slug = request.GetPathArg("slug"); - const auto userId = context.GetData>("id"); - auto res = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kGetArticleIdBySlug.data(), slug); - if (res.IsEmpty()) { - request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); - return {}; - } - res = GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kDeleteArticleBySlug.data(), slug, - userId); + userver::server::request::RequestContext& context +) const { + const auto& slug = request.GetPathArg("slug"); + const auto userId = context.GetData>("id"); + auto res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, real_medium::sql::kGetArticleIdBySlug.data(), slug + ); + if (res.IsEmpty()) { + request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); + return {}; + } + res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + real_medium::sql::kDeleteArticleBySlug.data(), + slug, + userId + ); - if (res.IsEmpty()) { - request.SetResponseStatus(userver::server::http::HttpStatus::kForbidden); - return {}; - } + if (res.IsEmpty()) { + request.SetResponseStatus(userver::server::http::HttpStatus::kForbidden); + return {}; + } - return {}; + return {}; } } // namespace real_medium::handlers::articles_slug::del diff --git a/src/handlers/articles/articles_slug_delete.hpp b/src/handlers/articles/articles_slug_delete.hpp index 4f27f9a..b0cefbd 100644 --- a/src/handlers/articles/articles_slug_delete.hpp +++ b/src/handlers/articles/articles_slug_delete.hpp @@ -3,23 +3,24 @@ #include #include -#include "userver/storages/postgres/cluster.hpp" -#include "userver/storages/postgres/component.hpp" +#include +#include #include "handlers/common.hpp" namespace real_medium::handlers::articles_slug::del { class Handler final : public Common { - public: - static constexpr std::string_view kName{"handler-delete-article"}; +public: + static constexpr std::string_view kName{"handler-delete-article"}; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& request_context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& request_context + ) const override; }; } // namespace real_medium::handlers::articles_slug::del diff --git a/src/handlers/articles/articles_slug_get.cpp b/src/handlers/articles/articles_slug_get.cpp index 19f9456..b7e9491 100644 --- a/src/handlers/articles/articles_slug_get.cpp +++ b/src/handlers/articles/articles_slug_get.cpp @@ -1,26 +1,26 @@ #include "articles_slug_get.hpp" -#include "../../db/sql.hpp" -#include "../../dto/article.hpp" -#include "../../models/article.hpp" +#include "db/sql.hpp" +#include "dto/article.hpp" +#include "models/article.hpp" namespace real_medium::handlers::articles_slug::get { 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 { - const auto& slug = request.GetPathArg("slug"); - const std::optional userId = - context.GetData>("id"); - auto data = GetArticlesCache().Get(); - auto article = data->findArticleBySlug(slug); - if (article == nullptr) { - request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); - return {}; - } - userver::formats::json::ValueBuilder builder; - builder["article"] = real_medium::dto::Article::Parse(*article, userId); - return builder.ExtractValue(); + userver::server::request::RequestContext& context +) const { + const auto& slug = request.GetPathArg("slug"); + const std::optional userId = context.GetData>("id"); + auto data = GetArticlesCache().Get(); + auto article = data->findArticleBySlug(slug); + if (article == nullptr) { + request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); + return {}; + } + userver::formats::json::ValueBuilder builder; + builder["article"] = real_medium::dto::Article::Parse(*article, userId); + return builder.ExtractValue(); } } // namespace real_medium::handlers::articles_slug::get diff --git a/src/handlers/articles/articles_slug_get.hpp b/src/handlers/articles/articles_slug_get.hpp index 8024831..b21b0fb 100644 --- a/src/handlers/articles/articles_slug_get.hpp +++ b/src/handlers/articles/articles_slug_get.hpp @@ -7,15 +7,16 @@ namespace real_medium::handlers::articles_slug::get { class Handler final : public Common { - public: - static constexpr std::string_view kName{"handler-get-article"}; +public: + static constexpr std::string_view kName{"handler-get-article"}; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::articles_slug::get diff --git a/src/handlers/articles/articles_slug_put.cpp b/src/handlers/articles/articles_slug_put.cpp index c101808..1d25d08 100644 --- a/src/handlers/articles/articles_slug_put.cpp +++ b/src/handlers/articles/articles_slug_put.cpp @@ -12,52 +12,56 @@ namespace real_medium::handlers::articles_slug::put { 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 { - auto slug = request.GetPathArg("slug"); - handlers::UpdateArticleRequest updateRequest = - request_json["article"].As(); - try { - validator::validate(updateRequest); - } catch (const real_medium::utils::error::ValidationException& ex) { - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return ex.GetDetails(); - } - auto userId = context.GetData>("id"); - - std::string articleId; - try { - const auto newSlug = - updateRequest.title - ? std::make_optional( - real_medium::utils::slug::Slugify(*updateRequest.title)) - : std::nullopt; - const auto res = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kUpdateArticleBySlug.data(), slug, - userId, updateRequest.title, newSlug, - updateRequest.description, updateRequest.body); - if (res.IsEmpty()) { - request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); - return {}; + userver::server::request::RequestContext& context +) const { + auto slug = request.GetPathArg("slug"); + handlers::UpdateArticleRequest updateRequest = request_json["article"].As(); + try { + validator::validate(updateRequest); + } catch (const real_medium::utils::error::ValidationException& ex) { + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return ex.GetDetails(); } - articleId = res.AsSingleRow(); - } catch (const userver::storages::postgres::UniqueViolation& ex) { - const auto constraint = ex.GetServerMessage().GetConstraint(); - if (constraint == "uniq_slug") { - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return real_medium::utils::error::MakeError("slug", "already exists"); + auto userId = context.GetData>("id"); + + std::string articleId; + try { + const auto newSlug = + updateRequest.title + ? std::make_optional(real_medium::utils::slug::Slugify(*updateRequest.title)) + : std::nullopt; + const auto res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + real_medium::sql::kUpdateArticleBySlug.data(), + slug, + userId, + updateRequest.title, + newSlug, + updateRequest.description, + updateRequest.body + ); + if (res.IsEmpty()) { + request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); + return {}; + } + articleId = res.AsSingleRow(); + } catch (const userver::storages::postgres::UniqueViolation& ex) { + const auto constraint = ex.GetServerMessage().GetConstraint(); + if (constraint == "uniq_slug") { + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return real_medium::utils::error::MakeError("slug", "already exists"); + } + throw; } - throw; - } - const auto res = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kGetArticleWithAuthorProfile.data(), articleId, userId); + const auto res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + real_medium::sql::kGetArticleWithAuthorProfile.data(), + articleId, + userId + ); - userver::formats::json::ValueBuilder builder; - builder["article"] = - res.AsSingleRow(); - return builder.ExtractValue(); + userver::formats::json::ValueBuilder builder; + builder["article"] = res.AsSingleRow(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::articles_slug::put diff --git a/src/handlers/articles/articles_slug_put.hpp b/src/handlers/articles/articles_slug_put.hpp index c2676d1..689cb71 100644 --- a/src/handlers/articles/articles_slug_put.hpp +++ b/src/handlers/articles/articles_slug_put.hpp @@ -7,16 +7,16 @@ namespace real_medium::handlers::articles_slug::put { class Handler final : public Common { - public: - static constexpr std::string_view kName{"handler-update-article"}; +public: + static constexpr std::string_view kName{"handler-update-article"}; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& request_context) - const override final; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& request_context + ) const override final; }; } // namespace real_medium::handlers::articles_slug::put diff --git a/src/handlers/articles/articles_unfavorite.cpp b/src/handlers/articles/articles_unfavorite.cpp index e7433a1..a67547d 100644 --- a/src/handlers/articles/articles_unfavorite.cpp +++ b/src/handlers/articles/articles_unfavorite.cpp @@ -8,40 +8,41 @@ namespace real_medium::handlers::articles_favorite::del { 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"); - auto& slug = request.GetPathArg("slug"); - - auto transaction = - GetPg().Begin("unfavorite_article_transaction", - userver::storages::postgres::ClusterHostType::kMaster, - userver::storages::postgres::Transaction::RW); - - auto res = - transaction.Execute(sql::kDeleteFavoritePair.data(), user_id, slug); - - if (!res.IsEmpty()) { - auto article_id = res.AsSingleRow(); - - transaction.Execute(sql::kDecrementFavoritesCount.data(), article_id); - transaction.Commit(); - } - - const auto get_article_res = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kSlave, - real_medium::sql::kGetArticleWithAuthorProfileBySlug.data(), slug, - user_id); - - if (get_article_res.IsEmpty()) { - request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); - return {}; - } - - userver::formats::json::ValueBuilder builder; - builder["article"] = - get_article_res - .AsSingleRow(); - return builder.ExtractValue(); + userver::server::request::RequestContext& context +) const { + auto& user_id = context.GetData>("id"); + auto& slug = request.GetPathArg("slug"); + + auto transaction = GetPg().Begin( + "unfavorite_article_transaction", + userver::storages::postgres::ClusterHostType::kMaster, + userver::storages::postgres::Transaction::RW + ); + + auto res = transaction.Execute(sql::kDeleteFavoritePair.data(), user_id, slug); + + if (!res.IsEmpty()) { + auto article_id = res.AsSingleRow(); + + transaction.Execute(sql::kDecrementFavoritesCount.data(), article_id); + transaction.Commit(); + } + + const auto get_article_res = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kSlave, + real_medium::sql::kGetArticleWithAuthorProfileBySlug.data(), + slug, + user_id + ); + + if (get_article_res.IsEmpty()) { + request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); + return {}; + } + + userver::formats::json::ValueBuilder builder; + builder["article"] = get_article_res.AsSingleRow(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::articles_favorite::del diff --git a/src/handlers/articles/articles_unfavorite.hpp b/src/handlers/articles/articles_unfavorite.hpp index 000add7..aac0aca 100644 --- a/src/handlers/articles/articles_unfavorite.hpp +++ b/src/handlers/articles/articles_unfavorite.hpp @@ -1,22 +1,23 @@ #pragma once -#include "userver/formats/json/serialize_container.hpp" -#include "userver/storages/postgres/cluster.hpp" +#include +#include #include "handlers/common.hpp" namespace real_medium::handlers::articles_favorite::del { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-articles-favorite-delete"; +public: + static constexpr std::string_view kName = "handler-articles-favorite-delete"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::articles_favorite::del diff --git a/src/handlers/articles/feed_articles.cpp b/src/handlers/articles/feed_articles.cpp index ca60f8e..f1f0525 100644 --- a/src/handlers/articles/feed_articles.cpp +++ b/src/handlers/articles/feed_articles.cpp @@ -13,26 +13,26 @@ namespace real_medium::handlers::articles::feed::get { 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 { - handlers::FeedArticleFilterDTO filter; + userver::server::request::RequestContext& context +) const { + handlers::FeedArticleFilterDTO filter; - try { - filter = dto::Parse(request); - } catch (std::bad_cast& ex) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kUnprocessableEntity); - return utils::error::MakeError("filters", "invalid filters entered"); - } + try { + filter = dto::Parse(request); + } catch (std::bad_cast& ex) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return utils::error::MakeError("filters", "invalid filters entered"); + } - auto user_id = context.GetData>("id"); - auto data = GetArticlesCache().Get(); - auto articles = data->getFeed(filter, user_id.value()); - userver::formats::json::ValueBuilder builder; - builder["articles"] = userver::formats::common::Type::kArray; - for (auto& article : articles) - builder["articles"].PushBack(dto::Article::Parse(*article, user_id)); - builder["articlesCount"] = articles.size(); - return builder.ExtractValue(); + auto user_id = context.GetData>("id"); + auto data = GetArticlesCache().Get(); + auto articles = data->getFeed(filter, user_id.value()); + userver::formats::json::ValueBuilder builder; + builder["articles"] = userver::formats::common::Type::kArray; + for (auto& article : articles) builder["articles"].PushBack(dto::Article::Parse(*article, user_id)); + builder["articlesCount"] = articles.size(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::articles::feed::get diff --git a/src/handlers/articles/feed_articles.hpp b/src/handlers/articles/feed_articles.hpp index 77e9e64..22c2b49 100644 --- a/src/handlers/articles/feed_articles.hpp +++ b/src/handlers/articles/feed_articles.hpp @@ -7,13 +7,14 @@ namespace real_medium::handlers::articles::feed::get { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-feed-articles"; - using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; +public: + static constexpr std::string_view kName = "handler-feed-articles"; + using Common::Common; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::articles::feed::get diff --git a/src/handlers/auth/auth_bearer.cpp b/src/handlers/auth/auth_bearer.cpp index 75746bc..682a30e 100644 --- a/src/handlers/auth/auth_bearer.cpp +++ b/src/handlers/auth/auth_bearer.cpp @@ -10,93 +10,89 @@ namespace real_medium::auth { -class AuthCheckerBearer final - : public userver::server::handlers::auth::AuthCheckerBase { - public: - using AuthCheckResult = userver::server::handlers::auth::AuthCheckResult; +class AuthCheckerBearer final : public userver::server::handlers::auth::AuthCheckerBase { +public: + using AuthCheckResult = userver::server::handlers::auth::AuthCheckResult; - AuthCheckerBearer(userver::storages::postgres::ClusterPtr pg_cluster, - bool is_required) - : pg_cluster_(std::move(pg_cluster)), is_required_(is_required) {} + AuthCheckerBearer(userver::storages::postgres::ClusterPtr pg_cluster, bool is_required) + : pg_cluster_(std::move(pg_cluster)), is_required_(is_required) {} - [[nodiscard]] AuthCheckResult CheckAuth( - const userver::server::http::HttpRequest& request, - userver::server::request::RequestContext& request_context) const override; + [[nodiscard]] AuthCheckResult CheckAuth( + const userver::server::http::HttpRequest& request, + userver::server::request::RequestContext& request_context + ) const override; - [[nodiscard]] bool SupportsUserAuth() const noexcept override { return true; } + [[nodiscard]] bool SupportsUserAuth() const noexcept override { return true; } - private: - userver::storages::postgres::ClusterPtr pg_cluster_; - const bool is_required_; +private: + userver::storages::postgres::ClusterPtr pg_cluster_; + const bool is_required_; }; AuthCheckerBearer::AuthCheckResult AuthCheckerBearer::CheckAuth( const userver::server::http::HttpRequest& request, - userver::server::request::RequestContext& request_context) const { - const auto& auth_value = - request.GetHeader(userver::http::headers::kAuthorization); - if (auth_value.empty()) { - if (!is_required_) { - request_context.SetData("id", std::optional(std::nullopt)); - return {}; + userver::server::request::RequestContext& request_context +) const { + const auto& auth_value = request.GetHeader(userver::http::headers::kAuthorization); + if (auth_value.empty()) { + if (!is_required_) { + request_context.SetData("id", std::optional(std::nullopt)); + return {}; + } + return AuthCheckResult{ + AuthCheckResult::Status::kTokenNotFound, + {}, + "Empty 'Authorization' header", + userver::server::handlers::HandlerErrorCode::kUnauthorized + }; } - return AuthCheckResult{ - AuthCheckResult::Status::kTokenNotFound, - {}, - "Empty 'Authorization' header", - userver::server::handlers::HandlerErrorCode::kUnauthorized}; - } - const auto bearer_sep_pos = auth_value.find(' '); - if (bearer_sep_pos == std::string::npos || - std::string_view{auth_value.data(), bearer_sep_pos} != "Token") { - return AuthCheckResult{ - AuthCheckResult::Status::kTokenNotFound, - {}, - "'Authorization' header should have 'Bearer some-token' format", - userver::server::handlers::HandlerErrorCode::kUnauthorized}; - } - std::string_view token{auth_value.data() + bearer_sep_pos + 1}; - jwt::jwt_payload payload; - try { - payload = utils::jwt::DecodeJWT(token); - } catch (...) { - return AuthCheckResult{ - AuthCheckResult::Status::kTokenNotFound, - {}, - "Token verification error", - userver::server::handlers::HandlerErrorCode::kUnauthorized}; - } - auto id = payload.get_claim_value("id"); + const auto bearer_sep_pos = auth_value.find(' '); + if (bearer_sep_pos == std::string::npos || std::string_view{auth_value.data(), bearer_sep_pos} != "Token") { + return AuthCheckResult{ + AuthCheckResult::Status::kTokenNotFound, + {}, + "'Authorization' header should have 'Bearer some-token' format", + userver::server::handlers::HandlerErrorCode::kUnauthorized + }; + } + std::string_view token{auth_value.data() + bearer_sep_pos + 1}; + jwt::jwt_payload payload; + try { + payload = utils::jwt::DecodeJWT(token); + } catch (...) { + return AuthCheckResult{ + AuthCheckResult::Status::kTokenNotFound, + {}, + "Token verification error", + userver::server::handlers::HandlerErrorCode::kUnauthorized + }; + } + auto id = payload.get_claim_value("id"); - const auto res = - pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, - sql::kFindUserById.data(), id); - if (res.IsEmpty()) { - return AuthCheckResult{ - AuthCheckResult::Status::kTokenNotFound, - {}, - "Invalid user auth", - userver::server::handlers::HandlerErrorCode::kUnauthorized}; - } + const auto res = + pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, sql::kFindUserById.data(), id); + if (res.IsEmpty()) { + return AuthCheckResult{ + AuthCheckResult::Status::kTokenNotFound, + {}, + "Invalid user auth", + userver::server::handlers::HandlerErrorCode::kUnauthorized + }; + } - request_context.SetData("id", std::optional(id)); - return {}; + request_context.SetData("id", std::optional(id)); + return {}; } -CheckerFactory::CheckerFactory( - const userver::components::ComponentContext& context) - : pg_cluster_(context - .FindComponent( - "realmedium-database") - .GetCluster()) {} +CheckerFactory::CheckerFactory(const userver::components::ComponentContext& context) + : pg_cluster_(context.FindComponent("realmedium-database").GetCluster()) {} -userver::server::handlers::auth::AuthCheckerBasePtr -CheckerFactory::MakeAuthChecker( - const userver::server::handlers::auth::HandlerAuthConfig& auth_config) - const { - auto is_required = auth_config["required"].As(false); - return std::make_shared(pg_cluster_, is_required); +userver::server::handlers::auth::AuthCheckerBasePtr CheckerFactory::MakeAuthChecker( + const userver::server::handlers::auth::HandlerAuthConfig& auth_config +) const { + auto is_required = auth_config["required"].As(false); + return std::make_shared(pg_cluster_, is_required); } } // namespace real_medium::auth diff --git a/src/handlers/auth/auth_bearer.hpp b/src/handlers/auth/auth_bearer.hpp index 6e63946..c983e8a 100644 --- a/src/handlers/auth/auth_bearer.hpp +++ b/src/handlers/auth/auth_bearer.hpp @@ -5,18 +5,17 @@ namespace real_medium::auth { -class CheckerFactory final - : public userver::server::handlers::auth::AuthCheckerFactoryBase { - public: - static constexpr std::string_view kAuthType = "bearer"; +class CheckerFactory final : public userver::server::handlers::auth::AuthCheckerFactoryBase { +public: + static constexpr std::string_view kAuthType = "bearer"; - explicit CheckerFactory(const userver::components::ComponentContext& context); + explicit CheckerFactory(const userver::components::ComponentContext& context); - userver::server::handlers::auth::AuthCheckerBasePtr MakeAuthChecker( - const userver::server::handlers::auth::HandlerAuthConfig&) const override; + userver::server::handlers::auth::AuthCheckerBasePtr + MakeAuthChecker(const userver::server::handlers::auth::HandlerAuthConfig&) const override; - private: - userver::storages::postgres::ClusterPtr pg_cluster_; +private: + userver::storages::postgres::ClusterPtr pg_cluster_; }; } // namespace real_medium::auth diff --git a/src/handlers/comments/comment_delete.cpp b/src/handlers/comments/comment_delete.cpp index eab266e..4070851 100644 --- a/src/handlers/comments/comment_delete.cpp +++ b/src/handlers/comments/comment_delete.cpp @@ -10,34 +10,33 @@ namespace real_medium::handlers::comments::del { 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 { - auto user_id = context.GetData>("id"); - const auto& comment_id = - userver::utils::FromString(request.GetPathArg("id")); - const auto& slug = request.GetPathArg("slug"); - - const auto result_find_comment = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - sql::kFindCommentByIdAndSlug.data(), comment_id, slug); - - if (result_find_comment.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return utils::error::MakeError("comment_id", "Invalid comment_id."); - } - - const auto result_delete_comment = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - sql::kDeleteCommentById.data(), comment_id, user_id); - - if (result_delete_comment.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kForbidden); - return utils::error::MakeError("user_id", - "This user does not own this comment."); - } - - return {}; + userver::server::request::RequestContext& context +) const { + auto user_id = context.GetData>("id"); + const auto& comment_id = userver::utils::FromString(request.GetPathArg("id")); + const auto& slug = request.GetPathArg("slug"); + + const auto result_find_comment = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, sql::kFindCommentByIdAndSlug.data(), comment_id, slug + ); + + if (result_find_comment.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return utils::error::MakeError("comment_id", "Invalid comment_id."); + } + + const auto result_delete_comment = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, sql::kDeleteCommentById.data(), comment_id, user_id + ); + + if (result_delete_comment.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kForbidden); + return utils::error::MakeError("user_id", "This user does not own this comment."); + } + + return {}; } } // namespace real_medium::handlers::comments::del diff --git a/src/handlers/comments/comment_delete.hpp b/src/handlers/comments/comment_delete.hpp index d7e57dc..05abb9a 100644 --- a/src/handlers/comments/comment_delete.hpp +++ b/src/handlers/comments/comment_delete.hpp @@ -5,15 +5,16 @@ namespace real_medium::handlers::comments::del { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-comment-delete"; +public: + static constexpr std::string_view kName = "handler-comment-delete"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::comments::del diff --git a/src/handlers/comments/comment_post.cpp b/src/handlers/comments/comment_post.cpp index 3a4e4a3..771eaa4 100644 --- a/src/handlers/comments/comment_post.cpp +++ b/src/handlers/comments/comment_post.cpp @@ -14,56 +14,55 @@ namespace real_medium::handlers::comments::post { 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 { - auto user_id = context.GetData>("id"); - const auto comment_json = - userver::formats::json::FromString(request.RequestBody())["comment"] - .As(); + userver::server::request::RequestContext& context +) const { + auto user_id = context.GetData>("id"); + const auto comment_json = + userver::formats::json::FromString(request.RequestBody())["comment"].As(); - try { - validator::validate(comment_json); - } catch (const utils::error::ValidationException& err) { - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return err.GetDetails(); - } + try { + validator::validate(comment_json); + } catch (const utils::error::ValidationException& err) { + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return err.GetDetails(); + } - const auto& comment_body = comment_json.body; - const auto& slug = request.GetPathArg("slug"); + const auto& comment_body = comment_json.body; + const auto& slug = request.GetPathArg("slug"); - const auto res_find_article = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - sql::kFindIdArticleBySlug.data(), slug); + const auto res_find_article = + GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, sql::kFindIdArticleBySlug.data(), slug); - if (res_find_article.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return utils::error::MakeError("article_id", "Invalid article_id."); - } + if (res_find_article.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return utils::error::MakeError("article_id", "Invalid article_id."); + } - const auto article_id = res_find_article.AsSingleRow(); + const auto article_id = res_find_article.AsSingleRow(); - const auto res_ins_new_comment = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - sql::kAddComment.data(), comment_body, user_id, article_id); + const auto res_ins_new_comment = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + sql::kAddComment.data(), + comment_body, + user_id, + article_id + ); - if (res_ins_new_comment.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus( - userver::server::http::HttpStatus::kNotImplemented); // 501, мб надо - // заменить - return utils::error::MakeError( - "none", "Unknown error. The comment was not added to the database."); - } + if (res_ins_new_comment.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotImplemented); // 501, мб надо + // заменить + return utils::error::MakeError("none", "Unknown error. The comment was not added to the database."); + } - auto comment_res_data = - res_ins_new_comment.AsSingleRow( - userver::storages::postgres::kRowTag); + auto comment_res_data = + res_ins_new_comment.AsSingleRow(userver::storages::postgres::kRowTag); - userver::formats::json::ValueBuilder builder; - builder["comment"] = comment_res_data; + userver::formats::json::ValueBuilder builder; + builder["comment"] = comment_res_data; - return builder.ExtractValue(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::comments::post diff --git a/src/handlers/comments/comment_post.hpp b/src/handlers/comments/comment_post.hpp index 27d6c0a..e0bc3c0 100644 --- a/src/handlers/comments/comment_post.hpp +++ b/src/handlers/comments/comment_post.hpp @@ -10,15 +10,16 @@ namespace real_medium::handlers::comments::post { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-comment-post"; +public: + static constexpr std::string_view kName = "handler-comment-post"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::comments::post diff --git a/src/handlers/comments/comments_get.cpp b/src/handlers/comments/comments_get.cpp index 4d416c2..e5758e1 100644 --- a/src/handlers/comments/comments_get.cpp +++ b/src/handlers/comments/comments_get.cpp @@ -11,35 +11,35 @@ namespace real_medium::handlers::comments::get { -Handler::Handler(const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& component_context) +Handler::Handler( + const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& component_context +) : Common(config, component_context), - comments_cache_(component_context.FindComponent< - real_medium::cache::comments_cache::CommentsCache>()) {} + comments_cache_(component_context.FindComponent()) {} 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 { - auto user_id = context.GetData>("id"); - const auto& slug = request.GetPathArg("slug"); - const auto articles_data = GetArticlesCache().Get(); - userver::formats::json::ValueBuilder result = - userver::formats::json::MakeObject(); - if (!articles_data->findArticleBySlug(slug)) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return utils::error::MakeError("slug", "Invalid slug"); - } - const auto comments_data = comments_cache_.Get(); - const auto res_find_comments = comments_data->findComments(slug); - - userver::formats::json::ValueBuilder builder; - builder["comments"] = userver::formats::common::Type::kArray; - for (auto& comment : res_find_comments) - builder["comments"].PushBack(dto::Comment::Parse(*comment.second, user_id)); - - return builder.ExtractValue(); + userver::server::request::RequestContext& context +) const { + auto user_id = context.GetData>("id"); + const auto& slug = request.GetPathArg("slug"); + const auto articles_data = GetArticlesCache().Get(); + userver::formats::json::ValueBuilder result = userver::formats::json::MakeObject(); + if (!articles_data->findArticleBySlug(slug)) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return utils::error::MakeError("slug", "Invalid slug"); + } + const auto comments_data = comments_cache_.Get(); + const auto res_find_comments = comments_data->findComments(slug); + + userver::formats::json::ValueBuilder builder; + builder["comments"] = userver::formats::common::Type::kArray; + for (auto& comment : res_find_comments) builder["comments"].PushBack(dto::Comment::Parse(*comment.second, user_id)); + + return builder.ExtractValue(); } } // namespace real_medium::handlers::comments::get diff --git a/src/handlers/comments/comments_get.hpp b/src/handlers/comments/comments_get.hpp index 2ba39a3..27bad3b 100644 --- a/src/handlers/comments/comments_get.hpp +++ b/src/handlers/comments/comments_get.hpp @@ -6,19 +6,22 @@ namespace real_medium::handlers::comments::get { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-comments-get"; +public: + static constexpr std::string_view kName = "handler-comments-get"; - Handler(const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& component_context); + Handler( + const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& component_context + ); - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; - private: - const real_medium::cache::comments_cache::CommentsCache& comments_cache_; +private: + const real_medium::cache::comments_cache::CommentsCache& comments_cache_; }; } // namespace real_medium::handlers::comments::get diff --git a/src/handlers/common.cpp b/src/handlers/common.cpp index bf94239..95d72fd 100644 --- a/src/handlers/common.cpp +++ b/src/handlers/common.cpp @@ -6,14 +6,12 @@ namespace real_medium::handlers { -Common::Common(const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& component_context) +Common::Common( + const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& component_context +) : HttpHandlerJsonBase(config, component_context), - pg_cluster_(component_context - .FindComponent( - "realmedium-database") - .GetCluster()), - articles_cache_(component_context.FindComponent< - real_medium::cache::articles_cache::ArticlesCache>()) {} + pg_cluster_(component_context.FindComponent("realmedium-database").GetCluster()), + articles_cache_(component_context.FindComponent()) {} } // namespace real_medium::handlers diff --git a/src/handlers/common.hpp b/src/handlers/common.hpp index 1fa1a78..9ea6722 100644 --- a/src/handlers/common.hpp +++ b/src/handlers/common.hpp @@ -13,23 +13,24 @@ namespace real_medium::handlers { class Common : public userver::server::handlers::HttpHandlerJsonBase { - public: - Common(const userver::components::ComponentConfig& config, - const userver::components::ComponentContext& component_context); - - const real_medium::cache::articles_cache::ArticlesCache& GetArticlesCache() - const noexcept { - return articles_cache_; - } - - userver::storages::postgres::Cluster& GetPg() const noexcept { - UASSERT(pg_cluster_); - return *pg_cluster_; - } - - private: - userver::storages::postgres::ClusterPtr pg_cluster_; - const real_medium::cache::articles_cache::ArticlesCache& articles_cache_; +public: + Common( + const userver::components::ComponentConfig& config, + const userver::components::ComponentContext& component_context + ); + + const real_medium::cache::articles_cache::ArticlesCache& GetArticlesCache() const noexcept { + return articles_cache_; + } + + userver::storages::postgres::Cluster& GetPg() const noexcept { + UASSERT(pg_cluster_); + return *pg_cluster_; + } + +private: + userver::storages::postgres::ClusterPtr pg_cluster_; + const real_medium::cache::articles_cache::ArticlesCache& articles_cache_; }; } // namespace real_medium::handlers diff --git a/src/handlers/profiles/profiles.cpp b/src/handlers/profiles/profiles.cpp index 758c1a9..9e8c774 100644 --- a/src/handlers/profiles/profiles.cpp +++ b/src/handlers/profiles/profiles.cpp @@ -5,10 +5,10 @@ #include "models/profile.hpp" #include "utils/make_error.hpp" -#include "userver/formats/yaml/value_builder.hpp" -#include "userver/server/handlers/http_handler_base.hpp" -#include "userver/storages/postgres/cluster.hpp" -#include "userver/storages/postgres/component.hpp" +#include +#include +#include +#include using namespace userver::formats; using namespace userver::server::http; @@ -17,28 +17,23 @@ using namespace userver::storages::postgres; namespace real_medium::handlers::profiles::get { -json::Value Handler::HandleRequestJsonThrow(const HttpRequest& request, - const json::Value&, - RequestContext& context) const { - auto user_id = context.GetData>("id"); - const auto& username = request.GetPathArg("username"); - auto res = - GetPg().Execute(ClusterHostType::kMaster, - sql::kGetProfileByUsername.data(), username, user_id); - if (res.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return utils::error::MakeError("username", - "There is no user with this nickname."); - } - - auto profile = - res.AsSingleRow(userver::storages::postgres::kRowTag); - - userver::formats::json::ValueBuilder builder; - builder["profile"] = profile; - - return builder.ExtractValue(); +json::Value Handler::HandleRequestJsonThrow(const HttpRequest& request, const json::Value&, RequestContext& context) + const { + auto user_id = context.GetData>("id"); + const auto& username = request.GetPathArg("username"); + auto res = GetPg().Execute(ClusterHostType::kMaster, sql::kGetProfileByUsername.data(), username, user_id); + if (res.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return utils::error::MakeError("username", "There is no user with this nickname."); + } + + auto profile = res.AsSingleRow(userver::storages::postgres::kRowTag); + + userver::formats::json::ValueBuilder builder; + builder["profile"] = profile; + + return builder.ExtractValue(); } } // namespace real_medium::handlers::profiles::get diff --git a/src/handlers/profiles/profiles.hpp b/src/handlers/profiles/profiles.hpp index 49f3de5..9d556b5 100644 --- a/src/handlers/profiles/profiles.hpp +++ b/src/handlers/profiles/profiles.hpp @@ -5,15 +5,16 @@ namespace real_medium::handlers::profiles::get { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-get-profile"; +public: + static constexpr std::string_view kName = "handler-get-profile"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::profiles::get diff --git a/src/handlers/profiles/profiles_follow.cpp b/src/handlers/profiles/profiles_follow.cpp index 45c5ba8..c18d89b 100644 --- a/src/handlers/profiles/profiles_follow.cpp +++ b/src/handlers/profiles/profiles_follow.cpp @@ -5,10 +5,10 @@ #include "profiles_follow.hpp" #include "utils/make_error.hpp" -#include "userver/formats/yaml/value_builder.hpp" -#include "userver/server/handlers/http_handler_base.hpp" -#include "userver/storages/postgres/cluster.hpp" -#include "userver/storages/postgres/component.hpp" +#include +#include +#include +#include using namespace userver::formats; using namespace userver::server::http; @@ -20,50 +20,48 @@ namespace real_medium::handlers::profiles::post { 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::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 = - GetPg().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 = GetPg().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 = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kSlave, - sql::KFollowingUser.data(), username_id, user_id); + const auto res_following = GetPg().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.following) { - 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(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::profiles::post diff --git a/src/handlers/profiles/profiles_follow.hpp b/src/handlers/profiles/profiles_follow.hpp index 2dbd614..551a82e 100644 --- a/src/handlers/profiles/profiles_follow.hpp +++ b/src/handlers/profiles/profiles_follow.hpp @@ -5,15 +5,16 @@ namespace real_medium::handlers::profiles::post { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-profile-follow"; +public: + static constexpr std::string_view kName = "handler-profile-follow"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::profiles::post diff --git a/src/handlers/profiles/profiles_follow_delete.cpp b/src/handlers/profiles/profiles_follow_delete.cpp index 75d9a2c..2f87557 100644 --- a/src/handlers/profiles/profiles_follow_delete.cpp +++ b/src/handlers/profiles/profiles_follow_delete.cpp @@ -1,12 +1,12 @@ #include +#include +#include +#include +#include #include "db/sql.hpp" #include "models/profile.hpp" #include "profiles_follow_delete.hpp" -#include "userver/formats/yaml/value_builder.hpp" -#include "userver/server/handlers/http_handler_base.hpp" -#include "userver/storages/postgres/cluster.hpp" -#include "userver/storages/postgres/component.hpp" #include "utils/make_error.hpp" using namespace userver::formats; @@ -19,50 +19,48 @@ namespace real_medium::handlers::profiles::del { 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::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 = - GetPg().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 = GetPg().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(); - 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."); - } + 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."); + } - const auto res_unfollowing = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kSlave, - sql::KUnFollowingUser.data(), username_id, user_id); + const auto res_unfollowing = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kSlave, sql::KUnFollowingUser.data(), username_id, user_id + ); - const auto profile = res_unfollowing.AsSingleRow( - userver::storages::postgres::kRowTag); + const auto profile = res_unfollowing.AsSingleRow(userver::storages::postgres::kRowTag); - if (profile.following) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kBadRequest); - return utils::error::MakeError("user_id", "has already unfollowed"); - } + if (profile.following) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kBadRequest); + return utils::error::MakeError("user_id", "has already unfollowed"); + } - userver::formats::json::ValueBuilder builder; - builder["profile"] = profile; + userver::formats::json::ValueBuilder builder; + builder["profile"] = profile; - return builder.ExtractValue(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::profiles::del diff --git a/src/handlers/profiles/profiles_follow_delete.hpp b/src/handlers/profiles/profiles_follow_delete.hpp index 3536b78..d15f702 100644 --- a/src/handlers/profiles/profiles_follow_delete.hpp +++ b/src/handlers/profiles/profiles_follow_delete.hpp @@ -5,15 +5,16 @@ namespace real_medium::handlers::profiles::del { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-profile-unfollow"; +public: + static constexpr std::string_view kName = "handler-profile-unfollow"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value&, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value&, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::profiles::del diff --git a/src/handlers/tags/tags.cpp b/src/handlers/tags/tags.cpp index 3242680..8ee377e 100644 --- a/src/handlers/tags/tags.cpp +++ b/src/handlers/tags/tags.cpp @@ -1,26 +1,24 @@ #include "tags.hpp" -#include "userver/components/component.hpp" -#include "userver/formats/json/serialize_container.hpp" -#include "userver/server/handlers/http_handler_json_base.hpp" -#include "userver/storages/postgres/cluster.hpp" -#include "userver/storages/postgres/component.hpp" +#include +#include +#include +#include +#include namespace real_medium::handlers::tags::get { -userver::formats::json::Value Handler::HandleRequestJsonThrow( - const userver::server::http::HttpRequest&, - const userver::formats::json::Value&, - userver::server::request::RequestContext&) const { - constexpr static auto query = "SELECT tag_name FROM real_medium.tag_list"; - auto result = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kSlave, query); - auto tags = result.AsSetOf(); +userver::formats::json::Value Handler:: + HandleRequestJsonThrow(const userver::server::http::HttpRequest&, const userver::formats::json::Value&, userver::server::request::RequestContext&) + const { + constexpr static auto query = "SELECT tag_name FROM real_medium.tag_list"; + auto result = GetPg().Execute(userver::storages::postgres::ClusterHostType::kSlave, query); + auto tags = result.AsSetOf(); - userver::formats::json::ValueBuilder response; - response["tags"] = tags; + userver::formats::json::ValueBuilder response; + response["tags"] = tags; - return response.ExtractValue(); + return response.ExtractValue(); } } // namespace real_medium::handlers::tags::get diff --git a/src/handlers/tags/tags.hpp b/src/handlers/tags/tags.hpp index b467983..d63ed03 100644 --- a/src/handlers/tags/tags.hpp +++ b/src/handlers/tags/tags.hpp @@ -5,15 +5,14 @@ namespace real_medium::handlers::tags::get { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-get-tags"; +public: + static constexpr std::string_view kName = "handler-get-tags"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest&, - const userver::formats::json::Value&, - userver::server::request::RequestContext&) const override; + userver::formats::json::Value + HandleRequestJsonThrow(const userver::server::http::HttpRequest&, const userver::formats::json::Value&, userver::server::request::RequestContext&) + const override; }; } // namespace real_medium::handlers::tags::get diff --git a/src/handlers/users/user_get.cpp b/src/handlers/users/user_get.cpp index 30ec3ba..e327b80 100644 --- a/src/handlers/users/user_get.cpp +++ b/src/handlers/users/user_get.cpp @@ -8,26 +8,25 @@ namespace real_medium::handlers::users::get { 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 { - auto user_id = context.GetData>("id"); + userver::server::request::RequestContext& context +) const { + auto user_id = context.GetData>("id"); - const auto result = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - sql::kFindUserById.data(), user_id); + const auto result = + GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, sql::kFindUserById.data(), user_id); - if (result.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return utils::error::MakeError("user_id", "Invalid user_id. Not found."); - } + if (result.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return utils::error::MakeError("user_id", "Invalid user_id. Not found."); + } - auto user = result.AsSingleRow( - userver::storages::postgres::kRowTag); + auto user = result.AsSingleRow(userver::storages::postgres::kRowTag); - userver::formats::json::ValueBuilder builder; - builder["user"] = user; + userver::formats::json::ValueBuilder builder; + builder["user"] = user; - return builder.ExtractValue(); + return builder.ExtractValue(); } } // namespace real_medium::handlers::users::get diff --git a/src/handlers/users/user_get.hpp b/src/handlers/users/user_get.hpp index 3259a67..da5530e 100644 --- a/src/handlers/users/user_get.hpp +++ b/src/handlers/users/user_get.hpp @@ -7,15 +7,16 @@ namespace real_medium::handlers::users::get { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-user-get"; +public: + static constexpr std::string_view kName = "handler-user-get"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::users::get diff --git a/src/handlers/users/user_put.cpp b/src/handlers/users/user_put.cpp index d9b5915..bdd6d5b 100644 --- a/src/handlers/users/user_put.cpp +++ b/src/handlers/users/user_put.cpp @@ -12,41 +12,44 @@ namespace real_medium::handlers::users::put { 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 { - auto user_id = context.GetData>("id"); - - handlers::UserUpdateDTO user_change_data = - request_json["user"].As(); - - try { - validator::validate(user_change_data); - } catch (const utils::error::ValidationException& err) { - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return err.GetDetails(); - } - - std::optional password_hash = std::nullopt; - std::optional salt = std::nullopt; - if (user_change_data.password) { - salt = utils::random::GenerateSalt(); - password_hash = userver::crypto::hash::Sha256( - user_change_data.password.value() + salt.value()); - } - - const auto result = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - sql::kUpdateUser.data(), user_id, user_change_data.username, - user_change_data.email, user_change_data.bio, user_change_data.image, - password_hash, salt); - - auto user_result_data = result.AsSingleRow( - userver::storages::postgres::kRowTag); - - userver::formats::json::ValueBuilder builder; - builder["user"] = user_result_data; - - return builder.ExtractValue(); + userver::server::request::RequestContext& context +) const { + auto user_id = context.GetData>("id"); + + handlers::UserUpdateDTO user_change_data = request_json["user"].As(); + + try { + validator::validate(user_change_data); + } catch (const utils::error::ValidationException& err) { + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return err.GetDetails(); + } + + std::optional password_hash = std::nullopt; + std::optional salt = std::nullopt; + if (user_change_data.password) { + salt = utils::random::GenerateSalt(); + password_hash = userver::crypto::hash::Sha256(user_change_data.password.value() + salt.value()); + } + + const auto result = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + sql::kUpdateUser.data(), + user_id, + user_change_data.username, + user_change_data.email, + user_change_data.bio, + user_change_data.image, + password_hash, + salt + ); + + auto user_result_data = result.AsSingleRow(userver::storages::postgres::kRowTag); + + userver::formats::json::ValueBuilder builder; + builder["user"] = user_result_data; + + return builder.ExtractValue(); } } // namespace real_medium::handlers::users::put diff --git a/src/handlers/users/user_put.hpp b/src/handlers/users/user_put.hpp index cd17782..e65c075 100644 --- a/src/handlers/users/user_put.hpp +++ b/src/handlers/users/user_put.hpp @@ -5,15 +5,16 @@ namespace real_medium::handlers::users::put { class Handler final : public Common { - public: - static constexpr std::string_view kName = "handler-user-put"; +public: + static constexpr std::string_view kName = "handler-user-put"; - using Common::Common; + using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::users::put diff --git a/src/handlers/users/users.cpp b/src/handlers/users/users.cpp index 8e5ed3d..76bd03b 100644 --- a/src/handlers/users/users.cpp +++ b/src/handlers/users/users.cpp @@ -14,43 +14,43 @@ namespace real_medium::handlers::users::post { -userver::formats::json::Value RegisterUser::HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext&) const { - handlers::UserRegistrationDTO user_register = - request_json["user"].As(); - ; - - try { - validator::validate(user_register); - } catch (const utils::error::ValidationException& err) { - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return err.GetDetails(); - } - - auto salt = utils::random::GenerateSalt(); - auto hash_password = - userver::crypto::hash::Sha256(user_register.password.value() + salt); - models::User result_user; - try { - auto query_result = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - sql::kInsertUser.data(), user_register.username, user_register.email, - user_register.bio, user_register.image, hash_password, salt); - result_user = query_result.AsSingleRow( - userver::storages::postgres::kRowTag); - } catch (const userver::storages::postgres::UniqueViolation& ex) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kUnprocessableEntity); - return utils::error::MakeError(ex.GetServerMessage().GetConstraint(), - ex.GetServerMessage().GetDetail()); - } - - userver::formats::json::ValueBuilder builder; - builder["user"] = result_user; - return builder.ExtractValue(); +userver::formats::json::Value RegisterUser:: + HandleRequestJsonThrow(const userver::server::http::HttpRequest& request, const userver::formats::json::Value& request_json, userver::server::request::RequestContext&) + const { + handlers::UserRegistrationDTO user_register = request_json["user"].As(); + ; + + try { + validator::validate(user_register); + } catch (const utils::error::ValidationException& err) { + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return err.GetDetails(); + } + + auto salt = utils::random::GenerateSalt(); + auto hash_password = userver::crypto::hash::Sha256(user_register.password.value() + salt); + models::User result_user; + try { + auto query_result = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + sql::kInsertUser.data(), + user_register.username, + user_register.email, + user_register.bio, + user_register.image, + hash_password, + salt + ); + result_user = query_result.AsSingleRow(userver::storages::postgres::kRowTag); + } catch (const userver::storages::postgres::UniqueViolation& ex) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return utils::error::MakeError(ex.GetServerMessage().GetConstraint(), ex.GetServerMessage().GetDetail()); + } + + userver::formats::json::ValueBuilder builder; + builder["user"] = result_user; + return builder.ExtractValue(); } } // namespace real_medium::handlers::users::post diff --git a/src/handlers/users/users.hpp b/src/handlers/users/users.hpp index 9720669..cc6d5a2 100644 --- a/src/handlers/users/users.hpp +++ b/src/handlers/users/users.hpp @@ -7,13 +7,14 @@ namespace real_medium::handlers::users::post { class RegisterUser final : public Common { - public: - static constexpr std::string_view kName = "handler-register-user"; - using Common::Common; - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext& context) const override; +public: + static constexpr std::string_view kName = "handler-register-user"; + using Common::Common; + userver::formats::json::Value HandleRequestJsonThrow( + const userver::server::http::HttpRequest& request, + const userver::formats::json::Value& request_json, + userver::server::request::RequestContext& context + ) const override; }; } // namespace real_medium::handlers::users::post diff --git a/src/handlers/users/users_login.cpp b/src/handlers/users/users_login.cpp index 5766ff0..fbfbb2c 100644 --- a/src/handlers/users/users_login.cpp +++ b/src/handlers/users/users_login.cpp @@ -17,62 +17,59 @@ namespace real_medium::handlers::users_login::post { namespace { class LoginUser final : public Common { - public: - static constexpr std::string_view kName = "handler-login-user"; - - using Common::Common; - - userver::formats::json::Value HandleRequestJsonThrow( - const userver::server::http::HttpRequest& request, - const userver::formats::json::Value& request_json, - userver::server::request::RequestContext&) const override { - auto&& user_login = request_json["user"].As(); - ; - - try { - validator::validate(user_login); - } catch (const utils::error::ValidationException& err) { - request.SetResponseStatus( - userver::server::http::HttpStatus::kUnprocessableEntity); - return err.GetDetails(); +public: + static constexpr std::string_view kName = "handler-login-user"; + + using Common::Common; + + userver::formats::json::Value + HandleRequestJsonThrow(const userver::server::http::HttpRequest& request, const userver::formats::json::Value& request_json, userver::server::request::RequestContext&) + const override { + auto&& user_login = request_json["user"].As(); + ; + + try { + validator::validate(user_login); + } catch (const utils::error::ValidationException& err) { + request.SetResponseStatus(userver::server::http::HttpStatus::kUnprocessableEntity); + return err.GetDetails(); + } + + auto salt = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, sql::kGetSaltByEmail.data(), user_login.email + ); + if (salt.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return {}; + } + auto password_hash = + userver::crypto::hash::Sha256(user_login.password.value() + salt.AsSingleRow()); + + auto userResult = GetPg().Execute( + userver::storages::postgres::ClusterHostType::kMaster, + sql::kSelectUserByEmailAndPassword.data(), + user_login.email, + password_hash + ); + + if (userResult.IsEmpty()) { + auto& response = request.GetHttpResponse(); + response.SetStatus(userver::server::http::HttpStatus::kNotFound); + return {}; + } + + auto user = userResult.AsSingleRow(userver::storages::postgres::kRowTag); + + userver::formats::json::ValueBuilder response; + response["user"] = user; + + return response.ExtractValue(); } - - auto salt = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - sql::kGetSaltByEmail.data(), user_login.email); - if (salt.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return {}; - } - auto password_hash = userver::crypto::hash::Sha256( - user_login.password.value() + salt.AsSingleRow()); - - auto userResult = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, - sql::kSelectUserByEmailAndPassword.data(), - user_login.email, password_hash); - - if (userResult.IsEmpty()) { - auto& response = request.GetHttpResponse(); - response.SetStatus(userver::server::http::HttpStatus::kNotFound); - return {}; - } - - auto user = userResult.AsSingleRow( - userver::storages::postgres::kRowTag); - - userver::formats::json::ValueBuilder response; - response["user"] = user; - - return response.ExtractValue(); - } }; } // namespace -void AppendLoginUser(userver::components::ComponentList& component_list) { - component_list.Append(); -} +void AppendLoginUser(userver::components::ComponentList& component_list) { component_list.Append(); } } // namespace real_medium::handlers::users_login::post diff --git a/src/main.cpp b/src/main.cpp index fb2901c..6a69fc5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,40 +38,37 @@ using namespace real_medium::handlers; int main(int argc, char* argv[]) { - userver::server::handlers::auth::RegisterAuthCheckerFactory< - real_medium::auth::CheckerFactory>(); + userver::server::handlers::auth::RegisterAuthCheckerFactory(); - auto component_list = - userver::components::MinimalServerComponentList() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append("realmedium-database") - .Append() // real_medium::handlers::users_login::post - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append(); + auto component_list = userver::components::MinimalServerComponentList() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append("realmedium-database") + .Append() // real_medium::handlers::users_login::post + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append() + .Append(); - real_medium::handlers::users_login::post::AppendLoginUser(component_list); + real_medium::handlers::users_login::post::AppendLoginUser(component_list); - return userver::utils::DaemonMain(argc, argv, component_list); + return userver::utils::DaemonMain(argc, argv, component_list); } diff --git a/src/models/article.cpp b/src/models/article.cpp index 34ad07b..09553f7 100644 --- a/src/models/article.cpp +++ b/src/models/article.cpp @@ -3,23 +3,22 @@ namespace real_medium::models { -userver::formats::json::Value Serialize( - const TaggedArticleWithProfile& article, - userver::formats::serialize::To) { - userver::formats::json::ValueBuilder item; +userver::formats::json::Value +Serialize(const TaggedArticleWithProfile& article, userver::formats::serialize::To) { + userver::formats::json::ValueBuilder item; - item["slug"] = article.slug; - item["title"] = article.title; - item["description"] = article.description; - item["body"] = article.body; - item["tags"] = article.tags; - item["createdAt"] = article.createdAt; - item["updatedAt"] = article.updatedAt; - item["favorited"] = article.isFavorited; - item["favoritesCount"] = article.favoritesCount; - item["author"] = article.authorProfile; + item["slug"] = article.slug; + item["title"] = article.title; + item["description"] = article.description; + item["body"] = article.body; + item["tags"] = article.tags; + item["createdAt"] = article.createdAt; + item["updatedAt"] = article.updatedAt; + item["favorited"] = article.isFavorited; + item["favoritesCount"] = article.favoritesCount; + item["author"] = article.authorProfile; - return item.ExtractValue(); + return item.ExtractValue(); } } // namespace real_medium::models diff --git a/src/models/article.hpp b/src/models/article.hpp index 6cf40c5..0a644ae 100644 --- a/src/models/article.hpp +++ b/src/models/article.hpp @@ -14,50 +14,69 @@ namespace real_medium::models { using ArticleId = std::string; struct FullArticleInfo { - ArticleId articleId; - std::string title; - std::string slug; - std::string description; - std::string body; - std::unordered_set tags; - userver::storages::postgres::TimePointTz createdAt; - userver::storages::postgres::TimePointTz updatedAt; - std::unordered_set articleFavoritedByUserIds; - std::unordered_set articleFavoritedByUsernames; - std::unordered_set authorFollowedByUsersIds; - User authorInfo; + ArticleId articleId; + std::string title; + std::string slug; + std::string description; + std::string body; + std::unordered_set tags; + userver::storages::postgres::TimePointTz createdAt; + userver::storages::postgres::TimePointTz updatedAt; + std::unordered_set articleFavoritedByUserIds; + std::unordered_set articleFavoritedByUsernames; + std::unordered_set authorFollowedByUsersIds; + User authorInfo; - auto Introspect() { - return std::tie(articleId, title, slug, description, body, tags, createdAt, - updatedAt, articleFavoritedByUserIds, - articleFavoritedByUsernames, authorFollowedByUsersIds, - authorInfo); - } + auto Introspect() { + return std::tie( + articleId, + title, + slug, + description, + body, + tags, + createdAt, + updatedAt, + articleFavoritedByUserIds, + articleFavoritedByUsernames, + authorFollowedByUsersIds, + authorInfo + ); + } }; struct TaggedArticleWithProfile { - ArticleId articleId; - std::string title; - std::string slug; - std::string body; - std::string description; - userver::storages::postgres::TimePointTz createdAt; - userver::storages::postgres::TimePointTz updatedAt; - std::optional> tags; - bool isFavorited; - std::int64_t favoritesCount; - handlers::Profile authorProfile; + ArticleId articleId; + std::string title; + std::string slug; + std::string body; + std::string description; + userver::storages::postgres::TimePointTz createdAt; + userver::storages::postgres::TimePointTz updatedAt; + std::optional> tags; + bool isFavorited; + std::int64_t favoritesCount; + handlers::Profile authorProfile; - auto Introspect() { - return std::tie(articleId, title, slug, body, description, createdAt, - updatedAt, tags, isFavorited, favoritesCount, - authorProfile); - } + auto Introspect() { + return std::tie( + articleId, + title, + slug, + body, + description, + createdAt, + updatedAt, + tags, + isFavorited, + favoritesCount, + authorProfile + ); + } }; -userver::formats::json::Value Serialize( - const TaggedArticleWithProfile& article, - userver::formats::serialize::To); +userver::formats::json::Value +Serialize(const TaggedArticleWithProfile& article, userver::formats::serialize::To); } // namespace real_medium::models @@ -65,13 +84,12 @@ namespace userver::storages::postgres::io { template <> struct CppToUserPg { - static constexpr DBTypeName postgres_name{ - "real_medium.tagged_article_with_author_profile"}; + static constexpr DBTypeName postgres_name{"real_medium.tagged_article_with_author_profile"}; }; template <> struct CppToUserPg { - static constexpr DBTypeName postgres_name{"real_medium.full_article_info"}; + static constexpr DBTypeName postgres_name{"real_medium.full_article_info"}; }; } // namespace userver::storages::postgres::io diff --git a/src/models/comment.cpp b/src/models/comment.cpp index dc7f96b..ed9918e 100644 --- a/src/models/comment.cpp +++ b/src/models/comment.cpp @@ -3,18 +3,17 @@ namespace real_medium::models { -userver::formats::json::Value Serialize( - const Comment& comment, - userver::formats::serialize::To) { - userver::formats::json::ValueBuilder item; +userver::formats::json::Value +Serialize(const Comment& comment, userver::formats::serialize::To) { + userver::formats::json::ValueBuilder item; - item["id"] = comment.id; - item["createdAt"] = comment.created_at; - item["updatedAt"] = comment.updated_at; - item["body"] = comment.body; - item["author"] = comment.author; + item["id"] = comment.id; + item["createdAt"] = comment.created_at; + item["updatedAt"] = comment.updated_at; + item["body"] = comment.body; + item["author"] = comment.author; - return item.ExtractValue(); + return item.ExtractValue(); } } // namespace real_medium::models diff --git a/src/models/comment.hpp b/src/models/comment.hpp index 9276b77..54ab3f6 100644 --- a/src/models/comment.hpp +++ b/src/models/comment.hpp @@ -15,37 +15,31 @@ namespace real_medium::models { using CommentId = int32_t; struct Comment { - CommentId id; - userver::storages::postgres::TimePointTz created_at; - userver::storages::postgres::TimePointTz updated_at; - std::string body; - std::string user_id; - real_medium::handlers::Profile author; - - auto Introspect() { - return std::tie(id, created_at, updated_at, body, user_id, author); - } + CommentId id; + userver::storages::postgres::TimePointTz created_at; + userver::storages::postgres::TimePointTz updated_at; + std::string body; + std::string user_id; + real_medium::handlers::Profile author; + + auto Introspect() { return std::tie(id, created_at, updated_at, body, user_id, author); } }; struct CachedComment { - CommentId id; - userver::storages::postgres::TimePointTz created_at; - userver::storages::postgres::TimePointTz updated_at; - std::string body; - std::string user_id; - std::string slug; - real_medium::models::User author; - std::unordered_set following; - - auto Introspect() { - return std::tie(id, created_at, updated_at, body, user_id, slug, author, - following); - } + CommentId id; + userver::storages::postgres::TimePointTz created_at; + userver::storages::postgres::TimePointTz updated_at; + std::string body; + std::string user_id; + std::string slug; + real_medium::models::User author; + std::unordered_set following; + + auto Introspect() { return std::tie(id, created_at, updated_at, body, user_id, slug, author, following); } }; -userver::formats::json::Value Serialize( - const Comment& comment, - userver::formats::serialize::To); +userver::formats::json::Value +Serialize(const Comment& comment, userver::formats::serialize::To); } // namespace real_medium::models @@ -53,7 +47,7 @@ namespace userver::storages::postgres::io { template <> struct CppToUserPg { - static constexpr DBTypeName postgres_name{"real_medium.comment"}; + static constexpr DBTypeName postgres_name{"real_medium.comment"}; }; } // namespace userver::storages::postgres::io diff --git a/src/models/profile.hpp b/src/models/profile.hpp index ecbf734..dbf12d9 100644 --- a/src/models/profile.hpp +++ b/src/models/profile.hpp @@ -9,7 +9,7 @@ namespace userver::storages::postgres::io { template <> struct CppToUserPg { - static constexpr DBTypeName postgres_name{"real_medium.profile"}; + static constexpr DBTypeName postgres_name{"real_medium.profile"}; }; } // namespace userver::storages::postgres::io diff --git a/src/models/user.cpp b/src/models/user.cpp index ac5c05e..86d6e2b 100644 --- a/src/models/user.cpp +++ b/src/models/user.cpp @@ -5,16 +5,15 @@ namespace real_medium::models { -userver::formats::json::Value Serialize( - const User& user, - userver::formats::serialize::To) { - userver::formats::json::ValueBuilder item; - item["email"] = user.email; - item["username"] = user.username; - item["token"] = utils::jwt::GenerateJWT(user.id); - item["bio"] = user.bio; - item["image"] = user.image; - return item.ExtractValue(); +userver::formats::json::Value +Serialize(const User& user, userver::formats::serialize::To) { + userver::formats::json::ValueBuilder item; + item["email"] = user.email; + item["username"] = user.username; + item["token"] = utils::jwt::GenerateJWT(user.id); + item["bio"] = user.bio; + item["image"] = user.image; + return item.ExtractValue(); } } // namespace real_medium::models diff --git a/src/models/user.hpp b/src/models/user.hpp index 9afae81..21a9251 100644 --- a/src/models/user.hpp +++ b/src/models/user.hpp @@ -12,22 +12,19 @@ namespace real_medium::models { using UserId = std::string; struct User final { - UserId id; - std::string username; - std::string email; - std::string bio; - std::string image; - std::string password_hash; - std::string salt; - - auto Introspect() { - return std::tie(id, username, email, bio, image, password_hash, salt); - } + UserId id; + std::string username; + std::string email; + std::string bio; + std::string image; + std::string password_hash; + std::string salt; + + auto Introspect() { return std::tie(id, username, email, bio, image, password_hash, salt); } }; -userver::formats::json::Value Serialize( - const User& user, - userver::formats::serialize::To); +userver::formats::json::Value +Serialize(const User& user, userver::formats::serialize::To); } // namespace real_medium::models @@ -35,7 +32,7 @@ namespace userver::storages::postgres::io { template <> struct CppToUserPg { - static constexpr DBTypeName postgres_name{"real_medium.user"}; + static constexpr DBTypeName postgres_name{"real_medium.user"}; }; } // namespace userver::storages::postgres::io diff --git a/src/utils/errors.hpp b/src/utils/errors.hpp index e19b13b..0c367e3 100644 --- a/src/utils/errors.hpp +++ b/src/utils/errors.hpp @@ -9,21 +9,17 @@ namespace real_medium::utils::error { * userver doesn't yet support 422 HTTP error code */ class ValidationException - : public userver::server::handlers::ExceptionWithCode< - userver::server::handlers::HandlerErrorCode::kClientError> { - public: - ValidationException(std::string_view field, std::string_view msg) - : BaseType(MakeError(field, msg)) {} + : public userver::server::handlers::ExceptionWithCode { +public: + ValidationException(std::string_view field, std::string_view msg) : BaseType(MakeError(field, msg)) {} - explicit ValidationException(userver::formats::json::Value&& json) - : BaseType(std::move(json)) {} + explicit ValidationException(userver::formats::json::Value&& json) : BaseType(std::move(json)) {} }; class SlugifyException - : public userver::server::handlers::ExceptionWithCode< - userver::server::handlers::HandlerErrorCode::kClientError> { - public: - using BaseType::BaseType; + : public userver::server::handlers::ExceptionWithCode { +public: + using BaseType::BaseType; }; } // namespace real_medium::utils::error diff --git a/src/utils/jwt.cpp b/src/utils/jwt.cpp index b0f9a75..502d86c 100644 --- a/src/utils/jwt.cpp +++ b/src/utils/jwt.cpp @@ -5,15 +5,13 @@ namespace real_medium::utils::jwt { using namespace ::jwt::params; std::string GenerateJWT(std::string_view id) { - ::jwt::jwt_object obj{algorithm("HS256"), secret("secret"), - payload({{"id", id.data()}})}; - return obj.signature(); + ::jwt::jwt_object obj{algorithm("HS256"), secret("secret"), payload({{"id", id.data()}})}; + return obj.signature(); } ::jwt::jwt_payload DecodeJWT(std::string_view jwt_token) { - auto dec_obj = ::jwt::decode(jwt_token, algorithms({"HS256"}), - secret("secret"), verify(true)); - return dec_obj.payload(); + auto dec_obj = ::jwt::decode(jwt_token, algorithms({"HS256"}), secret("secret"), verify(true)); + return dec_obj.payload(); } } // namespace real_medium::utils::jwt diff --git a/src/utils/make_error.cpp b/src/utils/make_error.cpp index c2f8a49..5ad3c6d 100644 --- a/src/utils/make_error.cpp +++ b/src/utils/make_error.cpp @@ -2,10 +2,9 @@ #include namespace real_medium::utils::error { -userver::formats::json::Value MakeError(std::string_view field_name, - std::string_view message) { - userver::formats::json::ValueBuilder error_builder; - error_builder["errors"][field_name.data()].PushBack(message); - return error_builder.ExtractValue(); +userver::formats::json::Value MakeError(std::string_view field_name, std::string_view message) { + userver::formats::json::ValueBuilder error_builder; + error_builder["errors"][field_name.data()].PushBack(message); + return error_builder.ExtractValue(); } } // namespace real_medium::utils::error diff --git a/src/utils/make_error.hpp b/src/utils/make_error.hpp index c6cdc44..27ccb2b 100644 --- a/src/utils/make_error.hpp +++ b/src/utils/make_error.hpp @@ -1,9 +1,8 @@ #pragma once #include -#include "userver/formats/json/value.hpp" +#include namespace real_medium::utils::error { -userver::formats::json::Value MakeError(std::string_view field_name, - std::string_view message); +userver::formats::json::Value MakeError(std::string_view field_name, std::string_view message); } diff --git a/src/utils/random.cpp b/src/utils/random.cpp index 426893c..2347b5e 100644 --- a/src/utils/random.cpp +++ b/src/utils/random.cpp @@ -4,11 +4,11 @@ namespace real_medium::utils::random { std::string GenerateSalt() { - const int kSaltLength = 32; - std::string salt; - for (size_t i = 0; i < kSaltLength; ++i) { - salt += userver::utils::RandRange(32, 126); - } - return salt; + const int kSaltLength = 32; + std::string salt; + for (size_t i = 0; i < kSaltLength; ++i) { + salt += userver::utils::RandRange(32, 126); + } + return salt; } } // namespace real_medium::utils::random \ No newline at end of file diff --git a/src/utils/slugify.cpp b/src/utils/slugify.cpp index 112b926..175b4fc 100644 --- a/src/utils/slugify.cpp +++ b/src/utils/slugify.cpp @@ -1,37 +1,38 @@ #include "slugify.hpp" #include #include +#include #include "errors.hpp" #include "fmt/core.h" #include "unicode/translit.h" -#include "userver/formats/json/value.hpp" namespace real_medium::utils::slug { std::string Slugify(const std::string& str) { - UErrorCode status{U_ZERO_ERROR}; - UParseError parse_error{}; - const auto transliterator = - std::unique_ptr{icu::Transliterator::createFromRules( - "Slugify", - ":: Any-Latin;" - ":: [:Nonspacing Mark:] Remove;" - ":: [:Punctuation:] Remove;" - ":: [:Symbol:] Remove;" - ":: Latin-ASCII;" - ":: Lower();" - "' ' {' '} > ;" - "::NULL;" - "[:Separator:] > '-'", - UTRANS_FORWARD, parse_error, status)}; - if (status != U_ZERO_ERROR) { - throw std::runtime_error(fmt::format( - "icu::Transliterator::createFromRules failed with status {}", status)); - } - auto unicode_str = icu::UnicodeString::fromUTF8(str); - transliterator->transliterate(unicode_str); - std::string slug; - return unicode_str.toUTF8String(slug); + UErrorCode status{U_ZERO_ERROR}; + UParseError parse_error{}; + const auto transliterator = std::unique_ptr{icu::Transliterator::createFromRules( + "Slugify", + ":: Any-Latin;" + ":: [:Nonspacing Mark:] Remove;" + ":: [:Punctuation:] Remove;" + ":: [:Symbol:] Remove;" + ":: Latin-ASCII;" + ":: Lower();" + "' ' {' '} > ;" + "::NULL;" + "[:Separator:] > '-'", + UTRANS_FORWARD, + parse_error, + status + )}; + if (status != U_ZERO_ERROR) { + throw std::runtime_error(fmt::format("icu::Transliterator::createFromRules failed with status {}", status)); + } + auto unicode_str = icu::UnicodeString::fromUTF8(str); + transliterator->transliterate(unicode_str); + std::string slug; + return unicode_str.toUTF8String(slug); } } // namespace real_medium::utils::slug diff --git a/src/utils/slugify_test.cpp b/src/utils/slugify_test.cpp index e871128..ba216f5 100644 --- a/src/utils/slugify_test.cpp +++ b/src/utils/slugify_test.cpp @@ -14,28 +14,20 @@ TEST(SlugifyTest, TestEnglish3) { EXPECT_EQ(Slugify("ab%c"), "abc"); } TEST(SlugifyTest, TestEnglish4) { EXPECT_EQ(Slugify("ab c"), "ab-c"); } TEST(SlugifyTest, TestEnglish5) { - EXPECT_EQ(Slugify("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345" - "6789-._~:/?#[]@!$&'()*+,;="), - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789"); + EXPECT_EQ( + Slugify("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345" + "6789-._~:/?#[]@!$&'()*+,;="), + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789" + ); } -TEST(SlugifyTest, TestRussian) { - EXPECT_EQ(Slugify("Заголовок статьи"), "zagolovok-stat'i"); -} +TEST(SlugifyTest, TestRussian) { EXPECT_EQ(Slugify("Заголовок статьи"), "zagolovok-stat'i"); } -TEST(SlugifyTest, TestGerman) { - EXPECT_EQ(Slugify("Artikelüberschrift"), "artikeluberschrift"); -} +TEST(SlugifyTest, TestGerman) { EXPECT_EQ(Slugify("Artikelüberschrift"), "artikeluberschrift"); } -TEST(SlugifyTest, TestFrench) { - EXPECT_EQ(Slugify("le titre de l'article"), "le-titre-de-larticle"); -} +TEST(SlugifyTest, TestFrench) { EXPECT_EQ(Slugify("le titre de l'article"), "le-titre-de-larticle"); } -TEST(SlugifyTest, TestSpanish) { - EXPECT_EQ(Slugify("título del artículo"), "titulo-del-articulo"); -} +TEST(SlugifyTest, TestSpanish) { EXPECT_EQ(Slugify("título del artículo"), "titulo-del-articulo"); } -TEST(SlugifyTest, TestChineese) { - EXPECT_EQ(Slugify("文章標題"), "wen-zhang-biao-ti"); -} +TEST(SlugifyTest, TestChineese) { EXPECT_EQ(Slugify("文章標題"), "wen-zhang-biao-ti"); } } // namespace real_medium::utils::slug diff --git a/src/validators/article_validators.cpp b/src/validators/article_validators.cpp index 5409437..1222535 100644 --- a/src/validators/article_validators.cpp +++ b/src/validators/article_validators.cpp @@ -5,29 +5,29 @@ namespace real_medium::validator { void ValidateTitle(const std::string& title) { - static constexpr std::size_t MIN_TITLE_LEN = 3; - static constexpr std::size_t MAX_TITLE_LEN = 256; - CheckLength(title, "title", MIN_TITLE_LEN, MAX_TITLE_LEN); + static constexpr std::size_t MIN_TITLE_LEN = 3; + static constexpr std::size_t MAX_TITLE_LEN = 256; + CheckLength(title, "title", MIN_TITLE_LEN, MAX_TITLE_LEN); } void ValidateDescription(const std::string& description) { - static constexpr std::size_t MIN_DESCR_LEN = 5; - static constexpr std::size_t MAX_DESCR_LEN = 8192; + static constexpr std::size_t MIN_DESCR_LEN = 5; + static constexpr std::size_t MAX_DESCR_LEN = 8192; - CheckLength(description, "description", MIN_DESCR_LEN, MAX_DESCR_LEN); + CheckLength(description, "description", MIN_DESCR_LEN, MAX_DESCR_LEN); } void ValidateBody(const std::string& body) { - static constexpr std::size_t MIN_BODY_LEN = 5; - static constexpr std::size_t MAX_BODY_LEN = 65535; + static constexpr std::size_t MIN_BODY_LEN = 5; + static constexpr std::size_t MAX_BODY_LEN = 65535; - CheckLength(body, "body", MIN_BODY_LEN, MAX_BODY_LEN); + CheckLength(body, "body", MIN_BODY_LEN, MAX_BODY_LEN); } void ValidateTags(const std::vector& tags) { - static constexpr std::size_t MIN_TAG_NAME_LEN = 2; - static constexpr std::size_t MAX_TAG_NAME_LEN = 256; + static constexpr std::size_t MIN_TAG_NAME_LEN = 2; + static constexpr std::size_t MAX_TAG_NAME_LEN = 256; - for (const auto& tag : tags) { - CheckLength(tag, "tags", MIN_TAG_NAME_LEN, MAX_TAG_NAME_LEN); - } + for (const auto& tag : tags) { + CheckLength(tag, "tags", MIN_TAG_NAME_LEN, MAX_TAG_NAME_LEN); + } } } // namespace real_medium::validator diff --git a/src/validators/length_validator.cpp b/src/validators/length_validator.cpp index 799d635..8d28b97 100644 --- a/src/validators/length_validator.cpp +++ b/src/validators/length_validator.cpp @@ -4,15 +4,13 @@ namespace real_medium::validator { -void CheckLength(const std::string& value, std::string_view fieldName, - std::size_t minLen, std::size_t maxLen) { - auto length = userver::utils::text::utf8::GetCodePointsCount(value); - if (length < minLen || length > maxLen) { - throw utils::error::ValidationException{ - fieldName, - fmt::format("must be longer than {} characters and less than {}", - minLen, maxLen)}; - } +void CheckLength(const std::string& value, std::string_view fieldName, std::size_t minLen, std::size_t maxLen) { + auto length = userver::utils::text::utf8::GetCodePointsCount(value); + if (length < minLen || length > maxLen) { + throw utils::error::ValidationException{ + fieldName, fmt::format("must be longer than {} characters and less than {}", minLen, maxLen) + }; + } } } // namespace real_medium::validator diff --git a/src/validators/length_validator.hpp b/src/validators/length_validator.hpp index a831d10..4e854d6 100644 --- a/src/validators/length_validator.hpp +++ b/src/validators/length_validator.hpp @@ -3,12 +3,11 @@ #include #include #include -#include "../utils/errors.hpp" -#include "../utils/make_error.hpp" +#include "utils/errors.hpp" +#include "utils/make_error.hpp" namespace real_medium::validator { -void CheckLength(const std::string& value, std::string_view fieldName, - std::size_t minLen, std::size_t maxLen); +void CheckLength(const std::string& value, std::string_view fieldName, std::size_t minLen, std::size_t maxLen); } // namespace real_medium::validator diff --git a/src/validators/user_validators.cpp b/src/validators/user_validators.cpp index acb578f..46312c8 100644 --- a/src/validators/user_validators.cpp +++ b/src/validators/user_validators.cpp @@ -3,8 +3,8 @@ namespace real_medium::validator { bool ValidateEmail(const std::string& email) { - const std::regex pattern{R"(^[\w\-\.]+@([\w-]+\.)+[\w-]{2,4}$)"}; - return regex_match(email.begin(), email.end(), pattern); + const std::regex pattern{R"(^[\w\-\.]+@([\w-]+\.)+[\w-]{2,4}$)"}; + return regex_match(email.begin(), email.end(), pattern); } bool ValidatePassword(const std::string& password) { return !password.empty(); } bool ValidateUsername(const std::string& username) { return !username.empty(); } diff --git a/src/validators/validator_test.cpp b/src/validators/validator_test.cpp index 347fbf5..f087f88 100644 --- a/src/validators/validator_test.cpp +++ b/src/validators/validator_test.cpp @@ -8,92 +8,78 @@ using namespace real_medium::validator; using namespace real_medium::utils::error; UTEST(UserValidation, EmailValidation) { - EXPECT_TRUE(ValidateEmail("kek123@kek-lol.ru")); - EXPECT_FALSE(ValidateEmail("")); - EXPECT_FALSE(ValidateEmail("kek@lol.ru@lol.com")); + EXPECT_TRUE(ValidateEmail("kek123@kek-lol.ru")); + EXPECT_FALSE(ValidateEmail("")); + EXPECT_FALSE(ValidateEmail("kek@lol.ru@lol.com")); } UTEST(UserValidation, PasswordValidation) { - EXPECT_TRUE(ValidatePassword("keklol")); - EXPECT_FALSE(ValidatePassword("")); + EXPECT_TRUE(ValidatePassword("keklol")); + EXPECT_FALSE(ValidatePassword("")); } UTEST(UserValidation, UsernameValidation) { - EXPECT_TRUE(ValidateUsername("kek")); - EXPECT_FALSE(ValidateUsername("")); + EXPECT_TRUE(ValidateUsername("kek")); + EXPECT_FALSE(ValidateUsername("")); } UTEST(UserValidation, LoginValidation) { - using real_medium::handlers::UserLoginDTO; + using real_medium::handlers::UserLoginDTO; - UEXPECT_NO_THROW(validate(UserLoginDTO{"kek@lol.ru", "keklol"})); - UEXPECT_THROW(validate(UserLoginDTO{std::nullopt, "keklol"}), - ValidationException); - UEXPECT_THROW(validate(UserLoginDTO{"kek@lol.ru", std::nullopt}), - ValidationException); + UEXPECT_NO_THROW(validate(UserLoginDTO{"kek@lol.ru", "keklol"})); + UEXPECT_THROW(validate(UserLoginDTO{std::nullopt, "keklol"}), ValidationException); + UEXPECT_THROW(validate(UserLoginDTO{"kek@lol.ru", std::nullopt}), ValidationException); } UTEST(UserValidation, RegisterValidation) { - using real_medium::handlers::UserRegistrationDTO; - - UEXPECT_NO_THROW( - validate(UserRegistrationDTO{"kek", "kek@lol.ru", "keklol"})); - UEXPECT_THROW( - validate(UserRegistrationDTO{std::nullopt, "kek@lol.ru", "keklol"}), - ValidationException); - UEXPECT_THROW(validate(UserRegistrationDTO{"kek", std::nullopt, "keklol"}), - ValidationException); - UEXPECT_THROW( - validate(UserRegistrationDTO{"kek", "kek@lol.ru", std::nullopt}), - ValidationException); + using real_medium::handlers::UserRegistrationDTO; + + UEXPECT_NO_THROW(validate(UserRegistrationDTO{"kek", "kek@lol.ru", "keklol"})); + UEXPECT_THROW(validate(UserRegistrationDTO{std::nullopt, "kek@lol.ru", "keklol"}), ValidationException); + UEXPECT_THROW(validate(UserRegistrationDTO{"kek", std::nullopt, "keklol"}), ValidationException); + UEXPECT_THROW(validate(UserRegistrationDTO{"kek", "kek@lol.ru", std::nullopt}), ValidationException); } UTEST(CommentValidation, CreateCommentValidation) { - using real_medium::handlers::AddComment; + using real_medium::handlers::AddComment; - UEXPECT_NO_THROW(validate(AddComment{"some body"})); - UEXPECT_THROW(validate(AddComment{std::nullopt}), ValidationException); - UEXPECT_THROW(validate(AddComment{""}), ValidationException); + UEXPECT_NO_THROW(validate(AddComment{"some body"})); + UEXPECT_THROW(validate(AddComment{std::nullopt}), ValidationException); + UEXPECT_THROW(validate(AddComment{""}), ValidationException); } UTEST(ArticleValidation, CreateArticleValidation) { - using real_medium::handlers::CreateArticleRequest; - - UEXPECT_NO_THROW(validate( - CreateArticleRequest{"title", "description", "some body", std::nullopt})); - UEXPECT_NO_THROW(validate(CreateArticleRequest{ - "title", "description", "some body", std::vector{}})); - UEXPECT_NO_THROW( - validate(CreateArticleRequest{"title", "description", "some body", - std::vector{"kek", "lol"}})); - - UEXPECT_THROW( - validate(CreateArticleRequest{std::nullopt, "description", "some body", - std::vector{}}), - ValidationException); - UEXPECT_THROW( - validate(CreateArticleRequest{"title", std::nullopt, "some body", - std::vector{}}), - ValidationException); - UEXPECT_THROW( - validate(CreateArticleRequest{"title", "description", std::nullopt, - std::vector{}}), - ValidationException); - UEXPECT_THROW( - validate(CreateArticleRequest{"title", "description", "some body", - std::vector{""}}), - ValidationException); + using real_medium::handlers::CreateArticleRequest; + + UEXPECT_NO_THROW(validate(CreateArticleRequest{"title", "description", "some body", std::nullopt})); + UEXPECT_NO_THROW(validate(CreateArticleRequest{"title", "description", "some body", std::vector{}})); + UEXPECT_NO_THROW( + validate(CreateArticleRequest{"title", "description", "some body", std::vector{"kek", "lol"}}) + ); + + UEXPECT_THROW( + validate(CreateArticleRequest{std::nullopt, "description", "some body", std::vector{}}), + ValidationException + ); + UEXPECT_THROW( + validate(CreateArticleRequest{"title", std::nullopt, "some body", std::vector{}}), + ValidationException + ); + UEXPECT_THROW( + validate(CreateArticleRequest{"title", "description", std::nullopt, std::vector{}}), + ValidationException + ); + UEXPECT_THROW( + validate(CreateArticleRequest{"title", "description", "some body", std::vector{""}}), + ValidationException + ); } UTEST(ArticleValidation, UpdateArticleValidation) { - using real_medium::handlers::UpdateArticleRequest; + using real_medium::handlers::UpdateArticleRequest; - UEXPECT_NO_THROW( - validate(UpdateArticleRequest{"title", "description", "some body"})); - UEXPECT_NO_THROW( - validate(UpdateArticleRequest{std::nullopt, std::nullopt, std::nullopt})); + UEXPECT_NO_THROW(validate(UpdateArticleRequest{"title", "description", "some body"})); + UEXPECT_NO_THROW(validate(UpdateArticleRequest{std::nullopt, std::nullopt, std::nullopt})); - UEXPECT_THROW( - validate(UpdateArticleRequest{std::nullopt, std::nullopt, "body"}), - ValidationException); + UEXPECT_THROW(validate(UpdateArticleRequest{std::nullopt, std::nullopt, "body"}), ValidationException); } diff --git a/src/validators/validators.cpp b/src/validators/validators.cpp index 1369edd..f8f7be7 100644 --- a/src/validators/validators.cpp +++ b/src/validators/validators.cpp @@ -6,96 +6,96 @@ namespace real_medium::validator { void validate(const handlers::UserLoginDTO& dto) { - if (!dto.email) { - throw utils::error::ValidationException("email", "Field is missing"); - } else if (!ValidateEmail(dto.email.value())) { - throw utils::error::ValidationException("email", "Invalid field"); - } - - if (!dto.password) { - throw utils::error::ValidationException("password", "Field is missing"); - } else if (!ValidatePassword(dto.password.value())) { - throw utils::error::ValidationException("password", "Invalid field"); - } + if (!dto.email) { + throw utils::error::ValidationException("email", "Field is missing"); + } else if (!ValidateEmail(dto.email.value())) { + throw utils::error::ValidationException("email", "Invalid field"); + } + + if (!dto.password) { + throw utils::error::ValidationException("password", "Field is missing"); + } else if (!ValidatePassword(dto.password.value())) { + throw utils::error::ValidationException("password", "Invalid field"); + } } void validate(const handlers::UserRegistrationDTO& dto) { - if (!dto.username) { - throw utils::error::ValidationException("username", "Field is missing"); - } else if (!ValidateUsername(dto.username.value())) { - throw utils::error::ValidationException("username", "Invalid field"); - } - - if (!dto.email) { - throw utils::error::ValidationException("email", "Field is missing"); - } else if (!ValidateEmail(dto.email.value())) { - throw utils::error::ValidationException("email", "Invalid field"); - } - - if (!dto.password) { - throw utils::error::ValidationException("password", "Field is missing"); - } else if (!ValidatePassword(dto.password.value())) { - throw utils::error::ValidationException("password", "Invalid value"); - } + if (!dto.username) { + throw utils::error::ValidationException("username", "Field is missing"); + } else if (!ValidateUsername(dto.username.value())) { + throw utils::error::ValidationException("username", "Invalid field"); + } + + if (!dto.email) { + throw utils::error::ValidationException("email", "Field is missing"); + } else if (!ValidateEmail(dto.email.value())) { + throw utils::error::ValidationException("email", "Invalid field"); + } + + if (!dto.password) { + throw utils::error::ValidationException("password", "Field is missing"); + } else if (!ValidatePassword(dto.password.value())) { + throw utils::error::ValidationException("password", "Invalid value"); + } } void validate(const handlers::UserUpdateDTO& dto) { - if (dto.username && !ValidateUsername(dto.username.value())) { - throw utils::error::ValidationException("username", "Invalid field"); - } + if (dto.username && !ValidateUsername(dto.username.value())) { + throw utils::error::ValidationException("username", "Invalid field"); + } - if (dto.email && !ValidateEmail(dto.email.value())) { - throw utils::error::ValidationException("email", "Invalid field"); - } + if (dto.email && !ValidateEmail(dto.email.value())) { + throw utils::error::ValidationException("email", "Invalid field"); + } - if (dto.password && !ValidatePassword(dto.password.value())) { - throw utils::error::ValidationException("password", "Invalid field"); - } + if (dto.password && !ValidatePassword(dto.password.value())) { + throw utils::error::ValidationException("password", "Invalid field"); + } } void validate(const handlers::AddComment& dto) { - if (!dto.body) { - throw utils::error::ValidationException("body", "Field is missing"); - } + if (!dto.body) { + throw utils::error::ValidationException("body", "Field is missing"); + } - if (dto.body.value().empty()) { - throw utils::error::ValidationException("body", "Invalid field"); - } + if (dto.body.value().empty()) { + throw utils::error::ValidationException("body", "Invalid field"); + } } void validate(const handlers::CreateArticleRequest& dto) { - if (!dto.title) { - throw utils::error::ValidationException("title", "Field is missing"); - } else { - ValidateTitle(dto.title.value()); - } - - if (!dto.description) { - throw utils::error::ValidationException("description", "Field is missing"); - } else { - ValidateDescription(dto.description.value()); - } - - if (!dto.body) { - throw utils::error::ValidationException("body", "Field is missing"); - } else { - ValidateBody(dto.body.value()); - } - - if (dto.tags) { - ValidateTags(dto.tags.value()); - } + if (!dto.title) { + throw utils::error::ValidationException("title", "Field is missing"); + } else { + ValidateTitle(dto.title.value()); + } + + if (!dto.description) { + throw utils::error::ValidationException("description", "Field is missing"); + } else { + ValidateDescription(dto.description.value()); + } + + if (!dto.body) { + throw utils::error::ValidationException("body", "Field is missing"); + } else { + ValidateBody(dto.body.value()); + } + + if (dto.tags) { + ValidateTags(dto.tags.value()); + } } void validate(const handlers::UpdateArticleRequest& dto) { - if (dto.title) { - ValidateTitle(dto.title.value()); - } + if (dto.title) { + ValidateTitle(dto.title.value()); + } - if (dto.description) { - ValidateDescription(dto.description.value()); - } + if (dto.description) { + ValidateDescription(dto.description.value()); + } - if (dto.body) { - ValidateBody(dto.body.value()); - } + if (dto.body) { + ValidateBody(dto.body.value()); + } } } // namespace real_medium::validator