Skip to content

Commit 031c877

Browse files
authored
Merge pull request #17 from tuanle03/clr-56-create-api-post
[CLR-56] Create API Posts
2 parents 7175b14 + 323cb16 commit 031c877

File tree

12 files changed

+534
-16
lines changed

12 files changed

+534
-16
lines changed

app/api/web/posts_api.rb

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
module AP
2+
class Web::PostsAPI < Grape::API
3+
4+
helpers do
5+
def format_post(post)
6+
{
7+
id: post.id,
8+
title: post.title,
9+
body: post.body,
10+
total_view: post.total_view,
11+
total_feedback: post.feedbacks.count,
12+
created_at: post.created_at.to_fs(:ymd_hms)
13+
}
14+
end
15+
end
16+
17+
resource :posts do
18+
19+
desc 'Get a list of posts by limit'
20+
params do
21+
optional :limit, type: Integer, default: 10, desc: 'Limit number of posts'
22+
end
23+
get do
24+
posts = Post.approved.limit(params[:limit]).map { |post| format_post(post) }
25+
status 200
26+
{
27+
success: true,
28+
posts: posts
29+
}
30+
end
31+
32+
desc 'Get a list of posts by newest'
33+
params do
34+
optional :limit, type: Integer, default: 10, desc: 'Limit number of posts'
35+
end
36+
get '/newest' do
37+
posts = Post.approved.newest.limit(params[:limit]).map { |post| format_post(post) }
38+
status 200
39+
{
40+
success: true,
41+
posts: posts
42+
}
43+
end
44+
45+
desc 'Get a list of posts by most viewed'
46+
params do
47+
optional :limit, type: Integer, default: 10, desc: 'Limit number of posts'
48+
end
49+
get '/most_viewed' do
50+
posts = Post.approved.most_viewed.limit(params[:limit]).map { |post| format_post(post) }
51+
status 200
52+
{
53+
success: true,
54+
posts: posts
55+
}
56+
end
57+
58+
desc 'Get a post'
59+
params do
60+
requires :id, type: Integer, desc: 'Post id'
61+
end
62+
get ':id' do
63+
post = Post.find_by(id: params[:id])
64+
if post
65+
post.update(total_view: post.total_view + 1)
66+
status 200
67+
{
68+
success: true,
69+
post: format_post(post)
70+
}
71+
else
72+
status 400
73+
{
74+
success: false,
75+
error: 'Post not found'
76+
}
77+
end
78+
end
79+
80+
desc 'Create a post'
81+
params do
82+
requires :title, type: String, desc: 'Post title', allow_blank: false
83+
requires :body, type: String, desc: 'Post body', allow_blank: false
84+
end
85+
post do
86+
authenticate_user!
87+
if current_user.admin?
88+
post = current_user.posts.new({
89+
title: params[:title],
90+
body: params[:body],
91+
status: 'approved'
92+
})
93+
end
94+
95+
if post.save
96+
status 200
97+
{
98+
success: true,
99+
message: 'Post created successfully'
100+
}
101+
else
102+
status 400
103+
{
104+
success: false,
105+
error: 'Post cannot be created'
106+
}
107+
end
108+
end
109+
110+
desc 'Update a post'
111+
params do
112+
requires :id, type: Integer, desc: 'Post id'
113+
optional :new_title, type: String, desc: 'New post title'
114+
optional :new_body, type: String, desc: 'New post body'
115+
end
116+
put ':id' do
117+
authenticate_user!
118+
post = current_user.posts.find_by(id: params[:id])
119+
if post
120+
post.update({
121+
title: params[:new_title],
122+
body: params[:new_body]
123+
})
124+
status 200
125+
{
126+
success: true,
127+
message: 'Post updated successfully'
128+
}
129+
else
130+
status 400
131+
{
132+
success: false,
133+
error: 'Post cannot be updated'
134+
}
135+
end
136+
end
137+
138+
desc 'Delete a post'
139+
params do
140+
requires :id, type: Integer, desc: 'Post id'
141+
end
142+
delete ':id' do
143+
authenticate_user!
144+
if current_user.admin?
145+
post = Post.find_by(id: params[:id])
146+
else
147+
post = current_user.posts.find_by(id: params[:id])
148+
end
149+
150+
if post
151+
post.update(status: 'deleted')
152+
status 200
153+
{
154+
success: true,
155+
message: 'Post deleted successfully'
156+
}
157+
else
158+
status 400
159+
{
160+
success: false,
161+
error: 'Post cannot be deleted'
162+
}
163+
end
164+
end
165+
166+
desc 'List of posts by user'
167+
params do
168+
requires :id, type: Integer, desc: 'User id'
169+
end
170+
get '/user/:id' do
171+
status 200
172+
{
173+
success: true,
174+
posts: current_user.posts.approved.map { |post| format_post(post) }
175+
}
176+
end
177+
178+
desc 'Approve a post'
179+
params do
180+
requires :id, type: Integer, desc: 'Post id'
181+
end
182+
put '/approve/:id' do
183+
authenticate_user!
184+
if current_user.admin?
185+
post = Post.find_by(id: params[:id])
186+
if post
187+
post.update(status: 'approved')
188+
status 200
189+
{
190+
success: true,
191+
message: 'Post approved successfully'
192+
}
193+
end
194+
else
195+
status 400
196+
{
197+
success: false,
198+
error: 'You are not authorized'
199+
}
200+
end
201+
end
202+
203+
desc 'Get a list feedbacks of a post'
204+
params do
205+
requires :id, type: Integer, desc: 'Post id'
206+
end
207+
get '/:id/feedbacks' do
208+
feedbacks = Post.find_by(id: params[:id])&.feedbacks
209+
return status 400 unless feedbacks
210+
status 200
211+
{
212+
success: true,
213+
feedbacks: feedbacks.map do |feedback|
214+
{
215+
id: feedback.id,
216+
user_name: feedback.user.name,
217+
content: feedback.content,
218+
total_like: feedback.total_likes,
219+
created_at: feedback.created_at.to_fs(:ymd_hms)
220+
}
221+
end
222+
}
223+
end
224+
end
225+
end
226+
end

