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
01443314
Commit
01443314
authored
Feb 15, 2022
by
Eugenia Grieff
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Extract common model code to a concern
Extract commond specs code to shared examples
parent
f2a49bb2
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
182 additions
and
164 deletions
+182
-164
app/models/concerns/issuable_link.rb
app/models/concerns/issuable_link.rb
+55
-0
app/models/issue_link.rb
app/models/issue_link.rb
+3
-32
ee/app/models/epic/related_epic_link.rb
ee/app/models/epic/related_epic_link.rb
+4
-26
ee/spec/models/epic/related_epic_link_spec.rb
ee/spec/models/epic/related_epic_link_spec.rb
+5
-53
spec/models/concerns/issuable_link_spec.rb
spec/models/concerns/issuable_link_spec.rb
+45
-0
spec/models/issue_link_spec.rb
spec/models/issue_link_spec.rb
+5
-53
spec/support/shared_examples/models/issuable_link_shared_examples.rb
...t/shared_examples/models/issuable_link_shared_examples.rb
+65
-0
No files found.
app/models/concerns/issuable_link.rb
0 → 100644
View file @
01443314
# frozen_string_literal: true
# == IssuableLink concern
#
# Contains common functionality shared between related Issues and related Epics
#
# Used by IssueLink, Epic::RelatedEpicLink
#
module
IssuableLink
extend
ActiveSupport
::
Concern
TYPE_RELATES_TO
=
'relates_to'
TYPE_BLOCKS
=
'blocks'
# we don't store is_blocked_by in the db but need it for displaying the relation
# from the target
TYPE_IS_BLOCKED_BY
=
'is_blocked_by'
class_methods
do
def
inverse_link_type
(
type
)
type
end
end
included
do
validates
:source
,
presence:
true
validates
:target
,
presence:
true
validates
:source
,
uniqueness:
{
scope: :target_id
,
message:
'is already related'
}
validate
:check_self_relation
validate
:check_opposite_relation
enum
link_type:
{
TYPE_RELATES_TO
=>
0
,
TYPE_BLOCKS
=>
1
}
private
def
check_self_relation
return
unless
source
&&
target
if
source
==
target
errors
.
add
(
:source
,
'cannot be related to itself'
)
end
end
def
check_opposite_relation
return
unless
source
&&
target
if
self
.
class
.
base_class
.
find_by
(
source:
target
,
target:
source
)
errors
.
add
(
:source
,
"is already related to this
#{
issuable_type
}
"
)
end
end
def
issuable_type
raise
NotImplementedError
end
end
end
app/models/issue_link.rb
View file @
01443314
...
...
@@ -2,47 +2,18 @@
class
IssueLink
<
ApplicationRecord
include
FromUnion
include
IssuableLink
belongs_to
:source
,
class_name:
'Issue'
belongs_to
:target
,
class_name:
'Issue'
validates
:source
,
presence:
true
validates
:target
,
presence:
true
validates
:source
,
uniqueness:
{
scope: :target_id
,
message:
'is already related'
}
validate
:check_self_relation
validate
:check_opposite_relation
scope
:for_source_issue
,
->
(
issue
)
{
where
(
source_id:
issue
.
id
)
}
scope
:for_target_issue
,
->
(
issue
)
{
where
(
target_id:
issue
.
id
)
}
TYPE_RELATES_TO
=
'relates_to'
TYPE_BLOCKS
=
'blocks'
# we don't store is_blocked_by in the db but need it for displaying the relation
# from the target (used in IssueLink.inverse_link_type)
TYPE_IS_BLOCKED_BY
=
'is_blocked_by'
enum
link_type:
{
TYPE_RELATES_TO
=>
0
,
TYPE_BLOCKS
=>
1
}
def
self
.
inverse_link_type
(
type
)
type
end
private
def
check_self_relation
return
unless
source
&&
target
if
source
==
target
errors
.
add
(
:source
,
'cannot be related to itself'
)
end
end
def
check_opposite_relation
return
unless
source
&&
target
if
IssueLink
.
find_by
(
source:
target
,
target:
source
)
errors
.
add
(
:source
,
'is already related to this issue'
)
end
def
issuable_type
:issue
end
end
...
...
ee/app/models/epic/related_epic_link.rb
View file @
01443314
# frozen_string_literal: true
class
Epic::RelatedEpicLink
<
ApplicationRecord
self
.
table_name
=
'related_epic_links'
include
IssuableLink
belongs_to
:source
,
class_name:
'Epic'
belongs_to
:target
,
class_name:
'Epic'
validates
:source
,
presence:
true
validates
:target
,
presence:
true
validates
:source
,
uniqueness:
{
scope: :target_id
,
message:
'is already related'
}
validate
:check_self_relation
validate
:check_opposite_relation
self
.
table_name
=
'related_epic_links'
scope
:for_source_epic
,
->
(
epic
)
{
where
(
source_id:
epic
.
id
)
}
scope
:for_target_epic
,
->
(
epic
)
{
where
(
target_id:
epic
.
id
)
}
TYPE_RELATES_TO
=
'relates_to'
TYPE_BLOCKS
=
'blocks'
TYPE_IS_BLOCKED_BY
=
'is_blocked_by'
enum
link_type:
{
TYPE_RELATES_TO
=>
0
,
TYPE_BLOCKS
=>
1
}
private
def
check_self_relation
return
unless
source
&&
target
if
source
==
target
errors
.
add
(
:source
,
'cannot be related to itself'
)
end
end
def
check_opposite_relation
return
unless
source
&&
target
if
Epic
::
RelatedEpicLink
.
find_by
(
source:
target
,
target:
source
)
errors
.
add
(
:source
,
'is already related to this epic'
)
end
def
issuable_type
:epic
end
end
ee/spec/models/epic/related_epic_link_spec.rb
View file @
01443314
...
...
@@ -3,59 +3,11 @@
require
'spec_helper'
RSpec
.
describe
Epic
::
RelatedEpicLink
do
describe
'Associations'
do
it
{
is_expected
.
to
belong_to
(
:source
).
class_name
(
'Epic'
)
}
it
{
is_expected
.
to
belong_to
(
:target
).
class_name
(
'Epic'
)
}
end
describe
'link_type'
do
it
{
is_expected
.
to
define_enum_for
(
:link_type
).
with_values
(
relates_to:
0
,
blocks:
1
)
}
it
'provides the "related" as default link_type'
do
expect
(
create
(
:related_epic_link
).
link_type
).
to
eq
'relates_to'
end
end
describe
'Validation'
do
subject
{
create
:related_epic_link
}
it
{
is_expected
.
to
validate_presence_of
(
:source
)
}
it
{
is_expected
.
to
validate_presence_of
(
:target
)
}
it
do
is_expected
.
to
validate_uniqueness_of
(
:source
)
.
scoped_to
(
:target_id
)
.
with_message
(
/already related/
)
end
it
'is not valid if an opposite link already exists'
do
related_epic_link
=
build
(
:related_epic_link
,
source:
subject
.
target
,
target:
subject
.
source
)
expect
(
related_epic_link
).
to
be_invalid
expect
(
related_epic_link
.
errors
[
:source
]).
to
include
(
'is already related to this epic'
)
end
context
'when it relates to itself'
do
let
(
:epic
)
{
create
:epic
}
context
'cannot be validated'
do
it
'does not invalidate object with self relation error'
do
related_epic_link
=
build
(
:related_epic_link
,
source:
epic
,
target:
nil
)
related_epic_link
.
valid?
expect
(
related_epic_link
.
errors
[
:source
]).
to
be_empty
end
end
context
'can be invalidated'
do
it
'invalidates object'
do
related_epic_link
=
build
(
:related_epic_link
,
source:
epic
,
target:
epic
)
expect
(
related_epic_link
).
to
be_invalid
expect
(
related_epic_link
.
errors
[
:source
]).
to
include
(
'cannot be related to itself'
)
end
end
end
it_behaves_like
'issuable link'
do
let_it_be_with_reload
(
:issuable_link
)
{
create
(
:related_epic_link
)
}
let_it_be
(
:issuable
)
{
create
(
:epic
)
}
let
(
:issuable_class
)
{
'Epic'
}
let
(
:issuable_link_factory
)
{
:related_epic_link
}
end
describe
'Scopes'
do
...
...
spec/models/concerns/issuable_link_spec.rb
0 → 100644
View file @
01443314
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
IssuableLink
do
let
(
:test_class
)
do
Class
.
new
(
ApplicationRecord
)
do
include
IssuableLink
self
.
table_name
=
'issue_links'
belongs_to
:source
,
class_name:
'Issue'
belongs_to
:target
,
class_name:
'Issue'
def
self
.
name
'TestClass'
end
end
end
describe
'.inverse_link_type'
do
it
'returns the inverse type of link'
do
expect
(
test_class
.
inverse_link_type
(
'relates_to'
)).
to
eq
(
'relates_to'
)
expect
(
test_class
.
inverse_link_type
(
'is_blocked_by'
)).
to
eq
(
'is_blocked_by'
)
expect
(
test_class
.
inverse_link_type
(
'blocks'
)).
to
eq
(
'blocks'
)
end
end
describe
'.issuable_type'
do
let_it_be
(
:source_issue
)
{
create
(
:issue
)
}
let_it_be
(
:target_issue
)
{
create
(
:issue
)
}
before
do
test_class
.
create!
(
source:
source_issue
,
target:
target_issue
)
end
context
'when opposite relation already exists'
do
it
'raises NotImplementedError when performing validations'
do
instance
=
test_class
.
new
(
source:
target_issue
,
target:
source_issue
)
expect
{
instance
.
save!
}.
to
raise_error
(
NotImplementedError
)
end
end
end
end
spec/models/issue_link_spec.rb
View file @
01443314
...
...
@@ -3,58 +3,10 @@
require
'spec_helper'
RSpec
.
describe
IssueLink
do
describe
'Associations'
do
it
{
is_expected
.
to
belong_to
(
:source
).
class_name
(
'Issue'
)
}
it
{
is_expected
.
to
belong_to
(
:target
).
class_name
(
'Issue'
)
}
end
describe
'link_type'
do
it
{
is_expected
.
to
define_enum_for
(
:link_type
).
with_values
(
relates_to:
0
,
blocks:
1
)
}
it
'provides the "related" as default link_type'
do
expect
(
create
(
:issue_link
).
link_type
).
to
eq
'relates_to'
end
end
describe
'Validation'
do
subject
{
create
:issue_link
}
it
{
is_expected
.
to
validate_presence_of
(
:source
)
}
it
{
is_expected
.
to
validate_presence_of
(
:target
)
}
it
do
is_expected
.
to
validate_uniqueness_of
(
:source
)
.
scoped_to
(
:target_id
)
.
with_message
(
/already related/
)
end
it
'is not valid if an opposite link already exists'
do
issue_link
=
build
(
:issue_link
,
source:
subject
.
target
,
target:
subject
.
source
)
expect
(
issue_link
).
to
be_invalid
expect
(
issue_link
.
errors
[
:source
]).
to
include
(
'is already related to this issue'
)
end
context
'when it relates to itself'
do
let
(
:issue
)
{
create
:issue
}
context
'cannot be validated'
do
it
'does not invalidate object with self relation error'
do
issue_link
=
build
:issue_link
,
source:
issue
,
target:
nil
issue_link
.
valid?
expect
(
issue_link
.
errors
[
:source
]).
to
be_empty
end
end
context
'can be invalidated'
do
it
'invalidates object'
do
issue_link
=
build
:issue_link
,
source:
issue
,
target:
issue
expect
(
issue_link
).
to
be_invalid
expect
(
issue_link
.
errors
[
:source
]).
to
include
(
'cannot be related to itself'
)
end
end
end
it_behaves_like
'issuable link'
do
let_it_be_with_reload
(
:issuable_link
)
{
create
(
:issue_link
)
}
let_it_be
(
:issuable
)
{
create
(
:issue
)
}
let
(
:issuable_class
)
{
'Issue'
}
let
(
:issuable_link_factory
)
{
:issue_link
}
end
end
spec/support/shared_examples/models/issuable_link_shared_examples.rb
0 → 100644
View file @
01443314
# frozen_string_literal: true
# This shared example requires the following variables
# issuable_link
# issuable
# issuable_class
# issuable_link_factory
RSpec
.
shared_examples
'issuable link'
do
describe
'Associations'
do
it
{
is_expected
.
to
belong_to
(
:source
).
class_name
(
issuable
.
class
.
name
)
}
it
{
is_expected
.
to
belong_to
(
:target
).
class_name
(
issuable
.
class
.
name
)
}
end
describe
'Validation'
do
subject
{
issuable_link
}
it
{
is_expected
.
to
validate_presence_of
(
:source
)
}
it
{
is_expected
.
to
validate_presence_of
(
:target
)
}
it
do
is_expected
.
to
validate_uniqueness_of
(
:source
)
.
scoped_to
(
:target_id
)
.
with_message
(
/already related/
)
end
it
'is not valid if an opposite link already exists'
do
issuable_link
=
create_issuable_link
(
subject
.
target
,
subject
.
source
)
expect
(
issuable_link
).
to
be_invalid
expect
(
issuable_link
.
errors
[
:source
]).
to
include
(
"is already related to this
#{
issuable
.
class
.
name
.
downcase
}
"
)
end
context
'when it relates to itself'
do
context
'when target is nil'
do
it
'does not invalidate object with self relation error'
do
issuable_link
=
create_issuable_link
(
issuable
,
nil
)
issuable_link
.
valid?
expect
(
issuable_link
.
errors
[
:source
]).
to
be_empty
end
end
context
'when source and target are present'
do
it
'invalidates object'
do
issuable_link
=
create_issuable_link
(
issuable
,
issuable
)
expect
(
issuable_link
).
to
be_invalid
expect
(
issuable_link
.
errors
[
:source
]).
to
include
(
'cannot be related to itself'
)
end
end
end
def
create_issuable_link
(
source
,
target
)
build
(
issuable_link_factory
,
source:
source
,
target:
target
)
end
end
describe
'.link_type'
do
it
{
is_expected
.
to
define_enum_for
(
:link_type
).
with_values
(
relates_to:
0
,
blocks:
1
)
}
it
'provides the "related" as default link_type'
do
expect
(
issuable_link
.
link_type
).
to
eq
'relates_to'
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