From ba3b6a52e1b8b4f278f2161fcbf0171dd3eb44c8 Mon Sep 17 00:00:00 2001 From: Miguel Prada Date: Sun, 30 Jul 2023 19:38:34 -0500 Subject: [PATCH] sort on issues endpoint per sort params --- .../controllers/api/v1/issues_controller.rb | 6 ++- api/app/models/issue.rb | 4 ++ api/app/services/issues_feed_builder.rb | 33 +++++++++++++++ api/spec/models/issue_spec.rb | 24 +++++++++++ api/spec/services/issues_feed_builder_spec.rb | 40 +++++++++++++++++++ 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 api/app/services/issues_feed_builder.rb create mode 100644 api/spec/services/issues_feed_builder_spec.rb diff --git a/api/app/controllers/api/v1/issues_controller.rb b/api/app/controllers/api/v1/issues_controller.rb index 76ee22a..cde18df 100644 --- a/api/app/controllers/api/v1/issues_controller.rb +++ b/api/app/controllers/api/v1/issues_controller.rb @@ -7,7 +7,7 @@ def index return head :bad_request if params[:product_slug].blank? return head :not_found if product.blank? - issues = Issue.includes(:issue_category, :user, :issue_upvotes).where(product:) + issues = IssuesFeedBuilder.new(product:, sort_params:).call render json: ::V1::IssuesBlueprint.render(issues, current_user:) end @@ -61,6 +61,10 @@ def issue_params def validate_identifiers! return head :not_found if issue.blank? end + + def sort_params + params.permit(:sort_by, :sort_direction) + end end end end diff --git a/api/app/models/issue.rb b/api/app/models/issue.rb index b2ba104..444d3e2 100644 --- a/api/app/models/issue.rb +++ b/api/app/models/issue.rb @@ -19,6 +19,10 @@ class Issue < ApplicationRecord validates :detail, presence: true validates :status, presence: true, inclusion: { in: STATUSES.values } + scope :latest_first, -> { order(created_at: :desc) } + scope :sort_by_upvotes, -> (direction) { order(upvotes_count: direction) } + scope :sort_by_comments, -> (direction) { order(comments_count: direction) } + def upvoted_by?(user) issue_upvotes.exists?(user_id: user.id) end diff --git a/api/app/services/issues_feed_builder.rb b/api/app/services/issues_feed_builder.rb new file mode 100644 index 0000000..e439ece --- /dev/null +++ b/api/app/services/issues_feed_builder.rb @@ -0,0 +1,33 @@ +class IssuesFeedBuilder + attr_reader :product, :sort_params, :issues + + def initialize(product:, sort_params:) + @product = product + @sort_params = sort_params + end + + def call + @issues = Issue.includes(:issue_category, :user, :issue_upvotes).latest_first + @issues = filtered_by_product if product.present? + @issues = sorted if sort_params.present? + + issues + end + + private + + def filtered_by_product + issues.where(product:) + end + + def sorted + byebug + if sort_params[:sort_by] == 'upvotes' + issues.sort_by_upvotes(sort_params[:sort_direction] == 'desc' ? :desc : :asc) + elsif sort_params[:sort_by] == 'comments' + issues.sort_by_comments(sort_params[:sort_direction] == 'desc' ? :desc : :asc) + else + raise StandardError, 'Invalid sort_by param' + end + end +end diff --git a/api/spec/models/issue_spec.rb b/api/spec/models/issue_spec.rb index baa39f5..dc941e1 100644 --- a/api/spec/models/issue_spec.rb +++ b/api/spec/models/issue_spec.rb @@ -55,4 +55,28 @@ end end end + + describe '.latest_first' do + subject(:latest_first) { described_class.latest_first } + + let!(:issue_1) { create(:issue, created_at: 1.day.ago) } + let!(:issue_2) { create(:issue, created_at: 2.days.ago) } + + it 'returns issues sorted by created_at desc' do + expect(latest_first).to eq [issue_1, issue_2] + end + end + + describe '.sort_by_upvotes' do + subject(:sort_by_upvotes) { described_class.sort_by_upvotes(direction) } + + let(:direction) { :desc } + + let!(:issue_1) { create(:issue, upvotes_count: 5) } + let!(:issue_2) { create(:issue, upvotes_count: 10) } + + it 'returns issues sorted by upvotes desc' do + expect(sort_by_upvotes).to eq [issue_1, issue_2] + end + end end diff --git a/api/spec/services/issues_feed_builder_spec.rb b/api/spec/services/issues_feed_builder_spec.rb new file mode 100644 index 0000000..1a15d8c --- /dev/null +++ b/api/spec/services/issues_feed_builder_spec.rb @@ -0,0 +1,40 @@ +require 'rails_helper' + +RSpec.describe IssuesFeedBuilder do + subject(:service) { described_class.new(product:, sort_params:) } + + let(:product) { create(:product) } + + describe '#call' do + subject(:result) { service.call } + + before do + create(:issue, upvotes_count: 10, comments_count: 5, product: product) + create(:issue, upvotes_count: 5, comments_count: 10, product: product) + end + + context 'when sort params are not present' do + let(:sort_params) { nil } + + it 'returns issues sorted by created_at desc' do + expect(result.pluck(:created_at)).to eq result.pluck(:created_at).sort.reverse + end + end + + context 'when sorting by upvotes' do + let(:sort_params) { { sort_by: 'upvotes', sort_direction: 'desc' } } + + it 'returns issues sorted by upvotes desc' do + expect(result.pluck(:upvotes_count)).to eq [10, 5] + end + end + + context 'when sorting by comments' do + let(:sort_params) { { sort_by: 'comments', sort_direction: 'desc' } } + + it 'returns issues sorted by comments desc' do + expect(result.pluck(:comments_count)).to eq [10, 5] + end + end + end +end