app/api/web_api.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class WebAPI < Grape::API
55
helpers ::Helpers::AuthenticationHelper
66

77
mount Web::FeedbacksAPI
8+
mount Web::PostsAPI
89
mount Web::SessionsAPI
910
mount Web::RegistrationsAPI
1011

app/models/feedback.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
class Feedback < ApplicationRecord
22
belongs_to :user
3+
belongs_to :post
34

45
has_many :comments, as: :linked_object
56
has_many :votes, as: :linked_object
7+
8+
def total_likes
9+
votes.liked.count
10+
end
11+
12+
def total_comments
13+
comments.count
14+
end
615
end

app/models/post.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
class Post < ApplicationRecord
22
validates :title, presence: true
3+
4+
belongs_to :user
5+
has_many :feedbacks, dependent: :destroy
6+
7+
scope :newest, -> { order(created_at: :desc) }
8+
scope :oldest, -> { order(created_at: :asc) }
9+
scope :most_viewed, -> { order(total_view: :desc) }
10+
scope :approved, -> { where(status: 'approved') }
11+
scope :pending, -> { where(status: 'pending') }
12+
scope :rejected, -> { where(status: 'rejected') }
13+
scope :total, -> { count }
314
end

app/models/user.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,12 @@ def revoke_jwt(token)
2929
def jwt_revoked?(token)
3030
revoked_tokens.include?(token)
3131
end
32+
33+
def admin?
34+
role == 'admin'
35+
end
36+
37+
def name
38+
"#{first_name} #{last_name}"
39+
end
3240
end

app/models/vote.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,26 @@ class Vote < ApplicationRecord
33

44
validates :user_id, presence: true
55

6-
def self.create_or_update(user_id, vote_params)
7-
vote = Vote.find_by(user_id: user_id, linked_object_id: vote_params[:linked_object_id], linked_object_type: vote_params[:linked_object_type])
8-
if vote && vote.status.present?
9-
vote.update(status: !vote.status)
10-
else
11-
vote = Vote.new(vote_params)
12-
vote.user_id = user_id
6+
scope :liked, -> { where(status: true) }
7+
8+
def self.toggle_vote(vote_params, user)
9+
vote = Vote.find_or_initialize_by(
10+
linked_object_id: vote_params[:linked_object_id],
11+
linked_object_type: vote_params[:linked_object_type],
12+
user_id: user.id
13+
)
14+
15+
if vote.status.nil? || vote.status == false
1316
vote.status = true
14-
vote.save
17+
else
18+
vote.status = false
1519
end
20+
vote.save
1621
end
1722

1823
private
1924

2025
def self.vote_params
21-
params.require(:vote).permit(:linked_object_id, :linked_object_type, :vote_type)
26+
params.require(:vote).permit(:linked_object_id, :linked_object_type)
2227
end
2328
end

config/puma.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
1717
#
18-
port ENV.fetch("PORT") { 3000 }
18+
port ENV.fetch("PORT") { 2106 }
1919

2020
# Specifies the `environment` that Puma will run in.
2121
#

spec/api/web/feedbacks_api_spec.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ def app
1313
let(:user) { create(:user) }
1414
let(:token) { user.generate_jwt }
1515

16+
before do
17+
create(:post, :approved, user: user)
18+
end
19+
1620
describe 'POST /web/feedbacks' do
1721
context 'with valid parameters' do
1822
it 'creates a feedback' do
19-
post '/web/feedbacks', { content: 'Great post!', post_id: 1 }, 'HTTP_AUTHORIZATION' => "Bearer #{token}"
23+
post '/web/feedbacks', { content: 'Feedback content', post_id: 1 }, 'HTTP_AUTHORIZATION' => "Bearer #{token}"
2024

2125
expect(last_response.status).to eq(200)
2226
json = JSON.parse(last_response.body)

0 commit comments

Comments
 (0)