Skip to content

Commit

Permalink
Added codegen by schema (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
Liiizak authored Dec 8, 2024
1 parent d02ab27 commit c8ea4ca
Show file tree
Hide file tree
Showing 35 changed files with 360 additions and 241 deletions.
20 changes: 17 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ project(realmedium_sample CXX)


# Adding userver dependency
find_package(userver COMPONENTS core postgresql QUIET)
find_package(userver COMPONENTS core postgresql chaotic QUIET)
if(NOT userver_FOUND) # Fallback to subdirectory usage
# Enable userver libraries that are needed in this project
set(USERVER_FEATURE_POSTGRESQL ON CACHE BOOL "" FORCE)
Expand Down Expand Up @@ -79,8 +79,6 @@ add_library(${PROJECT_NAME}_objs OBJECT
src/dto/article.hpp
src/dto/profile.cpp
src/dto/profile.hpp
src/dto/user.hpp
src/dto/user.cpp
src/dto/comment.hpp
src/dto/comment.cpp
src/db/sql.hpp
Expand Down Expand Up @@ -120,6 +118,22 @@ add_library(${PROJECT_NAME}_objs OBJECT
include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(${PROJECT_NAME}_objs PUBLIC userver::core userver::postgresql)

file(GLOB_RECURSE SCHEMAS ${CMAKE_CURRENT_SOURCE_DIR}/docs/*.yaml)
userver_target_generate_chaotic(${PROJECT_NAME}-chgen
ARGS
-n "/components/schemas/([^/]*)/=real_medium::handlers::{0}"
-f "(.*)={0}"
--clang-format=
--generate-serializers
OUTPUT_DIR
${CMAKE_CURRENT_BINARY_DIR}/src
SCHEMAS
${SCHEMAS}
RELATIVE_TO
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}-chgen)

target_include_directories(${PROJECT_NAME}_objs PUBLIC cpp-jwt)
target_link_libraries(${PROJECT_NAME}_objs PUBLIC cpp-jwt)

Expand Down
225 changes: 225 additions & 0 deletions docs/api/api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
openapi: 3.0.0
info:
title: realmedium_sample 1.0
description: a backend application built with userver framework
version: 1.0.0
servers:
- url: localhost:8080
description: local

paths:
# /api/profiles/{username}:
# get:
# description: Get user profile

# /api/profiles/{username}/follow:
# post:
# description: Follow user
# delete:
# description: Unfollow user

/api/user:
# get:
# description: Get user profile
put:
description: Update user profile
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UserUpdateDTO'
responses:
'200':
description: User profile successfully updated

/api/users:
post:
description: Register user
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UserRegistrationDTO'
responses:
'200':
description: Successfully registered

/api/users/login:
post:
description: Login user
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UserLoginDTO'
responses:
'200':
description: Successfully logged in

/api/articles:
# get:
# description: Get list of articles
post:
description: Create article
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateArticleRequest'
responses:
'200':
description: Successfully created article

/api/articles/{slug}:
# get:
# description: Get an article
put:
description: Update article
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateArticleRequest'
responses:
'200':
description: Successfully updated article
# delete:
# description: Delete article

/api/articles/{slug}/favorite:
# post:
# description: Make an article favorite
# delete:
# description: Remove an article from favorites

/api/articles/{slug}/comments:
post:
description: Add comment
parameters:
- name: slug
in: path
required: true
schema:
type: string
responses:
'200':
description: Comment successfully added
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/AddComment'
# get:
# description: Get comments list

# /api/articles/{slug}/comments/{id}:
# delete:
# description: Delete comment

components:
schemas:
UserLoginDTO:
type: object
additionalProperties: false
properties:
email:
type: string
password:
type: string

UserRegistrationDTO:
type: object
additionalProperties: false
properties:
username:
type: string
email:
type: string
password:
type: string

UserUpdateDTO:
type: object
additionalProperties: false
properties:
email:
type: string
username:
type: string
password:
type: string
bio:
type: string
image:
type: string

CreateArticleRequest:
type: object
additionalProperties: false
properties:
title:
type: string
description:
type: string
body:
type: string
tags:
type: array
items:
type: string

UpdateArticleRequest:
type: object
additionalProperties: false
properties:
title:
type: string
description:
type: string
body:
type: string

AddComment:
type: object
additionalProperties: false
properties:
body:
type: string

# Comment:

# Article:

# Profile:
# type: object
# additionalProperties: false
# properties:
# username:
# type: string
# bio:
# type: string
# image:
# type: string
# following:
# type: boolean
# required:
# - username
# - following

# securitySchemes:
# bearerAuth:
# type: http
# scheme: bearer
# bearerFormat: JWT

responses:
UnauthorizedError:
description: User is not authorized
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "Unauthorized"
2 changes: 1 addition & 1 deletion postgresql/schemas/db-1.sql
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ CREATE TYPE real_medium.tagged_article_with_author_profile AS (
description TEXT,
created_at TIMESTAMP WITH TIME ZONE,
updated_at TIMESTAMP WITH TIME ZONE,
tagList VARCHAR(255)[],
tags VARCHAR(255)[],
favorited BOOL,
favorites_count BIGINT,
author real_medium.profile);
Expand Down
26 changes: 4 additions & 22 deletions src/dto/article.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Article Article::Parse(const models::TaggedArticleWithProfile& model) {
article.profile.bio = model.authorProfile.bio;
article.profile.image = model.authorProfile.image;
article.profile.username = model.authorProfile.username;
article.profile.isFollowing = model.authorProfile.isFollowing;
article.profile.following = model.authorProfile.following;
return article;
}

Expand All @@ -41,31 +41,13 @@ Article Article::Parse(const models::FullArticleInfo& model,
article.profile.bio = model.authorInfo.bio;
article.profile.image = model.authorInfo.image;
article.profile.username = model.authorInfo.username;
article.profile.isFollowing =
article.profile.following =
authUserId ? model.authorFollowedByUsersIds.find(authUserId.value()) !=
model.authorFollowedByUsersIds.end()
: false;
return article;
}

CreateArticleRequest CreateArticleRequest::Parse(
const userver::formats::json::Value& json) {
return CreateArticleRequest{
json["title"].As<std::optional<std::string>>(),
json["description"].As<std::optional<std::string>>(),
json["body"].As<std::optional<std::string>>(),
json["tagList"].As<std::optional<std::vector<std::string>>>()};
}

UpdateArticleRequest UpdateArticleRequest::Parse(
const userver::formats::json::Value& json,
const userver::server::http::HttpRequest& request) {
return UpdateArticleRequest{
json["title"].As<std::optional<std::string>>(),
json["description"].As<std::optional<std::string>>(),
json["body"].As<std::optional<std::string>>()};
}

userver::formats::json::Value Serialize(
const Article& article,
userver::formats::serialize::To<userver::formats::json::Value>) {
Expand All @@ -74,11 +56,11 @@ userver::formats::json::Value Serialize(
builder["title"] = article.title;
builder["description"] = article.description;
builder["body"] = article.body;
builder["tagList"] = userver::formats::common::Type::kArray;
builder["tags"] = userver::formats::common::Type::kArray;
if (article.tags) {
std::for_each(
article.tags->begin(), article.tags->end(),
[&builder](const auto& tag) { builder["tagList"].PushBack(tag); });
[&builder](const auto& tag) { builder["tags"].PushBack(tag); });
}
builder["createdAt"] = article.createdAt;
builder["updatedAt"] = article.updatedAt;
Expand Down
19 changes: 1 addition & 18 deletions src/dto/article.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,7 @@ struct Article final {
userver::storages::postgres::TimePointTz updatedAt;
std::int64_t favoritesCount{};
bool isFavorited{false};
Profile profile;
};

struct CreateArticleRequest final {
static CreateArticleRequest Parse(const userver::formats::json::Value& json);
std::optional<std::string> title;
std::optional<std::string> description;
std::optional<std::string> body;
std::optional<std::vector<std::string>> tags;
};

struct UpdateArticleRequest final {
static UpdateArticleRequest Parse(
const userver::formats::json::Value& json,
const userver::server::http::HttpRequest& request);
std::optional<std::string> title;
std::optional<std::string> description;
std::optional<std::string> body;
dto::Profile profile;
};

userver::formats::json::Value Serialize(
Expand Down
7 changes: 1 addition & 6 deletions src/dto/comment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Comment Comment::Parse(const real_medium::models::CachedComment& cachedComment,
comment.author.username = cachedComment.author.username;
comment.author.bio = cachedComment.author.bio;
comment.author.image = cachedComment.author.image;
comment.author.isFollowing =
comment.author.following =
!userId.has_value() ? false : cachedComment.following.count(*userId);
return comment;
}
Expand All @@ -31,9 +31,4 @@ userver::formats::json::Value Serialize(
return item.ExtractValue();
}

AddComment Parse(const userver::formats::json::Value& json,
userver::formats::parse::To<AddComment>) {
return AddComment{json["body"].As<std::optional<std::string>>()};
}

} // namespace real_medium::dto
9 changes: 1 addition & 8 deletions src/dto/comment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,9 @@ struct Comment final {
userver::storages::postgres::TimePointTz createdAt;
userver::storages::postgres::TimePointTz updatedAt;
std::string body;
Profile author;
dto::Profile author;
};

struct AddComment {
std::optional<std::string> body;
};

AddComment Parse(const userver::formats::json::Value& json,
userver::formats::parse::To<AddComment>);

userver::formats::json::Value Serialize(
const Comment& comment,
userver::formats::serialize::To<userver::formats::json::Value>);
Expand Down
Loading

0 comments on commit c8ea4ca

Please sign in to comment.