Skip to content

Commit c8ea4ca

Browse files
authored
Added codegen by schema (#8)
1 parent d02ab27 commit c8ea4ca

35 files changed

+360
-241
lines changed

CMakeLists.txt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ project(realmedium_sample CXX)
33

44

55
# Adding userver dependency
6-
find_package(userver COMPONENTS core postgresql QUIET)
6+
find_package(userver COMPONENTS core postgresql chaotic QUIET)
77
if(NOT userver_FOUND) # Fallback to subdirectory usage
88
# Enable userver libraries that are needed in this project
99
set(USERVER_FEATURE_POSTGRESQL ON CACHE BOOL "" FORCE)
@@ -79,8 +79,6 @@ add_library(${PROJECT_NAME}_objs OBJECT
7979
src/dto/article.hpp
8080
src/dto/profile.cpp
8181
src/dto/profile.hpp
82-
src/dto/user.hpp
83-
src/dto/user.cpp
8482
src/dto/comment.hpp
8583
src/dto/comment.cpp
8684
src/db/sql.hpp
@@ -120,6 +118,22 @@ add_library(${PROJECT_NAME}_objs OBJECT
120118
include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
121119
target_link_libraries(${PROJECT_NAME}_objs PUBLIC userver::core userver::postgresql)
122120

121+
file(GLOB_RECURSE SCHEMAS ${CMAKE_CURRENT_SOURCE_DIR}/docs/*.yaml)
122+
userver_target_generate_chaotic(${PROJECT_NAME}-chgen
123+
ARGS
124+
-n "/components/schemas/([^/]*)/=real_medium::handlers::{0}"
125+
-f "(.*)={0}"
126+
--clang-format=
127+
--generate-serializers
128+
OUTPUT_DIR
129+
${CMAKE_CURRENT_BINARY_DIR}/src
130+
SCHEMAS
131+
${SCHEMAS}
132+
RELATIVE_TO
133+
${CMAKE_CURRENT_SOURCE_DIR}
134+
)
135+
target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}-chgen)
136+
123137
target_include_directories(${PROJECT_NAME}_objs PUBLIC cpp-jwt)
124138
target_link_libraries(${PROJECT_NAME}_objs PUBLIC cpp-jwt)
125139

docs/api/api.yaml

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
openapi: 3.0.0
2+
info:
3+
title: realmedium_sample 1.0
4+
description: a backend application built with userver framework
5+
version: 1.0.0
6+
servers:
7+
- url: localhost:8080
8+
description: local
9+
10+
paths:
11+
# /api/profiles/{username}:
12+
# get:
13+
# description: Get user profile
14+
15+
# /api/profiles/{username}/follow:
16+
# post:
17+
# description: Follow user
18+
# delete:
19+
# description: Unfollow user
20+
21+
/api/user:
22+
# get:
23+
# description: Get user profile
24+
put:
25+
description: Update user profile
26+
requestBody:
27+
content:
28+
application/json:
29+
schema:
30+
$ref: '#/components/schemas/UserUpdateDTO'
31+
responses:
32+
'200':
33+
description: User profile successfully updated
34+
35+
/api/users:
36+
post:
37+
description: Register user
38+
requestBody:
39+
content:
40+
application/json:
41+
schema:
42+
$ref: '#/components/schemas/UserRegistrationDTO'
43+
responses:
44+
'200':
45+
description: Successfully registered
46+
47+
/api/users/login:
48+
post:
49+
description: Login user
50+
requestBody:
51+
content:
52+
application/json:
53+
schema:
54+
$ref: '#/components/schemas/UserLoginDTO'
55+
responses:
56+
'200':
57+
description: Successfully logged in
58+
59+
/api/articles:
60+
# get:
61+
# description: Get list of articles
62+
post:
63+
description: Create article
64+
requestBody:
65+
content:
66+
application/json:
67+
schema:
68+
$ref: '#/components/schemas/CreateArticleRequest'
69+
responses:
70+
'200':
71+
description: Successfully created article
72+
73+
/api/articles/{slug}:
74+
# get:
75+
# description: Get an article
76+
put:
77+
description: Update article
78+
requestBody:
79+
content:
80+
application/json:
81+
schema:
82+
$ref: '#/components/schemas/UpdateArticleRequest'
83+
responses:
84+
'200':
85+
description: Successfully updated article
86+
# delete:
87+
# description: Delete article
88+
89+
/api/articles/{slug}/favorite:
90+
# post:
91+
# description: Make an article favorite
92+
# delete:
93+
# description: Remove an article from favorites
94+
95+
/api/articles/{slug}/comments:
96+
post:
97+
description: Add comment
98+
parameters:
99+
- name: slug
100+
in: path
101+
required: true
102+
schema:
103+
type: string
104+
responses:
105+
'200':
106+
description: Comment successfully added
107+
requestBody:
108+
content:
109+
application/json:
110+
schema:
111+
$ref: '#/components/schemas/AddComment'
112+
# get:
113+
# description: Get comments list
114+
115+
# /api/articles/{slug}/comments/{id}:
116+
# delete:
117+
# description: Delete comment
118+
119+
components:
120+
schemas:
121+
UserLoginDTO:
122+
type: object
123+
additionalProperties: false
124+
properties:
125+
email:
126+
type: string
127+
password:
128+
type: string
129+
130+
UserRegistrationDTO:
131+
type: object
132+
additionalProperties: false
133+
properties:
134+
username:
135+
type: string
136+
email:
137+
type: string
138+
password:
139+
type: string
140+
141+
UserUpdateDTO:
142+
type: object
143+
additionalProperties: false
144+
properties:
145+
email:
146+
type: string
147+
username:
148+
type: string
149+
password:
150+
type: string
151+
bio:
152+
type: string
153+
image:
154+
type: string
155+
156+
CreateArticleRequest:
157+
type: object
158+
additionalProperties: false
159+
properties:
160+
title:
161+
type: string
162+
description:
163+
type: string
164+
body:
165+
type: string
166+
tags:
167+
type: array
168+
items:
169+
type: string
170+
171+
UpdateArticleRequest:
172+
type: object
173+
additionalProperties: false
174+
properties:
175+
title:
176+
type: string
177+
description:
178+
type: string
179+
body:
180+
type: string
181+
182+
AddComment:
183+
type: object
184+
additionalProperties: false
185+
properties:
186+
body:
187+
type: string
188+
189+
# Comment:
190+
191+
# Article:
192+
193+
# Profile:
194+
# type: object
195+
# additionalProperties: false
196+
# properties:
197+
# username:
198+
# type: string
199+
# bio:
200+
# type: string
201+
# image:
202+
# type: string
203+
# following:
204+
# type: boolean
205+
# required:
206+
# - username
207+
# - following
208+
209+
# securitySchemes:
210+
# bearerAuth:
211+
# type: http
212+
# scheme: bearer
213+
# bearerFormat: JWT
214+
215+
responses:
216+
UnauthorizedError:
217+
description: User is not authorized
218+
content:
219+
application/json:
220+
schema:
221+
type: object
222+
properties:
223+
error:
224+
type: string
225+
example: "Unauthorized"

postgresql/schemas/db-1.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ CREATE TYPE real_medium.tagged_article_with_author_profile AS (
122122
description TEXT,
123123
created_at TIMESTAMP WITH TIME ZONE,
124124
updated_at TIMESTAMP WITH TIME ZONE,
125-
tagList VARCHAR(255)[],
125+
tags VARCHAR(255)[],
126126
favorited BOOL,
127127
favorites_count BIGINT,
128128
author real_medium.profile);

src/dto/article.cpp

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Article Article::Parse(const models::TaggedArticleWithProfile& model) {
1717
article.profile.bio = model.authorProfile.bio;
1818
article.profile.image = model.authorProfile.image;
1919
article.profile.username = model.authorProfile.username;
20-
article.profile.isFollowing = model.authorProfile.isFollowing;
20+
article.profile.following = model.authorProfile.following;
2121
return article;
2222
}
2323

@@ -41,31 +41,13 @@ Article Article::Parse(const models::FullArticleInfo& model,
4141
article.profile.bio = model.authorInfo.bio;
4242
article.profile.image = model.authorInfo.image;
4343
article.profile.username = model.authorInfo.username;
44-
article.profile.isFollowing =
44+
article.profile.following =
4545
authUserId ? model.authorFollowedByUsersIds.find(authUserId.value()) !=
4646
model.authorFollowedByUsersIds.end()
4747
: false;
4848
return article;
4949
}
5050

51-
CreateArticleRequest CreateArticleRequest::Parse(
52-
const userver::formats::json::Value& json) {
53-
return CreateArticleRequest{
54-
json["title"].As<std::optional<std::string>>(),
55-
json["description"].As<std::optional<std::string>>(),
56-
json["body"].As<std::optional<std::string>>(),
57-
json["tagList"].As<std::optional<std::vector<std::string>>>()};
58-
}
59-
60-
UpdateArticleRequest UpdateArticleRequest::Parse(
61-
const userver::formats::json::Value& json,
62-
const userver::server::http::HttpRequest& request) {
63-
return UpdateArticleRequest{
64-
json["title"].As<std::optional<std::string>>(),
65-
json["description"].As<std::optional<std::string>>(),
66-
json["body"].As<std::optional<std::string>>()};
67-
}
68-
6951
userver::formats::json::Value Serialize(
7052
const Article& article,
7153
userver::formats::serialize::To<userver::formats::json::Value>) {
@@ -74,11 +56,11 @@ userver::formats::json::Value Serialize(
7456
builder["title"] = article.title;
7557
builder["description"] = article.description;
7658
builder["body"] = article.body;
77-
builder["tagList"] = userver::formats::common::Type::kArray;
59+
builder["tags"] = userver::formats::common::Type::kArray;
7860
if (article.tags) {
7961
std::for_each(
8062
article.tags->begin(), article.tags->end(),
81-
[&builder](const auto& tag) { builder["tagList"].PushBack(tag); });
63+
[&builder](const auto& tag) { builder["tags"].PushBack(tag); });
8264
}
8365
builder["createdAt"] = article.createdAt;
8466
builder["updatedAt"] = article.updatedAt;

src/dto/article.hpp

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,7 @@ struct Article final {
2222
userver::storages::postgres::TimePointTz updatedAt;
2323
std::int64_t favoritesCount{};
2424
bool isFavorited{false};
25-
Profile profile;
26-
};
27-
28-
struct CreateArticleRequest final {
29-
static CreateArticleRequest Parse(const userver::formats::json::Value& json);
30-
std::optional<std::string> title;
31-
std::optional<std::string> description;
32-
std::optional<std::string> body;
33-
std::optional<std::vector<std::string>> tags;
34-
};
35-
36-
struct UpdateArticleRequest final {
37-
static UpdateArticleRequest Parse(
38-
const userver::formats::json::Value& json,
39-
const userver::server::http::HttpRequest& request);
40-
std::optional<std::string> title;
41-
std::optional<std::string> description;
42-
std::optional<std::string> body;
25+
dto::Profile profile;
4326
};
4427

4528
userver::formats::json::Value Serialize(

src/dto/comment.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Comment Comment::Parse(const real_medium::models::CachedComment& cachedComment,
1212
comment.author.username = cachedComment.author.username;
1313
comment.author.bio = cachedComment.author.bio;
1414
comment.author.image = cachedComment.author.image;
15-
comment.author.isFollowing =
15+
comment.author.following =
1616
!userId.has_value() ? false : cachedComment.following.count(*userId);
1717
return comment;
1818
}
@@ -31,9 +31,4 @@ userver::formats::json::Value Serialize(
3131
return item.ExtractValue();
3232
}
3333

34-
AddComment Parse(const userver::formats::json::Value& json,
35-
userver::formats::parse::To<AddComment>) {
36-
return AddComment{json["body"].As<std::optional<std::string>>()};
37-
}
38-
3934
} // namespace real_medium::dto

src/dto/comment.hpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,9 @@ struct Comment final {
1818
userver::storages::postgres::TimePointTz createdAt;
1919
userver::storages::postgres::TimePointTz updatedAt;
2020
std::string body;
21-
Profile author;
21+
dto::Profile author;
2222
};
2323

24-
struct AddComment {
25-
std::optional<std::string> body;
26-
};
27-
28-
AddComment Parse(const userver::formats::json::Value& json,
29-
userver::formats::parse::To<AddComment>);
30-
3124
userver::formats::json::Value Serialize(
3225
const Comment& comment,
3326
userver::formats::serialize::To<userver::formats::json::Value>);

0 commit comments

Comments
 (0)