Commit 0e99daae authored by Patrick Derichs's avatar Patrick Derichs

Use NotesFinder in IssuableActions module

Remove project from NotesFinder constructor

Add project parameter to specs

Also look for methods in private scope

Fix specs to match new NotesFinder constructor
parent 533237a0
...@@ -98,13 +98,12 @@ module IssuableActions ...@@ -98,13 +98,12 @@ module IssuableActions
render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" } render json: { notice: "#{quantity} #{resource_name.pluralize(quantity)} updated" }
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop:disable CodeReuse/ActiveRecord
def discussions def discussions
notes = issuable.discussion_notes notes = NotesFinder.new(current_user, finder_params_for_issuable).execute
.inc_relations_for_view .inc_relations_for_view
.with_notes_filter(notes_filter) .includes(:noteable)
.includes(:noteable) .fresh
.fresh
if notes_filter != UserPreference::NOTES_FILTERS[:only_comments] if notes_filter != UserPreference::NOTES_FILTERS[:only_comments]
notes = ResourceEvents::MergeIntoNotesService.new(issuable, current_user).execute(notes) notes = ResourceEvents::MergeIntoNotesService.new(issuable, current_user).execute(notes)
...@@ -117,7 +116,7 @@ module IssuableActions ...@@ -117,7 +116,7 @@ module IssuableActions
render json: discussion_serializer.represent(discussions, context: self) render json: discussion_serializer.represent(discussions, context: self)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop:enable CodeReuse/ActiveRecord
private private
...@@ -222,4 +221,13 @@ module IssuableActions ...@@ -222,4 +221,13 @@ module IssuableActions
def parent def parent
@project || @group # rubocop:disable Gitlab/ModuleWithInstanceVariables @project || @group # rubocop:disable Gitlab/ModuleWithInstanceVariables
end end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def finder_params_for_issuable
{
target: @issuable,
notes_filter: notes_filter
}.tap { |hash| hash[:project] = project if respond_to?(:project, true) }
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end end
...@@ -243,7 +243,7 @@ module NotesActions ...@@ -243,7 +243,7 @@ module NotesActions
end end
def notes_finder def notes_finder
@notes_finder ||= NotesFinder.new(project, current_user, finder_params) @notes_finder ||= NotesFinder.new(current_user, finder_params)
end end
def note_serializer def note_serializer
......
...@@ -68,7 +68,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -68,7 +68,7 @@ class Projects::NotesController < Projects::ApplicationController
alias_method :awardable, :note alias_method :awardable, :note
def finder_params def finder_params
params.merge(last_fetched_at: last_fetched_at, notes_filter: notes_filter) params.merge(project: project, last_fetched_at: last_fetched_at, notes_filter: notes_filter)
end end
def authorize_admin_note! def authorize_admin_note!
......
...@@ -27,7 +27,9 @@ class Snippets::NotesController < ApplicationController ...@@ -27,7 +27,9 @@ class Snippets::NotesController < ApplicationController
alias_method :noteable, :snippet alias_method :noteable, :snippet
def finder_params def finder_params
params.merge(last_fetched_at: last_fetched_at, target_id: snippet.id, target_type: 'personal_snippet') params.merge(last_fetched_at: last_fetched_at, target_id: snippet.id, target_type: 'personal_snippet').tap do |hash|
hash[:project] = project if respond_to?(:project)
end
end end
def authorize_read_snippet! def authorize_read_snippet!
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
class NotesFinder class NotesFinder
FETCH_OVERLAP = 5.seconds FETCH_OVERLAP = 5.seconds
attr_reader :target_type
# Used to filter Notes # Used to filter Notes
# When used with target_type and target_id this returns notes specifically for the controller # When used with target_type and target_id this returns notes specifically for the controller
# #
...@@ -10,15 +12,17 @@ class NotesFinder ...@@ -10,15 +12,17 @@ class NotesFinder
# current_user - which user check authorizations with # current_user - which user check authorizations with
# project - which project to look for notes on # project - which project to look for notes on
# params: # params:
# target: noteable
# target_type: string # target_type: string
# target_id: integer # target_id: integer
# last_fetched_at: time # last_fetched_at: time
# search: string # search: string
# #
def initialize(project, current_user, params = {}) def initialize(current_user, params = {})
@project = project @project = params[:project]
@current_user = current_user @current_user = current_user
@params = params @params = params.dup
@target_type = @params[:target_type]
end end
def execute def execute
...@@ -32,7 +36,27 @@ class NotesFinder ...@@ -32,7 +36,27 @@ class NotesFinder
def target def target
return @target if defined?(@target) return @target if defined?(@target)
target_type = @params[:target_type] if target_given?
use_explicit_target
else
find_target_by_type_and_ids
end
end
private
def target_given?
@params.key?(:target)
end
def use_explicit_target
@target = @params[:target]
@target_type = @target.class.name.underscore
@target
end
def find_target_by_type_and_ids
target_id = @params[:target_id] target_id = @params[:target_id]
target_iid = @params[:target_iid] target_iid = @params[:target_iid]
...@@ -45,13 +69,11 @@ class NotesFinder ...@@ -45,13 +69,11 @@ class NotesFinder
@project.commit(target_id) @project.commit(target_id)
end end
else else
noteables_for_type_by_id(target_type, target_id, target_iid) noteable_for_type_by_id(target_type, target_id, target_iid)
end end
end end
private def noteable_for_type_by_id(type, id, iid)
def noteables_for_type_by_id(type, id, iid)
query = if id query = if id
{ id: id } { id: id }
else else
...@@ -77,10 +99,6 @@ class NotesFinder ...@@ -77,10 +99,6 @@ class NotesFinder
search(notes) search(notes)
end end
def target_type
@params[:target_type]
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def notes_of_any_type def notes_of_any_type
types = %w(commit issue merge_request snippet) types = %w(commit issue merge_request snippet)
......
...@@ -76,7 +76,7 @@ module API ...@@ -76,7 +76,7 @@ module API
def find_noteable(parent_type, parent_id, noteable_type, noteable_id) def find_noteable(parent_type, parent_id, noteable_type, noteable_id)
params = params_by_noteable_type_and_id(noteable_type, noteable_id) params = params_by_noteable_type_and_id(noteable_type, noteable_id)
noteable = NotesFinder.new(user_project, current_user, params).target noteable = NotesFinder.new(current_user, params.merge(project: user_project)).target
noteable = nil unless can?(current_user, noteable_read_ability_name(noteable), noteable) noteable = nil unless can?(current_user, noteable_read_ability_name(noteable), noteable)
noteable || not_found!(noteable_type) noteable || not_found!(noteable_type)
end end
......
...@@ -108,7 +108,7 @@ module Gitlab ...@@ -108,7 +108,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def notes_finder(type) def notes_finder(type)
NotesFinder.new(project, @current_user, search: query, target_type: type).execute.user.order('updated_at DESC') NotesFinder.new(@current_user, search: query, target_type: type, project: project).execute.user.order('updated_at DESC')
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
# frozen_string_literal: true
require 'spec_helper'
describe IssuableActions do
let(:project) { double('project') }
let(:user) { double('user') }
let(:issuable) { double('issuable') }
let(:finder_params_for_issuable) { {} }
let(:notes_result) { double('notes_result') }
let(:discussion_serializer) { double('discussion_serializer') }
let(:controller) do
klass = Class.new do
attr_reader :current_user, :project, :issuable
def self.before_action(action, params = nil)
end
include IssuableActions
def initialize(issuable, project, user, finder_params)
@issuable = issuable
@project = project
@current_user = user
@finder_params = finder_params
end
def finder_params_for_issuable
@finder_params
end
def params
{
notes_filter: 1
}
end
def prepare_notes_for_rendering(notes)
[]
end
def render(options)
end
end
klass.new(issuable, project, user, finder_params_for_issuable)
end
describe '#discussions' do
before do
allow(user).to receive(:set_notes_filter)
allow(user).to receive(:user_preference)
allow(discussion_serializer).to receive(:represent)
end
it 'instantiates and calls NotesFinder as expected' do
expect(Discussion).to receive(:build_collection).and_return([])
expect(DiscussionSerializer).to receive(:new).and_return(discussion_serializer)
expect(NotesFinder).to receive(:new).with(user, finder_params_for_issuable).and_call_original
expect_any_instance_of(NotesFinder).to receive(:execute).and_return(notes_result)
expect(notes_result).to receive_messages(inc_relations_for_view: notes_result, includes: notes_result, fresh: notes_result)
controller.discussions
end
end
end
...@@ -1260,6 +1260,28 @@ describe Projects::IssuesController do ...@@ -1260,6 +1260,28 @@ describe Projects::IssuesController do
sign_in(user) sign_in(user)
end end
context do
it_behaves_like 'discussions provider' do
let!(:author) { create(:user) }
let!(:project) { create(:project) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:note_on_issue1) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, author: create(:user)) }
let!(:note_on_issue2) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, author: create(:user)) }
let(:requested_iid) { issue.iid }
let(:expected_discussion_count) { 3 }
let(:expected_discussion_ids) do
[
issue.notes.first.discussion_id,
note_on_issue1.discussion_id,
note_on_issue2.discussion_id
]
end
end
end
it 'returns discussion json' do it 'returns discussion json' do
get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issue.iid } get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issue.iid }
......
...@@ -1210,6 +1210,22 @@ describe Projects::MergeRequestsController do ...@@ -1210,6 +1210,22 @@ describe Projects::MergeRequestsController do
end end
end end
end end
context do
it_behaves_like 'discussions provider' do
let!(:author) { create(:user) }
let!(:project) { create(:project) }
let!(:merge_request) { create(:merge_request, source_project: project) }
let!(:mr_note1) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) }
let!(:mr_note2) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) }
let(:requested_iid) { merge_request.iid }
let(:expected_discussion_count) { 2 }
let(:expected_discussion_ids) { [mr_note1.discussion_id, mr_note2.discussion_id] }
end
end
end end
describe 'GET edit' do describe 'GET edit' do
......
...@@ -43,7 +43,7 @@ describe Projects::NotesController do ...@@ -43,7 +43,7 @@ describe Projects::NotesController do
request.headers['X-Last-Fetched-At'] = last_fetched_at request.headers['X-Last-Fetched-At'] = last_fetched_at
expect(NotesFinder).to receive(:new) expect(NotesFinder).to receive(:new)
.with(anything, anything, hash_including(last_fetched_at: last_fetched_at)) .with(anything, hash_including(last_fetched_at: last_fetched_at))
.and_call_original .and_call_original
get :index, params: request_params get :index, params: request_params
......
This diff is collapsed.
{
"type": "object",
"required" : [
"id",
"notes",
"individual_note"
],
"properties" : {
"id": { "type": "string" },
"individual_note": { "type": "boolean" },
"notes": {
"type": "array",
"items": {
"type": "object",
"properties" : {
"id": { "type": "string" },
"type": { "type": ["string", "null"] },
"body": { "type": "string" },
"attachment": { "type": ["string", "null"]},
"award_emoji": { "type": "array" },
"author": {
"type": "object",
"properties": {
"name": { "type": "string" },
"username": { "type": "string" },
"id": { "type": "integer" },
"state": { "type": "string" },
"avatar_url": { "type": "uri" },
"web_url": { "type": "uri" },
"status_tooltip_html": { "type": ["string", "null"] },
"path": { "type": "string" }
},
"additionalProperties": false
},
"created_at": { "type": "date" },
"updated_at": { "type": "date" },
"system": { "type": "boolean" },
"noteable_id": { "type": "integer" },
"noteable_iid": { "type": "integer" },
"noteable_type": { "type": "string" },
"resolved": { "type": "boolean" },
"resolvable": { "type": "boolean" },
"resolved_by": { "type": ["string", "null"] },
"note": { "type": "string" },
"note_html": { "type": "string" },
"current_user": { "type": "object" },
"suggestions": { "type": "array" },
"discussion_id": { "type": "string" },
"emoji_awardable": { "type": "boolean" },
"report_abuse_path": { "type": "string" },
"noteable_note_url": { "type": "string" },
"resolve_path": { "type": "string" },
"resolve_with_issue_path": { "type": "string" },
"cached_markdown_version": { "type": "integer" },
"human_access": { "type": ["string", "null"] },
"toggle_award_path": { "type": "string" },
"path": { "type": "string" }
},
"required": [
"id", "attachment", "author", "created_at", "updated_at",
"system", "noteable_id", "noteable_type"
],
"additionalProperties": false
}
}
}
}
{
"type": "array",
"items": { "$ref": "discussion.json" }
}
# frozen_string_literal: true
require 'spec_helper'
shared_examples 'discussions provider' do
it 'returns the expected discussions' do
get :discussions, params: { namespace_id: project.namespace, project_id: project, id: requested_iid }
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('entities/discussions')
expect(json_response.size).to eq(expected_discussion_count)
expect(json_response.pluck('id')).to eq(expected_discussion_ids)
end
end
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