Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
631d430d
Commit
631d430d
authored
Sep 09, 2021
by
Heinrich Lee Yu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Paginate discussions endpoint
This can be used to progressively load the issuable discussions
parent
b11b5142
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
344 additions
and
19 deletions
+344
-19
app/controllers/concerns/issuable_actions.rb
app/controllers/concerns/issuable_actions.rb
+24
-1
app/helpers/notes_helper.rb
app/helpers/notes_helper.rb
+3
-3
app/models/concerns/noteable.rb
app/models/concerns/noteable.rb
+33
-0
app/models/note.rb
app/models/note.rb
+1
-0
app/services/resource_events/base_synthetic_notes_builder_service.rb
...s/resource_events/base_synthetic_notes_builder_service.rb
+12
-0
app/services/resource_events/synthetic_label_notes_builder_service.rb
.../resource_events/synthetic_label_notes_builder_service.rb
+4
-0
app/services/resource_events/synthetic_milestone_notes_builder_service.rb
...ource_events/synthetic_milestone_notes_builder_service.rb
+4
-0
app/services/resource_events/synthetic_state_notes_builder_service.rb
.../resource_events/synthetic_state_notes_builder_service.rb
+4
-0
app/views/projects/issues/_discussion.html.haml
app/views/projects/issues/_discussion.html.haml
+1
-1
ee/app/helpers/ee/notes_helper.rb
ee/app/helpers/ee/notes_helper.rb
+3
-3
ee/app/models/concerns/ee/noteable.rb
ee/app/models/concerns/ee/noteable.rb
+17
-0
ee/app/services/ee/resource_events/synthetic_iteration_notes_builder_service.rb
...ource_events/synthetic_iteration_notes_builder_service.rb
+4
-0
ee/app/services/ee/resource_events/synthetic_weight_notes_builder_service.rb
...resource_events/synthetic_weight_notes_builder_service.rb
+4
-0
ee/spec/models/concerns/ee/noteable_spec.rb
ee/spec/models/concerns/ee/noteable_spec.rb
+40
-0
ee/spec/services/ee/resource_events/synthetic_iteration_notes_builder_service_spec.rb
..._events/synthetic_iteration_notes_builder_service_spec.rb
+2
-0
ee/spec/services/ee/resource_events/synthetic_weight_notes_builder_service_spec.rb
...rce_events/synthetic_weight_notes_builder_service_spec.rb
+7
-5
lib/api/discussions.rb
lib/api/discussions.rb
+1
-1
spec/models/concerns/noteable_spec.rb
spec/models/concerns/noteable_spec.rb
+64
-0
spec/requests/projects/issues_controller_spec.rb
spec/requests/projects/issues_controller_spec.rb
+71
-0
spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
...urce_events/synthetic_label_notes_builder_service_spec.rb
+7
-5
spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
..._events/synthetic_milestone_notes_builder_service_spec.rb
+2
-0
spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb
...urce_events/synthetic_state_notes_builder_service_spec.rb
+11
-0
spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
...esource_events/synthetic_notes_builder_shared_examples.rb
+25
-0
No files found.
app/controllers/concerns/issuable_actions.rb
View file @
631d430d
...
@@ -149,8 +149,20 @@ module IssuableActions
...
@@ -149,8 +149,20 @@ module IssuableActions
.
includes
(
:noteable
)
.
includes
(
:noteable
)
.
fresh
.
fresh
if
paginated_discussions
paginated_discussions_by_type
=
paginated_discussions
.
records
.
group_by
(
&
:table_name
)
notes
=
if
paginated_discussions_by_type
[
'notes'
].
present?
notes
.
with_discussion_ids
(
paginated_discussions_by_type
[
'notes'
].
map
(
&
:discussion_id
))
else
notes
.
none
end
response
.
headers
[
'X-Next-Page-Cursor'
]
=
paginated_discussions
.
cursor_for_next_page
if
paginated_discussions
.
has_next_page?
end
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
,
paginated_notes:
paginated_discussions_by_type
).
execute
(
notes
)
end
end
notes
=
prepare_notes_for_rendering
(
notes
)
notes
=
prepare_notes_for_rendering
(
notes
)
...
@@ -170,6 +182,17 @@ module IssuableActions
...
@@ -170,6 +182,17 @@ module IssuableActions
private
private
def
paginated_discussions
return
if
params
[
:per_page
].
blank?
return
unless
issuable
.
instance_of?
(
Issue
)
&&
Feature
.
enabled?
(
:paginated_issue_discussions
,
project
,
default_enabled: :yaml
)
strong_memoize
(
:paginated_discussions
)
do
issuable
.
discussion_root_note_ids
(
notes_filter:
notes_filter
)
.
keyset_paginate
(
cursor:
params
[
:cursor
],
per_page:
params
[
:per_page
].
to_i
)
end
end
def
notes_filter
def
notes_filter
strong_memoize
(
:notes_filter
)
do
strong_memoize
(
:notes_filter
)
do
notes_filter_param
=
params
[
:notes_filter
]
&
.
to_i
notes_filter_param
=
params
[
:notes_filter
]
&
.
to_i
...
...
app/helpers/notes_helper.rb
View file @
631d430d
...
@@ -167,11 +167,11 @@ module NotesHelper
...
@@ -167,11 +167,11 @@ module NotesHelper
}
}
end
end
def
discussions_path
(
issuable
)
def
discussions_path
(
issuable
,
**
params
)
if
issuable
.
is_a?
(
Issue
)
if
issuable
.
is_a?
(
Issue
)
discussions_project_issue_path
(
@project
,
issuable
,
format: :json
)
discussions_project_issue_path
(
@project
,
issuable
,
params
.
merge
(
format: :json
)
)
else
else
discussions_project_merge_request_path
(
@project
,
issuable
,
format: :json
)
discussions_project_merge_request_path
(
@project
,
issuable
,
params
.
merge
(
format: :json
)
)
end
end
end
end
...
...
app/models/concerns/noteable.rb
View file @
631d430d
...
@@ -98,6 +98,27 @@ module Noteable
...
@@ -98,6 +98,27 @@ module Noteable
.
order
(
'MIN(created_at), MIN(id)'
)
.
order
(
'MIN(created_at), MIN(id)'
)
end
end
# This does not consider OutOfContextDiscussions in MRs
# where notes from commits are overriden so that they have
# the same discussion_id
def
discussion_root_note_ids
(
notes_filter
:)
relations
=
[]
relations
<<
discussion_notes
.
select
(
"'notes' AS table_name"
,
'discussion_id'
,
'MIN(id) AS id'
,
'MIN(created_at) AS created_at'
).
with_notes_filter
(
notes_filter
)
.
group
(
:discussion_id
)
if
notes_filter
!=
UserPreference
::
NOTES_FILTERS
[
:only_comments
]
relations
+=
synthetic_note_ids_relations
end
Note
.
from_union
(
relations
,
remove_duplicates:
false
).
fresh
end
def
capped_notes_count
(
max
)
def
capped_notes_count
(
max
)
notes
.
limit
(
max
).
count
notes
.
limit
(
max
).
count
end
end
...
@@ -179,6 +200,18 @@ module Noteable
...
@@ -179,6 +200,18 @@ module Noteable
project_email
.
sub
(
'@'
,
"-
#{
iid
}
@"
)
project_email
.
sub
(
'@'
,
"-
#{
iid
}
@"
)
end
end
private
# Synthetic system notes don't have discussion IDs because these are generated dynamically
# in Ruby. These are always root notes anyway so we don't need to group by discussion ID.
def
synthetic_note_ids_relations
[
resource_label_events
.
select
(
"'resource_label_events'"
,
"'NULL'"
,
:id
,
:created_at
),
resource_milestone_events
.
select
(
"'resource_milestone_events'"
,
"'NULL'"
,
:id
,
:created_at
),
resource_state_events
.
select
(
"'resource_state_events'"
,
"'NULL'"
,
:id
,
:created_at
)
]
end
end
end
Noteable
.
extend
(
Noteable
::
ClassMethods
)
Noteable
.
extend
(
Noteable
::
ClassMethods
)
...
...
app/models/note.rb
View file @
631d430d
...
@@ -114,6 +114,7 @@ class Note < ApplicationRecord
...
@@ -114,6 +114,7 @@ class Note < ApplicationRecord
scope
:fresh
,
->
{
order_created_asc
.
with_order_id_asc
}
scope
:fresh
,
->
{
order_created_asc
.
with_order_id_asc
}
scope
:updated_after
,
->
(
time
)
{
where
(
'updated_at > ?'
,
time
)
}
scope
:updated_after
,
->
(
time
)
{
where
(
'updated_at > ?'
,
time
)
}
scope
:with_updated_at
,
->
(
time
)
{
where
(
updated_at:
time
)
}
scope
:with_updated_at
,
->
(
time
)
{
where
(
updated_at:
time
)
}
scope
:with_discussion_ids
,
->
(
discussion_ids
)
{
where
(
discussion_id:
discussion_ids
)
}
scope
:with_suggestions
,
->
{
joins
(
:suggestions
)
}
scope
:with_suggestions
,
->
{
joins
(
:suggestions
)
}
scope
:inc_author
,
->
{
includes
(
:author
)
}
scope
:inc_author
,
->
{
includes
(
:author
)
}
scope
:with_api_entity_associations
,
->
{
preload
(
:note_diff_file
,
:author
)
}
scope
:with_api_entity_associations
,
->
{
preload
(
:note_diff_file
,
:author
)
}
...
...
app/services/resource_events/base_synthetic_notes_builder_service.rb
View file @
631d430d
...
@@ -24,10 +24,18 @@ module ResourceEvents
...
@@ -24,10 +24,18 @@ module ResourceEvents
private
private
def
apply_common_filters
(
events
)
def
apply_common_filters
(
events
)
events
=
apply_pagination
(
events
)
events
=
apply_last_fetched_at
(
events
)
events
=
apply_last_fetched_at
(
events
)
apply_fetch_until
(
events
)
apply_fetch_until
(
events
)
end
end
def
apply_pagination
(
events
)
return
events
if
params
[
:paginated_notes
].
nil?
return
events
.
none
if
params
[
:paginated_notes
][
table_name
].
blank?
events
.
id_in
(
params
[
:paginated_notes
][
table_name
].
map
(
&
:id
))
end
def
apply_last_fetched_at
(
events
)
def
apply_last_fetched_at
(
events
)
return
events
unless
params
[
:last_fetched_at
].
present?
return
events
unless
params
[
:last_fetched_at
].
present?
...
@@ -47,5 +55,9 @@ module ResourceEvents
...
@@ -47,5 +55,9 @@ module ResourceEvents
resource
.
project
||
resource
.
group
resource
.
project
||
resource
.
group
end
end
end
end
def
table_name
raise
NotImplementedError
end
end
end
end
end
app/services/resource_events/synthetic_label_notes_builder_service.rb
View file @
631d430d
...
@@ -23,5 +23,9 @@ module ResourceEvents
...
@@ -23,5 +23,9 @@ module ResourceEvents
events
.
group_by
{
|
event
|
event
.
discussion_id
}
events
.
group_by
{
|
event
|
event
.
discussion_id
}
end
end
def
table_name
'resource_label_events'
end
end
end
end
end
app/services/resource_events/synthetic_milestone_notes_builder_service.rb
View file @
631d430d
...
@@ -21,5 +21,9 @@ module ResourceEvents
...
@@ -21,5 +21,9 @@ module ResourceEvents
events
=
resource
.
resource_milestone_events
.
includes
(
user: :status
)
# rubocop: disable CodeReuse/ActiveRecord
events
=
resource
.
resource_milestone_events
.
includes
(
user: :status
)
# rubocop: disable CodeReuse/ActiveRecord
apply_common_filters
(
events
)
apply_common_filters
(
events
)
end
end
def
table_name
'resource_milestone_events'
end
end
end
end
end
app/services/resource_events/synthetic_state_notes_builder_service.rb
View file @
631d430d
...
@@ -16,5 +16,9 @@ module ResourceEvents
...
@@ -16,5 +16,9 @@ module ResourceEvents
events
=
resource
.
resource_state_events
.
includes
(
user: :status
)
# rubocop: disable CodeReuse/ActiveRecord
events
=
resource
.
resource_state_events
.
includes
(
user: :status
)
# rubocop: disable CodeReuse/ActiveRecord
apply_common_filters
(
events
)
apply_common_filters
(
events
)
end
end
def
table_name
'resource_state_events'
end
end
end
end
end
app/views/projects/issues/_discussion.html.haml
View file @
631d430d
-
add_page_startup_api_call
discussions_path
(
@issue
)
-
add_page_startup_api_call
Feature
.
enabled?
(
:paginated_discussions
,
@project
)
?
discussions_path
(
@issue
,
per_page:
20
)
:
discussions_path
(
@issue
)
-
@gfm_form
=
true
-
@gfm_form
=
true
...
...
ee/app/helpers/ee/notes_helper.rb
View file @
631d430d
...
@@ -13,9 +13,9 @@ module EE
...
@@ -13,9 +13,9 @@ module EE
end
end
override
:discussions_path
override
:discussions_path
def
discussions_path
(
issuable
)
def
discussions_path
(
issuable
,
**
params
)
return
discussions_group_epic_path
(
issuable
.
group
,
issuable
,
format: :json
)
if
issuable
.
is_a?
(
Epic
)
return
discussions_group_epic_path
(
issuable
.
group
,
issuable
,
params
.
merge
(
format: :json
)
)
if
issuable
.
is_a?
(
Epic
)
return
discussions_project_security_vulnerability_path
(
issuable
.
project
,
issuable
,
format: :json
)
if
issuable
.
is_a?
(
Vulnerability
)
return
discussions_project_security_vulnerability_path
(
issuable
.
project
,
issuable
,
params
.
merge
(
format: :json
)
)
if
issuable
.
is_a?
(
Vulnerability
)
super
super
end
end
...
...
ee/app/models/concerns/ee/noteable.rb
View file @
631d430d
...
@@ -24,5 +24,22 @@ module EE
...
@@ -24,5 +24,22 @@ module EE
super
super
end
end
end
end
private
override
:synthetic_note_ids_relations
def
synthetic_note_ids_relations
relations
=
super
if
respond_to?
(
:resource_weight_events
)
relations
<<
resource_weight_events
.
select
(
"'resource_weight_events'"
,
"'NULL'"
,
:id
,
:created_at
)
end
if
respond_to?
(
:resource_iteration_events
)
relations
<<
resource_iteration_events
.
select
(
"'resource_iteration_events'"
,
"'NULL'"
,
:id
,
:created_at
)
end
relations
end
end
end
end
end
ee/app/services/ee/resource_events/synthetic_iteration_notes_builder_service.rb
View file @
631d430d
...
@@ -22,6 +22,10 @@ module EE
...
@@ -22,6 +22,10 @@ module EE
events
=
resource
.
resource_iteration_events
.
includes
(
user: :status
)
# rubocop: disable CodeReuse/ActiveRecord
events
=
resource
.
resource_iteration_events
.
includes
(
user: :status
)
# rubocop: disable CodeReuse/ActiveRecord
apply_common_filters
(
events
)
apply_common_filters
(
events
)
end
end
def
table_name
'resource_iteration_events'
end
end
end
end
end
end
end
ee/app/services/ee/resource_events/synthetic_weight_notes_builder_service.rb
View file @
631d430d
...
@@ -22,6 +22,10 @@ module EE
...
@@ -22,6 +22,10 @@ module EE
events
=
resource
.
resource_weight_events
.
includes
(
user: :status
).
order
(
:id
)
# rubocop: disable CodeReuse/ActiveRecord
events
=
resource
.
resource_weight_events
.
includes
(
user: :status
).
order
(
:id
)
# rubocop: disable CodeReuse/ActiveRecord
apply_common_filters
(
events
)
apply_common_filters
(
events
)
end
end
def
table_name
'resource_weight_events'
end
end
end
end
end
end
end
ee/spec/models/concerns/ee/noteable_spec.rb
View file @
631d430d
...
@@ -14,4 +14,44 @@ RSpec.describe EE::Noteable do
...
@@ -14,4 +14,44 @@ RSpec.describe EE::Noteable do
expect
(
klazz
.
replyable_types
).
to
include
(
"Vulnerability"
)
expect
(
klazz
.
replyable_types
).
to
include
(
"Vulnerability"
)
end
end
end
end
describe
'#discussion_root_note_ids'
do
let_it_be
(
:issue
)
{
create
(
:issue
)
}
let_it_be
(
:weight_event
)
{
create
(
:resource_weight_event
,
issue:
issue
)
}
let_it_be
(
:regular_note
)
{
create
(
:note
,
project:
issue
.
project
,
noteable:
issue
)
}
let_it_be
(
:iteration_event
)
{
create
(
:resource_iteration_event
,
issue:
issue
)
}
it
'includes weight and iteration synthetic notes'
do
discussions
=
issue
.
discussion_root_note_ids
(
notes_filter:
UserPreference
::
NOTES_FILTERS
[
:all_notes
]).
map
do
|
n
|
{
table_name:
n
.
table_name
,
discussion_id:
n
.
discussion_id
,
id:
n
.
id
}
end
expect
(
discussions
).
to
match
([
a_hash_including
(
table_name:
'resource_weight_events'
,
id:
weight_event
.
id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
regular_note
.
discussion_id
),
a_hash_including
(
table_name:
'resource_iteration_events'
,
id:
iteration_event
.
id
)
])
end
it
'filters by comments only'
do
discussions
=
issue
.
discussion_root_note_ids
(
notes_filter:
UserPreference
::
NOTES_FILTERS
[
:only_comments
]).
map
do
|
n
|
{
table_name:
n
.
table_name
,
discussion_id:
n
.
discussion_id
,
id:
n
.
id
}
end
expect
(
discussions
).
to
match
([
a_hash_including
(
table_name:
'notes'
,
discussion_id:
regular_note
.
discussion_id
)
])
end
it
'filters by system notes only'
do
discussions
=
issue
.
discussion_root_note_ids
(
notes_filter:
UserPreference
::
NOTES_FILTERS
[
:only_activity
]).
map
do
|
n
|
{
table_name:
n
.
table_name
,
discussion_id:
n
.
discussion_id
,
id:
n
.
id
}
end
expect
(
discussions
).
to
match
([
a_hash_including
(
table_name:
'resource_weight_events'
,
id:
weight_event
.
id
),
a_hash_including
(
table_name:
'resource_iteration_events'
,
id:
iteration_event
.
id
)
])
end
end
end
end
ee/spec/services/ee/resource_events/synthetic_iteration_notes_builder_service_spec.rb
View file @
631d430d
...
@@ -21,5 +21,7 @@ RSpec.describe EE::ResourceEvents::SyntheticIterationNotesBuilderService do
...
@@ -21,5 +21,7 @@ RSpec.describe EE::ResourceEvents::SyntheticIterationNotesBuilderService do
expect
(
notes
.
size
).
to
eq
(
3
)
expect
(
notes
.
size
).
to
eq
(
3
)
end
end
end
end
it_behaves_like
'filters by paginated notes'
,
:resource_iteration_event
end
end
end
end
ee/spec/services/ee/resource_events/synthetic_weight_notes_builder_service_spec.rb
View file @
631d430d
...
@@ -4,18 +4,20 @@ require 'spec_helper'
...
@@ -4,18 +4,20 @@ require 'spec_helper'
RSpec
.
describe
EE
::
ResourceEvents
::
SyntheticWeightNotesBuilderService
do
RSpec
.
describe
EE
::
ResourceEvents
::
SyntheticWeightNotesBuilderService
do
describe
'#execute'
do
describe
'#execute'
do
let
!
(
:user
)
{
create
(
:user
)
}
let
_it_be
(
:user
)
{
create
(
:user
)
}
let
!
(
:issue
)
{
create
(
:issue
,
author:
user
)
}
let
_it_be
(
:issue
)
{
create
(
:issue
,
author:
user
)
}
let
!
(
:event1
)
{
create
(
:resource_weight_event
,
issue:
issue
)
}
let
_it_be
(
:event1
)
{
create
(
:resource_weight_event
,
issue:
issue
)
}
let
!
(
:event2
)
{
create
(
:resource_weight_event
,
issue:
issue
)
}
let
_it_be
(
:event2
)
{
create
(
:resource_weight_event
,
issue:
issue
)
}
let
!
(
:event3
)
{
create
(
:resource_weight_event
,
issue:
issue
)
}
let
_it_be
(
:event3
)
{
create
(
:resource_weight_event
,
issue:
issue
)
}
it
'returns the expected synthetic notes'
do
it
'returns the expected synthetic notes'
do
notes
=
EE
::
ResourceEvents
::
SyntheticWeightNotesBuilderService
.
new
(
issue
,
user
).
execute
notes
=
EE
::
ResourceEvents
::
SyntheticWeightNotesBuilderService
.
new
(
issue
,
user
).
execute
expect
(
notes
.
size
).
to
eq
(
3
)
expect
(
notes
.
size
).
to
eq
(
3
)
end
end
it_behaves_like
'filters by paginated notes'
,
:resource_weight_event
end
end
end
end
lib/api/discussions.rb
View file @
631d430d
...
@@ -239,7 +239,7 @@ module API
...
@@ -239,7 +239,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def
readable_discussion_notes
(
noteable
,
discussion_ids
)
def
readable_discussion_notes
(
noteable
,
discussion_ids
)
notes
=
noteable
.
notes
notes
=
noteable
.
notes
.
w
here
(
discussion_id:
discussion_ids
)
.
w
ith_discussion_ids
(
discussion_ids
)
.
inc_relations_for_view
.
inc_relations_for_view
.
includes
(
:noteable
)
.
includes
(
:noteable
)
.
fresh
.
fresh
...
...
spec/models/concerns/noteable_spec.rb
View file @
631d430d
...
@@ -77,6 +77,70 @@ RSpec.describe Noteable do
...
@@ -77,6 +77,70 @@ RSpec.describe Noteable do
end
end
end
end
describe
'#discussion_root_note_ids'
do
let!
(
:label_event
)
{
create
(
:resource_label_event
,
merge_request:
subject
)
}
let!
(
:system_note
)
{
create
(
:system_note
,
project:
project
,
noteable:
subject
)
}
let!
(
:milestone_event
)
{
create
(
:resource_milestone_event
,
merge_request:
subject
)
}
let!
(
:state_event
)
{
create
(
:resource_state_event
,
merge_request:
subject
)
}
it
'returns ordered discussion_ids and synthetic note ids'
do
discussions
=
subject
.
discussion_root_note_ids
(
notes_filter:
UserPreference
::
NOTES_FILTERS
[
:all_notes
]).
map
do
|
n
|
{
table_name:
n
.
table_name
,
discussion_id:
n
.
discussion_id
,
id:
n
.
id
}
end
expect
(
discussions
).
to
match
([
a_hash_including
(
table_name:
'notes'
,
discussion_id:
active_diff_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
active_diff_note3
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
outdated_diff_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
discussion_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_diff_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_note2
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_discussion_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_discussion_note3
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
note2
.
discussion_id
),
a_hash_including
(
table_name:
'resource_label_events'
,
id:
label_event
.
id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
system_note
.
discussion_id
),
a_hash_including
(
table_name:
'resource_milestone_events'
,
id:
milestone_event
.
id
),
a_hash_including
(
table_name:
'resource_state_events'
,
id:
state_event
.
id
)
])
end
it
'filters by comments only'
do
discussions
=
subject
.
discussion_root_note_ids
(
notes_filter:
UserPreference
::
NOTES_FILTERS
[
:only_comments
]).
map
do
|
n
|
{
table_name:
n
.
table_name
,
discussion_id:
n
.
discussion_id
,
id:
n
.
id
}
end
expect
(
discussions
).
to
match
([
a_hash_including
(
table_name:
'notes'
,
discussion_id:
active_diff_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
active_diff_note3
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
outdated_diff_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
discussion_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_diff_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_note2
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_discussion_note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
commit_discussion_note3
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
note1
.
discussion_id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
note2
.
discussion_id
)
])
end
it
'filters by system notes only'
do
discussions
=
subject
.
discussion_root_note_ids
(
notes_filter:
UserPreference
::
NOTES_FILTERS
[
:only_activity
]).
map
do
|
n
|
{
table_name:
n
.
table_name
,
discussion_id:
n
.
discussion_id
,
id:
n
.
id
}
end
expect
(
discussions
).
to
match
([
a_hash_including
(
table_name:
'resource_label_events'
,
id:
label_event
.
id
),
a_hash_including
(
table_name:
'notes'
,
discussion_id:
system_note
.
discussion_id
),
a_hash_including
(
table_name:
'resource_milestone_events'
,
id:
milestone_event
.
id
),
a_hash_including
(
table_name:
'resource_state_events'
,
id:
state_event
.
id
)
])
end
end
describe
'#grouped_diff_discussions'
do
describe
'#grouped_diff_discussions'
do
let
(
:grouped_diff_discussions
)
{
subject
.
grouped_diff_discussions
}
let
(
:grouped_diff_discussions
)
{
subject
.
grouped_diff_discussions
}
...
...
spec/requests/projects/issues_controller_spec.rb
0 → 100644
View file @
631d430d
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Projects
::
IssuesController
do
let_it_be
(
:issue
)
{
create
(
:issue
)
}
let_it_be
(
:group
)
{
create
(
:group
)
}
let_it_be
(
:project
)
{
issue
.
project
}
let_it_be
(
:user
)
{
issue
.
author
}
before
do
login_as
(
user
)
end
describe
'GET #discussions'
do
let_it_be
(
:discussion
)
{
create
(
:discussion_note_on_issue
,
noteable:
issue
,
project:
issue
.
project
)
}
let_it_be
(
:discussion_reply
)
{
create
(
:discussion_note_on_issue
,
noteable:
issue
,
project:
issue
.
project
,
in_reply_to:
discussion
)
}
let_it_be
(
:state_event
)
{
create
(
:resource_state_event
,
issue:
issue
)
}
let_it_be
(
:discussion_2
)
{
create
(
:discussion_note_on_issue
,
noteable:
issue
,
project:
issue
.
project
)
}
let_it_be
(
:discussion_3
)
{
create
(
:discussion_note_on_issue
,
noteable:
issue
,
project:
issue
.
project
)
}
context
'pagination'
do
def
get_discussions
(
**
params
)
get
discussions_project_issue_path
(
project
,
issue
,
params:
params
.
merge
(
format: :json
))
end
it
'returns paginated notes and cursor based on per_page param'
do
get_discussions
(
per_page:
2
)
discussions
=
Gitlab
::
Json
.
parse
(
response
.
body
)
notes
=
discussions
.
flat_map
{
|
d
|
d
[
'notes'
]
}
expect
(
discussions
.
count
).
to
eq
(
2
)
expect
(
notes
).
to
match
([
a_hash_including
(
'id'
=>
discussion
.
id
.
to_s
),
a_hash_including
(
'id'
=>
discussion_reply
.
id
.
to_s
),
a_hash_including
(
'type'
=>
'StateNote'
)
])
cursor
=
response
.
header
[
'X-Next-Page-Cursor'
]
expect
(
cursor
).
to
be_present
get_discussions
(
per_page:
1
,
cursor:
cursor
)
discussions
=
Gitlab
::
Json
.
parse
(
response
.
body
)
notes
=
discussions
.
flat_map
{
|
d
|
d
[
'notes'
]
}
expect
(
discussions
.
count
).
to
eq
(
1
)
expect
(
notes
).
to
match
([
a_hash_including
(
'id'
=>
discussion_2
.
id
.
to_s
)
])
end
context
'when paginated_issue_discussions is disabled'
do
before
do
stub_feature_flags
(
paginated_issue_discussions:
false
)
end
it
'returns all discussions and ignores per_page param'
do
get_discussions
(
per_page:
2
)
discussions
=
Gitlab
::
Json
.
parse
(
response
.
body
)
notes
=
discussions
.
flat_map
{
|
d
|
d
[
'notes'
]
}
expect
(
discussions
.
count
).
to
eq
(
4
)
expect
(
notes
.
count
).
to
eq
(
5
)
end
end
end
end
end
spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb
View file @
631d430d
...
@@ -4,18 +4,20 @@ require 'spec_helper'
...
@@ -4,18 +4,20 @@ require 'spec_helper'
RSpec
.
describe
ResourceEvents
::
SyntheticLabelNotesBuilderService
do
RSpec
.
describe
ResourceEvents
::
SyntheticLabelNotesBuilderService
do
describe
'#execute'
do
describe
'#execute'
do
let
!
(
:user
)
{
create
(
:user
)
}
let
_it_be
(
:user
)
{
create
(
:user
)
}
let
!
(
:issue
)
{
create
(
:issue
,
author:
user
)
}
let
_it_be
(
:issue
)
{
create
(
:issue
,
author:
user
)
}
let
!
(
:event1
)
{
create
(
:resource_label_event
,
issue:
issue
)
}
let
_it_be
(
:event1
)
{
create
(
:resource_label_event
,
issue:
issue
)
}
let
!
(
:event2
)
{
create
(
:resource_label_event
,
issue:
issue
)
}
let
_it_be
(
:event2
)
{
create
(
:resource_label_event
,
issue:
issue
)
}
let
!
(
:event3
)
{
create
(
:resource_label_event
,
issue:
issue
)
}
let
_it_be
(
:event3
)
{
create
(
:resource_label_event
,
issue:
issue
)
}
it
'returns the expected synthetic notes'
do
it
'returns the expected synthetic notes'
do
notes
=
ResourceEvents
::
SyntheticLabelNotesBuilderService
.
new
(
issue
,
user
).
execute
notes
=
ResourceEvents
::
SyntheticLabelNotesBuilderService
.
new
(
issue
,
user
).
execute
expect
(
notes
.
size
).
to
eq
(
3
)
expect
(
notes
.
size
).
to
eq
(
3
)
end
end
it_behaves_like
'filters by paginated notes'
,
:resource_label_event
end
end
end
end
spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb
View file @
631d430d
...
@@ -24,5 +24,7 @@ RSpec.describe ResourceEvents::SyntheticMilestoneNotesBuilderService do
...
@@ -24,5 +24,7 @@ RSpec.describe ResourceEvents::SyntheticMilestoneNotesBuilderService do
'removed milestone'
'removed milestone'
])
])
end
end
it_behaves_like
'filters by paginated notes'
,
:resource_milestone_event
end
end
end
end
spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb
0 → 100644
View file @
631d430d
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
ResourceEvents
::
SyntheticStateNotesBuilderService
do
describe
'#execute'
do
let_it_be
(
:user
)
{
create
(
:user
)
}
it_behaves_like
'filters by paginated notes'
,
:resource_state_event
end
end
spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
0 → 100644
View file @
631d430d
# frozen_string_literal: true
RSpec
.
shared_examples
'filters by paginated notes'
do
|
event_type
|
let
(
:event
)
{
create
(
event_type
)
}
# rubocop:disable Rails/SaveBang
before
do
create
(
event_type
,
issue:
event
.
issue
)
end
it
'only returns given notes'
do
paginated_notes
=
{
event_type
.
to_s
.
pluralize
=>
[
double
(
id:
event
.
id
)]
}
notes
=
described_class
.
new
(
event
.
issue
,
user
,
paginated_notes:
paginated_notes
).
execute
expect
(
notes
.
size
).
to
eq
(
1
)
expect
(
notes
.
first
.
event
).
to
eq
(
event
)
end
context
'when paginated notes is empty'
do
it
'does not return any notes'
do
notes
=
described_class
.
new
(
event
.
issue
,
user
,
paginated_notes:
{}).
execute
expect
(
notes
.
size
).
to
eq
(
0
)
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment