Commit 1c7aecc8 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'refacoring/selectors' into 'master'

Refacoring: Finders

Services -> change data
Finders -> select data
parents 0f473674 345b3d4b
...@@ -53,13 +53,13 @@ class DashboardController < ApplicationController ...@@ -53,13 +53,13 @@ class DashboardController < ApplicationController
end end
def merge_requests def merge_requests
@merge_requests = FilteringService.new.execute(MergeRequest, current_user, params) @merge_requests = MergeRequestsFinder.new.execute(current_user, params)
@merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
end end
def issues def issues
@issues = FilteringService.new.execute(Issue, current_user, params) @issues = IssuesFinder.new.execute(current_user, params)
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
......
...@@ -47,13 +47,13 @@ class GroupsController < ApplicationController ...@@ -47,13 +47,13 @@ class GroupsController < ApplicationController
end end
def merge_requests def merge_requests
@merge_requests = FilteringService.new.execute(MergeRequest, current_user, params) @merge_requests = MergeRequestsFinder.new.execute(current_user, params)
@merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
end end
def issues def issues
@issues = FilteringService.new.execute(Issue, current_user, params) @issues = IssuesFinder.new.execute(current_user, params)
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
...@@ -100,7 +100,7 @@ class GroupsController < ApplicationController ...@@ -100,7 +100,7 @@ class GroupsController < ApplicationController
end end
def projects def projects
@projects ||= Projects::CollectService.new.execute(current_user, group: group) @projects ||= ProjectsFinder.new.execute(current_user, group: group)
end end
def project_ids def project_ids
......
...@@ -121,7 +121,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -121,7 +121,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issues_filtered def issues_filtered
params[:scope] = 'all' if params[:scope].blank? params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank? params[:state] = 'opened' if params[:state].blank?
@issues = FilteringService.new.execute(Issue, current_user, params.merge(project_id: @project.id)) @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
end end
# Since iids are implemented only in 6.1 # Since iids are implemented only in 6.1
......
...@@ -21,7 +21,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -21,7 +21,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:scope] = 'all' if params[:scope].blank? params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank? params[:state] = 'opened' if params[:state].blank?
@merge_requests = FilteringService.new.execute(MergeRequest, current_user, params.merge(project_id: @project.id)) @merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
@merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.page(params[:page]).per(20)
@sort = params[:sort].humanize @sort = params[:sort].humanize
......
...@@ -5,7 +5,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -5,7 +5,7 @@ class Projects::NotesController < Projects::ApplicationController
before_filter :authorize_admin_note!, only: [:update, :destroy] before_filter :authorize_admin_note!, only: [:update, :destroy]
def index def index
@notes = Notes::LoadService.new(project, current_user, params).execute @notes = NotesFinder.new.execute(project, current_user, params)
notes_json = { notes: [] } notes_json = { notes: [] }
......
# Finders
This type of classes responsible for collectiong items based on different conditions.
To prevent lookup methods in models like this:
```ruby
class Project
def issues_for_user_filtered_by(user, filter)
# A lot of logic not related to project model itself
end
end
issues = project.issues_for_user_filtered_by(user, params)
```
Better use this:
```ruby
issues = IssuesFinder.new.execute(project, user, filter)
```
It will help keep models thiner
# FilteringService class # BaseFinder
# #
# Used to filter Issues and MergeRequests collections by set of params # Used to filter Issues and MergeRequests collections by set of params
# #
...@@ -16,11 +16,10 @@ ...@@ -16,11 +16,10 @@
# label_name: string # label_name: string
# sort: string # sort: string
# #
class FilteringService class BaseFinder
attr_accessor :klass, :current_user, :params attr_accessor :current_user, :params
def execute(klass, current_user, params) def execute(current_user, params)
@klass = klass
@current_user = current_user @current_user = current_user
@params = params @params = params
......
# Finders::Issues class
#
# Used to filter Issues collections by set of params
#
# Arguments:
# current_user - which user use
# params:
# scope: 'created-by-me' or 'assigned-to-me' or 'all'
# state: 'open' or 'closed' or 'all'
# group_id: integer
# project_id: integer
# milestone_id: integer
# assignee_id: integer
# search: string
# label_name: string
# sort: string
#
class IssuesFinder < BaseFinder
def klass
Issue
end
end
# Finders::MergeRequest class
#
# Used to filter MergeRequests collections by set of params
#
# Arguments:
# current_user - which user use
# params:
# scope: 'created-by-me' or 'assigned-to-me' or 'all'
# state: 'open' or 'closed' or 'all'
# group_id: integer
# project_id: integer
# milestone_id: integer
# assignee_id: integer
# search: string
# label_name: string
# sort: string
#
class MergeRequestsFinder < BaseFinder
def klass
MergeRequest
end
end
class NotesFinder
def execute(project, current_user, params)
target_type = params[:target_type]
target_id = params[:target_id]
case target_type
when "commit"
project.notes.for_commit_id(target_id).not_inline.fresh
when "issue"
project.issues.find(target_id).notes.inc_author.fresh
when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh
when "snippet"
project.snippets.find(target_id).notes.fresh
end
end
end
class ProjectsFinder
def execute(current_user, options)
group = options[:group]
if group
group_projects(current_user, group)
else
all_projects(current_user)
end
end
private
def group_projects(current_user, group)
if current_user
if group.users.include?(current_user)
# User is group member
#
# Return ALL group projects
group.projects
else
projects_members = UsersProject.where(
project_id: group.projects,
user_id: current_user
)
if projects_members.any?
# User is a project member
#
# Return only:
# public projects
# internal projects
# joined projects
#
group.projects.where(
"projects.id IN (?) OR projects.visibility_level IN (?)",
projects_members.pluck(:project_id),
Project.public_and_internal_levels
)
else
# User has no access to group or group projects
#
# Return only:
# public projects
# internal projects
#
group.projects.public_and_internal_only
end
end
else
# Not authenticated
#
# Return only:
# public projects
group.projects.public_only
end
end
def all_projects
# TODO: implement
raise 'Not implemented yet'
end
end
...@@ -184,7 +184,7 @@ class Ability ...@@ -184,7 +184,7 @@ class Ability
def group_abilities user, group def group_abilities user, group
rules = [] rules = []
if user.admin? || group.users.include?(user) || Projects::CollectService.new.execute(user, group: group).any? if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
rules << :read_group rules << :read_group
end end
......
module Notes
class LoadService < BaseService
def execute
target_type = params[:target_type]
target_id = params[:target_id]
@notes = case target_type
when "commit"
project.notes.for_commit_id(target_id).not_inline.fresh
when "issue"
project.issues.find(target_id).notes.inc_author.fresh
when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh
when "snippet"
project.snippets.find(target_id).notes.fresh
end
end
end
end
# Projects::CollectService class
#
# Used to collect projects user has access to
#
module Projects
class CollectService
def execute(current_user, options)
group = options[:group]
if group
group_projects(current_user, group)
else
all_projects(current_user)
end
end
private
def group_projects(current_user, group)
if current_user
if group.users.include?(current_user)
# User is group member
#
# Return ALL group projects
group.projects
else
projects_members = UsersProject.where(
project_id: group.projects,
user_id: current_user
)
if projects_members.any?
# User is a project member
#
# Return only:
# public projects
# internal projects
# joined projects
#
group.projects.where(
"projects.id IN (?) OR projects.visibility_level IN (?)",
projects_members.pluck(:project_id),
Project.public_and_internal_levels
)
else
# User has no access to group or group projects
#
# Return only:
# public projects
# internal projects
#
group.projects.public_and_internal_only
end
end
else
# Not authenticated
#
# Return only:
# public projects
group.projects.public_only
end
end
end
def all_projects
# TODO: implement
raise 'Not implemented yet'
end
end
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
# -- all .rb files in that directory are automatically loaded. # -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable. # Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/models/concerns #{config.root}/app/models/project_services) config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/finders #{config.root}/app/models/concerns #{config.root}/app/models/project_services)
# Only load the plugins named here, in the order given (default is alphabetical). # Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named. # :all can be used as a placeholder for all plugins not explicitly named.
......
require 'spec_helper' require 'spec_helper'
describe FilteringService do describe IssuesFinder do
let(:user) { create :user } let(:user) { create :user }
let(:user2) { create :user } let(:user2) { create :user }
let(:project1) { create(:project) } let(:project1) { create(:project) }
let(:project2) { create(:project) } let(:project2) { create(:project) }
let(:merge_request1) { create(:merge_request, author: user, source_project: project1, target_project: project2) }
let(:merge_request2) { create(:merge_request, author: user, source_project: project2, target_project: project1) }
let(:merge_request3) { create(:merge_request, author: user, source_project: project2, target_project: project2) }
let(:issue1) { create(:issue, assignee: user, project: project1) } let(:issue1) { create(:issue, assignee: user, project: project1) }
let(:issue2) { create(:issue, assignee: user, project: project2) } let(:issue2) { create(:issue, assignee: user, project: project2) }
let(:issue3) { create(:issue, assignee: user2, project: project2) } let(:issue3) { create(:issue, assignee: user2, project: project2) }
...@@ -18,27 +15,7 @@ describe FilteringService do ...@@ -18,27 +15,7 @@ describe FilteringService do
project2.team << [user2, :developer] project2.team << [user2, :developer]
end end
describe 'merge requests' do describe :execute do
before :each do
merge_request1
merge_request2
merge_request3
end
it 'should filter by scope' do
params = { scope: 'authored', state: 'opened' }
merge_requests = FilteringService.new.execute(MergeRequest, user, params)
merge_requests.size.should == 3
end
it 'should filter by project' do
params = { project_id: project1.id, scope: 'authored', state: 'opened' }
merge_requests = FilteringService.new.execute(MergeRequest, user, params)
merge_requests.size.should == 1
end
end
describe 'issues' do
before :each do before :each do
issue1 issue1
issue2 issue2
...@@ -47,31 +24,31 @@ describe FilteringService do ...@@ -47,31 +24,31 @@ describe FilteringService do
it 'should filter by all' do it 'should filter by all' do
params = { scope: "all", state: 'opened' } params = { scope: "all", state: 'opened' }
issues = FilteringService.new.execute(Issue, user, params) issues = IssuesFinder.new.execute(user, params)
issues.size.should == 3 issues.size.should == 3
end end
it 'should filter by assignee' do it 'should filter by assignee' do
params = { scope: "assigned-to-me", state: 'opened' } params = { scope: "assigned-to-me", state: 'opened' }
issues = FilteringService.new.execute(Issue, user, params) issues = IssuesFinder.new.execute(user, params)
issues.size.should == 2 issues.size.should == 2
end end
it 'should filter by project' do it 'should filter by project' do
params = { scope: "assigned-to-me", state: 'opened', project_id: project1.id } params = { scope: "assigned-to-me", state: 'opened', project_id: project1.id }
issues = FilteringService.new.execute(Issue, user, params) issues = IssuesFinder.new.execute(user, params)
issues.size.should == 1 issues.size.should == 1
end end
it 'should be empty for unauthorized user' do it 'should be empty for unauthorized user' do
params = { scope: "all", state: 'opened' } params = { scope: "all", state: 'opened' }
issues = FilteringService.new.execute(Issue, nil, params) issues = IssuesFinder.new.execute(nil, params)
issues.size.should be_zero issues.size.should be_zero
end end
it 'should not include unauthorized issues' do it 'should not include unauthorized issues' do
params = { scope: "all", state: 'opened' } params = { scope: "all", state: 'opened' }
issues = FilteringService.new.execute(Issue, user2, params) issues = IssuesFinder.new.execute(user2, params)
issues.size.should == 2 issues.size.should == 2
issues.should_not include(issue1) issues.should_not include(issue1)
issues.should include(issue2) issues.should include(issue2)
......
require 'spec_helper'
describe MergeRequestsFinder do
let(:user) { create :user }
let(:user2) { create :user }
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:merge_request1) { create(:merge_request, author: user, source_project: project1, target_project: project2) }
let(:merge_request2) { create(:merge_request, author: user, source_project: project2, target_project: project1) }
let(:merge_request3) { create(:merge_request, author: user, source_project: project2, target_project: project2) }
before do
project1.team << [user, :master]
project2.team << [user, :developer]
project2.team << [user2, :developer]
end
describe :execute do
before :each do
merge_request1
merge_request2
merge_request3
end
it 'should filter by scope' do
params = { scope: 'authored', state: 'opened' }
merge_requests = MergeRequestsFinder.new.execute(user, params)
merge_requests.size.should == 3
end
it 'should filter by project' do
params = { project_id: project1.id, scope: 'authored', state: 'opened' }
merge_requests = MergeRequestsFinder.new.execute(user, params)
merge_requests.size.should == 1
end
end
end
require 'spec_helper' require 'spec_helper'
describe Projects::CollectService do describe ProjectsFinder do
let(:user) { create :user } let(:user) { create :user }
let(:group) { create :group } let(:group) { create :group }
...@@ -10,7 +10,7 @@ describe Projects::CollectService do ...@@ -10,7 +10,7 @@ describe Projects::CollectService do
let(:project4) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) } let(:project4) { create(:empty_project, group: group, visibility_level: Project::PRIVATE) }
context 'non authenticated' do context 'non authenticated' do
subject { Projects::CollectService.new.execute(nil, group: group) } subject { ProjectsFinder.new.execute(nil, group: group) }
it { should include(project1) } it { should include(project1) }
it { should_not include(project2) } it { should_not include(project2) }
...@@ -19,7 +19,7 @@ describe Projects::CollectService do ...@@ -19,7 +19,7 @@ describe Projects::CollectService do
end end
context 'authenticated' do context 'authenticated' do
subject { Projects::CollectService.new.execute(user, group: group) } subject { ProjectsFinder.new.execute(user, group: group) }
it { should include(project1) } it { should include(project1) }
it { should include(project2) } it { should include(project2) }
...@@ -30,7 +30,7 @@ describe Projects::CollectService do ...@@ -30,7 +30,7 @@ describe Projects::CollectService do
context 'authenticated, project member' do context 'authenticated, project member' do
before { project3.team << [user, :developer] } before { project3.team << [user, :developer] }
subject { Projects::CollectService.new.execute(user, group: group) } subject { ProjectsFinder.new.execute(user, group: group) }
it { should include(project1) } it { should include(project1) }
it { should include(project2) } it { should include(project2) }
...@@ -41,7 +41,7 @@ describe Projects::CollectService do ...@@ -41,7 +41,7 @@ describe Projects::CollectService do
context 'authenticated, group member' do context 'authenticated, group member' do
before { group.add_user(user, Gitlab::Access::DEVELOPER) } before { group.add_user(user, Gitlab::Access::DEVELOPER) }
subject { Projects::CollectService.new.execute(user, group: group) } subject { ProjectsFinder.new.execute(user, group: group) }
it { should include(project1) } it { should include(project1) }
it { should include(project2) } it { should include(project2) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment