Introduce endpoint to retrieve a summary of groups and invite attendance #122
| @ -2,7 +2,6 @@ | |||||||
| 
 | 
 | ||||||
| class GroupsController < ApplicationController | class GroupsController < ApplicationController | ||||||
|   def index |   def index | ||||||
|     roots = Group.where(parent_id: nil) |     render json: Groups::SummaryQuery.new.call.as_json | ||||||
|     render jsonapi: roots, include: [children: [children: [:children]]] |  | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  | |||||||
| @ -30,6 +30,8 @@ class Group < ApplicationRecord | |||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def set_color |   def set_color | ||||||
|  |     return if color.present? | ||||||
|  | 
 | ||||||
|     new_color = "##{SecureRandom.hex(3)}".paint |     new_color = "##{SecureRandom.hex(3)}".paint | ||||||
|     new_color = new_color.lighten(30) if new_color.dark? |     new_color = new_color.lighten(30) if new_color.dark? | ||||||
|     self.color = new_color |     self.color = new_color | ||||||
|  | |||||||
							
								
								
									
										29
									
								
								app/queries/groups/summary_query.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/queries/groups/summary_query.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | module Groups | ||||||
|  |   class SummaryQuery | ||||||
|  |     def call | ||||||
|  |       ActiveRecord::Base.connection.execute(query).to_a | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     private | ||||||
|  | 
 | ||||||
|  |     def query | ||||||
|  |       <<~SQL.squish | ||||||
|  |         SELECT#{' '} | ||||||
|  |           groups.id, | ||||||
|  |           groups.name, | ||||||
|  |           groups.icon, | ||||||
|  |           groups.parent_id, | ||||||
|  |           groups.color, | ||||||
|  |           count(*) filter (where status IS NOT NULL) as total, | ||||||
|  |           count(*) filter (where status = 0) as considered, | ||||||
|  |           count(*) filter (where status = 10) as invited, | ||||||
|  |           count(*) filter (where status = 20) as confirmed, | ||||||
|  |           count(*) filter (where status = 30) as declined, | ||||||
|  |           count(*) filter (where status = 40) as tentative | ||||||
|  |         FROM groups | ||||||
|  |         LEFT JOIN guests on groups.id = guests.group_id | ||||||
|  |         GROUP BY groups.id | ||||||
|  |       SQL | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										96
									
								
								spec/queries/groups/summary_query_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								spec/queries/groups/summary_query_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | |||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | module Groups | ||||||
|  |   RSpec.describe SummaryQuery do | ||||||
|  |     describe '#call' do | ||||||
|  |       subject { described_class.new.call } | ||||||
|  | 
 | ||||||
|  |       context 'when there are no groups' do | ||||||
|  |         it { is_expected.to eq([]) } | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       context 'when groups are defined' do | ||||||
|  |         let!(:parent) { create(:group, name: 'Friends', icon: 'icon-1', color: '#FF0000') } | ||||||
|  |         let!(:child) { create(:group, name: 'Family', icon: 'icon-2', color: '#00FF00', parent:) } | ||||||
|  | 
 | ||||||
|  |         context 'when there are no guests' do | ||||||
|  |           it 'returns the summary of groups' do | ||||||
|  |             is_expected.to contain_exactly( | ||||||
|  |               { 'id' => parent.id, | ||||||
|  |                 'name' => 'Friends', | ||||||
|  |                 'icon' => 'icon-1', | ||||||
|  |                 'parent_id' => nil, | ||||||
|  |                 'color' => '#FF0000', | ||||||
|  |                 'total' => 0, | ||||||
|  |                 'considered' => 0, | ||||||
|  |                 'invited' => 0, | ||||||
|  |                 'confirmed' => 0, | ||||||
|  |                 'declined' => 0, | ||||||
|  |                 'tentative' => 0 }, | ||||||
|  |               { 'id' => child.id, | ||||||
|  |                 'name' => 'Family', | ||||||
|  |                 'icon' => 'icon-2', | ||||||
|  |                 'parent_id' => parent.id, | ||||||
|  |                 'color' => '#00FF00', | ||||||
|  |                 'total' => 0, | ||||||
|  |                 'considered' => 0, | ||||||
|  |                 'invited' => 0, | ||||||
|  |                 'confirmed' => 0, | ||||||
|  |                 'declined' => 0, | ||||||
|  |                 'tentative' => 0 } | ||||||
|  |             ) | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         context 'when there are guests' do | ||||||
|  |           before do | ||||||
|  |             # Parent group | ||||||
|  |             create_list(:guest, 2, group: parent, status: :considered) | ||||||
|  |             create_list(:guest, 3, group: parent, status: :invited) | ||||||
|  |             create_list(:guest, 4, group: parent, status: :confirmed) | ||||||
|  |             create_list(:guest, 5, group: parent, status: :declined) | ||||||
|  |             create_list(:guest, 6, group: parent, status: :tentative) | ||||||
|  | 
 | ||||||
|  |             # Child group | ||||||
|  |             create_list(:guest, 7, group: child, status: :considered) | ||||||
|  |             create_list(:guest, 8, group: child, status: :invited) | ||||||
|  |             create_list(:guest, 9, group: child, status: :confirmed) | ||||||
|  |             create_list(:guest, 10, group: child, status: :declined) | ||||||
|  |             create_list(:guest, 11, group: child, status: :tentative) | ||||||
|  |           end | ||||||
|  | 
 | ||||||
|  |           it 'returns the summary of groups' do | ||||||
|  |             is_expected.to contain_exactly( | ||||||
|  |               { | ||||||
|  |                 'id' => parent.id, | ||||||
|  |                 'name' => 'Friends', | ||||||
|  |                 'icon' => 'icon-1', | ||||||
|  |                 'parent_id' => nil, | ||||||
|  |                 'color' => '#FF0000', | ||||||
|  |                 'total' => 20, | ||||||
|  |                 'considered' => 2, | ||||||
|  |                 'invited' => 3, | ||||||
|  |                 'confirmed' => 4, | ||||||
|  |                 'declined' => 5, | ||||||
|  |                 'tentative' => 6 | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 'id' => child.id, | ||||||
|  |                 'name' => 'Family', | ||||||
|  |                 'icon' => 'icon-2', | ||||||
|  |                 'parent_id' => parent.id, | ||||||
|  |                 'color' => '#00FF00', | ||||||
|  |                 'total' => 45, | ||||||
|  |                 'considered' => 7, | ||||||
|  |                 'invited' => 8, | ||||||
|  |                 'confirmed' => 9, | ||||||
|  |                 'declined' => 10, | ||||||
|  |                 'tentative' => 11 | ||||||
|  |               } | ||||||
|  |             ) | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user