From 6d6b2af7df37f38fbb956d98959e925e1a64aa8f Mon Sep 17 00:00:00 2001 From: Osmond Oscar Date: Mon, 15 Feb 2016 17:19:41 +0100 Subject: [PATCH 1/4] Include User relationship in Comment Serializer --- app/serializers/comment_serializer.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/serializers/comment_serializer.rb b/app/serializers/comment_serializer.rb index 1e8cbe9..9df92e7 100644 --- a/app/serializers/comment_serializer.rb +++ b/app/serializers/comment_serializer.rb @@ -1,3 +1,4 @@ class CommentSerializer < MainSerializer - + attributes :user + belongs_to :user end From d61e8df860fd197a0a44243e5132128d5a661ce8 Mon Sep 17 00:00:00 2001 From: Osmond Oscar Date: Tue, 16 Feb 2016 15:20:38 +0100 Subject: [PATCH 2/4] Cleanup comment controller by using metaprogramming methods for resource object lookup. Modify comment routes to use scope restricted to questions and answers. Removed previous mixins and methods used for looking comment resource object --- app/controllers/application_controller.rb | 2 +- app/controllers/comments_controller.rb | 73 +++++++++-------------- app/controllers/concerns/common.rb | 22 ------- app/models/answer.rb | 27 --------- app/models/concerns/modify.rb | 16 ----- app/models/question.rb | 30 +--------- config/routes.rb | 11 ++-- 7 files changed, 36 insertions(+), 145 deletions(-) delete mode 100644 app/controllers/concerns/common.rb delete mode 100644 app/models/concerns/modify.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1b6c14f..064ce93 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,7 +10,7 @@ def resource_not_found render json: {errors: not_found}, status: 404 end - def invalid_request(message) + def invalid_request(message = error_msg) render json: {errors: message}, status: 400 end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 38f9fa4..2bcb976 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,76 +1,59 @@ class CommentsController < ApplicationController - before_action :custom_initializer - attr_reader :user_id, :question_id, :answer_id, :id, :content - - include Common + before_action :set_resource + before_action :set_comment, only: [:show, :update, :destroy] def index - question = Question.find_by(id: question_id) if action_on_question - answer = Answer.find_by(id: answer_id) if action_on_answer - comments = question.comments if question - comments = answer.comments if answer - render json: comments, status: 200 unless comments.nil? - render json: { error: false }, status: 404 if comments.nil? + render json: @resource_comments, status: 200 end def show - comments = Question.find_question_comment(question_id, id) if action_on_question - comments = Answer.find_answer_comment(answer_id, id) if action_on_answer - render json: comments , status: 200 - rescue - render json: { error: false }, status: 404 + render json: @comment, status: 200 end def create - unless content.nil? || content == "" - if action_on_question - comments = Question.add_comment_to_question(question_id, user_id, content) - elsif action_on_answer - comments = Answer.add_comment_to_answer(answer_id, user_id, content) - end - render json: comments, root: false + comment = @resource_comments.new(content: comment_params[:content]) + comment.user = current_user + if comment.save + render json: comment, root: false else - render json: { error: "Comment body can not be empty!" }, status: 403 + invalid_request("Comment body can not be empty!") end end def update - status = update_subject(id, user_id, "content", content) if content && content != "" - message = { response: status ? true : false } - render json: message - rescue - render json: { error: false }, status: 403 + if @comment.update(content: comment_params[:content]) + render json: @comment + else + invalid_request("Comment body can not be empty!") + end end def destroy - if action_on_question - deleted if Question.delete_question_comment(id, user_id, question_id) - elsif action_on_answer - deleted if Answer.delete_answer_comment(id, user_id, answer_id) + if @comment.try(:destroy) + render json: { response: "Comment deleted." }, status: 410 + else + invalid_request end - rescue - render json: { error: false }, status: 403 end private def comment_params - params.permit(:question_id, :answer_id, :id, :content, :downvote, :upvote) + params.permit(:resource_name, :resource_id, :content, :id) end - def custom_initializer - set_vars(comment_params) + def set_resource + resource = comment_params[:resource_name].singularize.camelize.constantize.find_by(id: comment_params[:resource_id]) + resource_not_found && return unless resource + @resource_comments = resource.comments end - def deleted - render json: { response: "Comment deleted." }, status: 410 - end + def set_comment + @comment = @resource_comments.find_by(id: comment_params[:id]) + resource_not_found && return unless @comment - def update_subject(id, user_id, attribute, value = nil) - if action_on_question - comments = Question.update_question_comment(id, user_id, question_id, attribute, value) - elsif action_on_answer - comments = Answer.update_answer_comment(id, user_id, answer_id, attribute, value) + unless (params[:action] == 'show') || (@comment.user.eql? current_user) + render json: { error: "It is your comment?" }, status: 401 end end end diff --git a/app/controllers/concerns/common.rb b/app/controllers/concerns/common.rb deleted file mode 100644 index ea39785..0000000 --- a/app/controllers/concerns/common.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Common - def set_vars(allowed = {}) - @question_id = allowed[:question_id] - @answer_id = allowed[:answer_id] - @comment_id = allowed[:comment_id] - @id = allowed[:id] - @content = allowed[:content] - @user_id = current_user.id if current_user - end - - def action_on_question - question_id && answer_id.nil? - end - - def action_on_answer - answer_id && question_id.nil? - end - - def action_on_comment - comment_id.present? - end -end diff --git a/app/models/answer.rb b/app/models/answer.rb index f8112f2..25d9ca8 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -7,31 +7,4 @@ class Answer < ActiveRecord::Base belongs_to :question, counter_cache: true validates :content, presence: true - - include Modify - - class << self - def add_comment_to_answer(answer_id, user_id, content) - answer = find_by(id: answer_id) - if answer - answer.comments.create(user_id: user_id, content: content) - end - end - - def find_answer_comment(answer_id, id) - answer = find_by(id: answer_id) - if answer - answer.comments.where(id: id) - end - end - - def delete_answer_comment(id, user_id, answer_id) - Modify::Updater.affected_record(answer_id, self, user_id).destroy(id) - end - - def update_answer_comment(id, user_id, answer_id, attribute, value) - Modify::Updater.update_subject_comment(id, user_id, answer_id, self, attribute, value) - end - end - end diff --git a/app/models/concerns/modify.rb b/app/models/concerns/modify.rb deleted file mode 100644 index bbf895b..0000000 --- a/app/models/concerns/modify.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Modify - class Updater - class << self - def update_subject_comment(id, user_id, subject_id, subject_name, attribute, value) - affected_record(subject_id, subject_name, user_id).update(id, content: value) - end - - def affected_record(subject_id, subject_name, user_id) - subject = subject_name.find_by(id: subject_id) - if subject - subject.comments.where(user_id: user_id) - end - end - end - end -end \ No newline at end of file diff --git a/app/models/question.rb b/app/models/question.rb index 24d8042..0636637 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -9,7 +9,6 @@ class Question < ActiveRecord::Base validates :content, presence: true validates :user, presence: true - include Modify include ActionView::Helpers::DateHelper def time_updated @@ -27,32 +26,7 @@ def increment_views update(views: views + 1) end - class << self - def with_answers - includes(:answers) - end - - - def add_comment_to_question(question_id, user_id, content) - question = find_by(id: question_id) - if question - question.comments.create(user_id: user_id, content: content) - end - end - - def find_question_comment(question_id, id) - question = find_by(id: question_id) - if question - question.comments.where(id: id) - end - end - - def delete_question_comment(id, user_id, question_id) - Modify::Updater.affected_record(question_id, self, user_id).destroy(id) - end - - def update_question_comment(id, user_id, question_id, attribute, value) - Modify::Updater.update_subject_comment(id, user_id, question_id, self, attribute, value) - end + def self.with_answers + includes(:answers) end end diff --git a/config/routes.rb b/config/routes.rb index 25c4727..f5725a0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,23 +4,22 @@ post '/downvote', to: 'votes#downvote' end + scope '/:resource_name/:resource_id', constraints: { resource_name: /(questions|answers)/} do + resources :comments, except: [:new, :edit] + end + post '/validate_token', to: 'tokens#validate' resources :questions, except: [:new, :edit] do get "recent_answers" get "popular_answers" - resources :comments, except: [:new, :edit] resources :answers, except: [:new, :edit] end get "top_questions" => "questions#top_questions" - resources :answers, except: [:index, :show, :create, :destroy, :update, :new, :edit] do - resources :comments, except: [:new, :edit] - end - - resources :comments, only: [:update, :delete] + resources :answers, except: [:index, :show, :create, :destroy, :update, :new, :edit] post "users/logout" => "user#logout" get 'users/renew_token' => 'users#renew_token' From 0501f56638ee5655a57fac34bd472b407657b005 Mon Sep 17 00:00:00 2001 From: Osmond Oscar Date: Tue, 16 Feb 2016 15:51:27 +0100 Subject: [PATCH 3/4] Update comment documentation to reflect new changes --- app/controllers/comments_controller.rb | 11 +- config/routes.rb | 2 - docs/comments_endpoints.md | 173 ++++++++++++++++++++++++- docs/vote_endpoints.md | 107 +++++++++++++++ 4 files changed, 278 insertions(+), 15 deletions(-) create mode 100644 docs/vote_endpoints.md diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 2bcb976..a5b0a31 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,6 +1,7 @@ class CommentsController < ApplicationController before_action :set_resource before_action :set_comment, only: [:show, :update, :destroy] + include OwnershipConcern def index render json: @resource_comments, status: 200 @@ -14,7 +15,7 @@ def create comment = @resource_comments.new(content: comment_params[:content]) comment.user = current_user if comment.save - render json: comment, root: false + render json: comment, status: 201 else invalid_request("Comment body can not be empty!") end @@ -22,7 +23,7 @@ def create def update if @comment.update(content: comment_params[:content]) - render json: @comment + render json: @comment, status: 200 else invalid_request("Comment body can not be empty!") end @@ -30,7 +31,7 @@ def update def destroy if @comment.try(:destroy) - render json: { response: "Comment deleted." }, status: 410 + render json: :head, status: 204 else invalid_request end @@ -51,9 +52,5 @@ def set_resource def set_comment @comment = @resource_comments.find_by(id: comment_params[:id]) resource_not_found && return unless @comment - - unless (params[:action] == 'show') || (@comment.user.eql? current_user) - render json: { error: "It is your comment?" }, status: 401 - end end end diff --git a/config/routes.rb b/config/routes.rb index f5725a0..814c997 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,8 +19,6 @@ get "top_questions" => "questions#top_questions" - resources :answers, except: [:index, :show, :create, :destroy, :update, :new, :edit] - post "users/logout" => "user#logout" get 'users/renew_token' => 'users#renew_token' get "users" => "users#index" diff --git a/docs/comments_endpoints.md b/docs/comments_endpoints.md index facc286..1674912 100644 --- a/docs/comments_endpoints.md +++ b/docs/comments_endpoints.md @@ -4,8 +4,12 @@ Endpoints | Usage | Public Access --------- | ----- | ------------- GET /answers/:id/comments | Returns all the comments for a particular answer | True POST /answers/:id/comments/ | Creates a new comment for the specified answer | False -PUT /comments/:id | Updates the comment with certain attributes | False -DELETE /comments/:id | Deletes an comment | False +PATCH /answers/:answer_id/comments/:id | Updates the specified comment of the specified answer | False +DELETE /answers/:answer_id/comments/:id | Deletes the specified comment of the specified answer | False +GET /questions/:id/comments | Returns all the comments for a particular question | True +POST /questions/:id/comments/ | Creates a new comment for the specified question | False +PATCH /questions/:question_id/comments/:id | Updates the specified comment of the specified question | False +DELETE /questions/:question_id/comments/:id | Deletes the specified comment of the specified question | False ### GET /answers/:id/comments @@ -49,11 +53,63 @@ Status: 200 } } ``` + Or ```ruby Status: 404 { - message: "Answer not found" + error: "The resource you tried to access was not found" +} +``` + +### GET /questions/:id/comments + +Request +```ruby + { + auth_token: "90ioji0j0i0i0ik0k0jmj0090jknieu93833r335" + } +``` + +Response +```ruby +Status: 200 + { + answer: { + id: 1, + comments: [ + { + id: 2, + text: 'What do you really want to do?', + date_created: 'Wed, 24TH Nov, 2017 10:00AM', + updated: false, + score: 8, + user: { + id: 2, + name: 'Oscar Laide' + } + }, + { + id: 4, + text: 'This is such a scam', + date_created: 'Wed, 25TH Nov, 2017 12:00PM', + updated: true, + score: 9, + user: { + id: 3, + name: 'Bayo Owoade' + } + } + ] + } + } +``` + Or + +```ruby +Status: 404 +{ + error: "The resource you tried to access was not found" } ``` @@ -72,11 +128,52 @@ Response ```ruby Status: 201 { - message: "Successfully added" + id: 4, + text: 'This is such a scam', + date_created: 'Wed, 25TH Nov, 2017 12:00PM', + updated: true, + score: 9, + user: { + id: 3, + name: 'Bayo Owoade' + } } ``` -### PUT /comments/:id +### POST /questions/:id/comments/ + +Request +```ruby + { + auth_token: "90ioji0j0i0i0ik0k0jmj0090jknieu93833r335", + content: "Why in the world do I have to do this?" + } +``` + +Response +```ruby +Status: 201 +{ + id: 4, + text: 'This is such a scam', + date_created: 'Wed, 25TH Nov, 2017 12:00PM', + updated: true, + score: 9, + user: { + id: 3, + name: 'Bayo Owoade' + } +} +``` +OR +```ruby +Status: 404 +{ + error: "Comment body can not be empty!" +} +``` + +### PATCH /answers/:answer_id/comments/:id Request ```ruby @@ -86,12 +183,68 @@ Request } ``` +Response +```ruby + Status: 200 + { + id: 4, + text: 'This is such a scam', + date_created: 'Wed, 25TH Nov, 2017 12:00PM', + updated: true, + score: 9, + user: { + id: 3, + name: 'Bayo Owoade' + } + } +``` +### PATCH /questions/:question_id/comments/:id + +Request +```ruby + { + auth_token: "90ioji0j0i0i0ik0k0jmj0090jknieu93833r335", + content: "What in the world does this mean?" + } +``` + +Response +```ruby + Status: 200 + { + id: 4, + text: 'This is such a scam', + date_created: 'Wed, 25TH Nov, 2017 12:00PM', + updated: true, + score: 9, + user: { + id: 3, + name: 'Bayo Owoade' + } + } +``` +OR +```ruby +Status: 404 +{ + error: "Comment body can not be empty!" +} +``` +### DELETE /answers/:answer_id/comments/:id + +Request +```ruby + { + auth_token: "90ioji0j0i0i0ik0k0jmj0090jknieu93833r335" + } +``` + Response ```ruby Status: 204 ``` -### DELETE /comments/:id +### DELETE /questions/:answer_id/comments/:id Request ```ruby @@ -104,3 +257,11 @@ Response ```ruby Status: 204 ``` + +OR +```ruby +Status: 404 +{ + error: "The operation could not be performed. Please check your request or try again later" +} +``` diff --git a/docs/vote_endpoints.md b/docs/vote_endpoints.md new file mode 100644 index 0000000..b9442cd --- /dev/null +++ b/docs/vote_endpoints.md @@ -0,0 +1,107 @@ +#Votes Resources + +Endpoints | Usage | Public Access +--------- | ----- | ------------- +POST /questions/:question_id/upvote | Increase the upvotes of the given question by one unit | False +POST /questions/:question_id/downvote | Increase the downvotes of the given question by one unit | False +POST /answers/:answer_id/upvote | Increase the upvotes of the given answer by one unit | False +POST /answers/:answer_id/downvote | Increase the downvotes of the given answers by one unit | False +POST /comments/:question_id/upvote | Increase the upvotes of the given comment by one unit | False +POST /comments/:question_id/downvote | Increase the downvotes of the given comments by one unit | False + +### POST /questions/:question_id/upvote/ + +Response +```ruby +Status: 200 +{ + response: 11 +} +``` +#### OR +```ruby +Status: 403 + { + error: "Invalid vote!" + } +``` +### POST /comments/:comment_id/upvote/ + +Response +```ruby +Status: 200 +{ + response: 23 +} +``` +#### OR +```ruby +Status: 403 + { + error: "Invalid vote!" + } +``` +### POST /answers/:answer_id/upvote/ + +Response +```ruby +Status: 200 +{ + response: 0 +} +``` +#### OR +```ruby +Status: 403 + { + error: "Invalid vote!" + } +``` +### POST /questions/:question_id/downvote/ + +Response +```ruby +Status: 200 +{ + response: 2 +} +``` +#### OR +```ruby +Status: 403 + { + error: "Invalid vote!" + } +``` +### POST /comments/:comment_id/downvote/ + +Response +```ruby +Status: 200 +{ + response: -12 +} +``` +#### OR +```ruby +Status: 403 + { + error: "Invalid vote!" + } +``` +### POST /answers/:answer_id/downvote/ + +Response +```ruby +Status: 200 +{ + response: 34 +} +``` +#### OR +```ruby +Status: 403 + { + error: "Invalid vote!" + } +``` From 1bc6fb5458b21397b29fe62ad741fa772286f405 Mon Sep 17 00:00:00 2001 From: Osmond Oscar Date: Thu, 18 Feb 2016 14:21:58 +0100 Subject: [PATCH 4/4] Merge scope contraints for comments and votes routes --- app/controllers/votes_controller.rb | 6 +++--- config/routes.rb | 7 ++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/controllers/votes_controller.rb b/app/controllers/votes_controller.rb index 918b852..6643c15 100644 --- a/app/controllers/votes_controller.rb +++ b/app/controllers/votes_controller.rb @@ -1,20 +1,20 @@ class VotesController < ApplicationController def upvote resource_to_upvote = vote_params[:resource_name].singularize.camelize.constantize - vote = Vote.act_on_vote('plus', resource_to_upvote, vote_params[:id], current_user) + vote = Vote.act_on_vote('plus', resource_to_upvote, vote_params[:resource_id], current_user) render json: { response: vote }, status: 200 unless vote.nil? render json: { error: "Invalid vote!" }, status: 403 if vote.nil? end def downvote resource_to_upvote = vote_params[:resource_name].singularize.camelize.constantize - vote = Vote.act_on_vote('minus', resource_to_upvote, vote_params[:id], current_user) + vote = Vote.act_on_vote('minus', resource_to_upvote, vote_params[:resource_id], current_user) render json: { response: vote }, status: 200 unless vote.nil? render json: { error: "Invalid vote!" }, status: 403 if vote.nil? end private def vote_params - params.permit(:resource_name, :id) + params.permit(:resource_name, :resource_id) end end diff --git a/config/routes.rb b/config/routes.rb index 814c997..8347f19 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,11 +1,8 @@ Rails.application.routes.draw do - scope '/:resource_name/:id', constraints: { resource_name: /(questions|answers|comments)/ } do + scope '/:resource_name/:resource_id', constraints: { resource_name: /(questions|answers|comments)/ } do post '/upvote', to: 'votes#upvote' post '/downvote', to: 'votes#downvote' - end - - scope '/:resource_name/:resource_id', constraints: { resource_name: /(questions|answers)/} do - resources :comments, except: [:new, :edit] + resources :comments, except: [:new, :edit], resource_name: /(?!comments).*/ end post '/validate_token', to: 'tokens#validate'