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
0c387aac
Commit
0c387aac
authored
Apr 15, 2021
by
Stan Hu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Merge branch 'move-iteration-as-ee-model' into 'master'"
This reverts merge request !59172
parent
a277aaf4
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
246 additions
and
228 deletions
+246
-228
.rubocop_manual_todo.yml
.rubocop_manual_todo.yml
+1
-1
app/models/iteration.rb
app/models/iteration.rb
+16
-0
ee/app/models/ee/iteration.rb
ee/app/models/ee/iteration.rb
+229
-0
ee/app/models/iteration.rb
ee/app/models/iteration.rb
+0
-227
ee/spec/models/ee/iteration_spec.rb
ee/spec/models/ee/iteration_spec.rb
+0
-0
No files found.
.rubocop_manual_todo.yml
View file @
0c387aac
...
@@ -1884,6 +1884,7 @@ Gitlab/NamespacedClass:
...
@@ -1884,6 +1884,7 @@ Gitlab/NamespacedClass:
-
'
app/models/issue_email_participant.rb'
-
'
app/models/issue_email_participant.rb'
-
'
app/models/issue_link.rb'
-
'
app/models/issue_link.rb'
-
'
app/models/issue_user_mention.rb'
-
'
app/models/issue_user_mention.rb'
-
'
app/models/iteration.rb'
-
'
app/models/jira_connect_installation.rb'
-
'
app/models/jira_connect_installation.rb'
-
'
app/models/jira_connect_subscription.rb'
-
'
app/models/jira_connect_subscription.rb'
-
'
app/models/jira_import_state.rb'
-
'
app/models/jira_import_state.rb'
...
@@ -2695,7 +2696,6 @@ Gitlab/NamespacedClass:
...
@@ -2695,7 +2696,6 @@ Gitlab/NamespacedClass:
-
'
ee/app/models/issuable_metric_image.rb'
-
'
ee/app/models/issuable_metric_image.rb'
-
'
ee/app/models/issuable_sla.rb'
-
'
ee/app/models/issuable_sla.rb'
-
'
ee/app/models/issuables_analytics.rb'
-
'
ee/app/models/issuables_analytics.rb'
-
'
ee/app/models/iteration.rb'
-
'
ee/app/models/iteration_note.rb'
-
'
ee/app/models/iteration_note.rb'
-
'
ee/app/models/ldap_group_link.rb'
-
'
ee/app/models/ldap_group_link.rb'
-
'
ee/app/models/ldap_key.rb'
-
'
ee/app/models/ldap_key.rb'
...
...
app/models/iteration.rb
0 → 100644
View file @
0c387aac
# frozen_string_literal: true
# Placeholder class for model that is implemented in EE
class
Iteration
<
ApplicationRecord
self
.
table_name
=
'sprints'
def
self
.
reference_prefix
'*iteration:'
end
def
self
.
reference_pattern
nil
end
end
Iteration
.
prepend_if_ee
(
'::EE::Iteration'
)
ee/app/models/ee/iteration.rb
0 → 100644
View file @
0c387aac
# frozen_string_literal: true
module
EE
module
Iteration
extend
ActiveSupport
::
Concern
STATE_ENUM_MAP
=
{
upcoming:
1
,
started:
2
,
closed:
3
}.
with_indifferent_access
.
freeze
# For Iteration
class
Predefined
None
=
::
Timebox
::
TimeboxStruct
.
new
(
'None'
,
'none'
,
::
Timebox
::
None
.
id
).
freeze
Any
=
::
Timebox
::
TimeboxStruct
.
new
(
'Any'
,
'any'
,
::
Timebox
::
Any
.
id
).
freeze
Current
=
::
Timebox
::
TimeboxStruct
.
new
(
'Current'
,
'current'
,
-
4
).
freeze
ALL
=
[
None
,
Any
,
Current
].
freeze
end
prepended
do
include
AtomicInternalId
include
Timebox
attr_accessor
:skip_future_date_validation
attr_accessor
:skip_project_validation
belongs_to
:project
belongs_to
:group
belongs_to
:iterations_cadence
,
class_name:
'::Iterations::Cadence'
,
foreign_key: :iterations_cadence_id
,
inverse_of: :iterations
has_many
:issues
,
foreign_key:
'sprint_id'
has_many
:merge_requests
,
foreign_key:
'sprint_id'
has_internal_id
:iid
,
scope: :project
has_internal_id
:iid
,
scope: :group
validates
:start_date
,
presence:
true
validates
:due_date
,
presence:
true
validates
:iterations_cadence
,
presence:
true
,
unless:
->
{
project_id
.
present?
}
validate
:dates_do_not_overlap
,
if: :start_or_due_dates_changed?
validate
:future_date
,
if: :start_or_due_dates_changed?
,
unless: :skip_future_date_validation
validate
:no_project
,
unless: :skip_project_validation
validate
:validate_group
before_validation
:set_iterations_cadence
,
unless:
->
{
project_id
.
present?
}
before_create
:set_past_iteration_state
scope
:upcoming
,
->
{
with_state
(
:upcoming
)
}
scope
:started
,
->
{
with_state
(
:started
)
}
scope
:closed
,
->
{
with_state
(
:closed
)
}
scope
:within_timeframe
,
->
(
start_date
,
end_date
)
do
where
(
'start_date <= ?'
,
end_date
).
where
(
'due_date >= ?'
,
start_date
)
end
scope
:start_date_passed
,
->
{
where
(
'start_date <= ?'
,
Date
.
current
).
where
(
'due_date >= ?'
,
Date
.
current
)
}
scope
:due_date_passed
,
->
{
where
(
'due_date < ?'
,
Date
.
current
)
}
state_machine
:state_enum
,
initial: :upcoming
do
event
:start
do
transition
upcoming: :started
end
event
:close
do
transition
[
:upcoming
,
:started
]
=>
:closed
end
state
:upcoming
,
value:
Iteration
::
STATE_ENUM_MAP
[
:upcoming
]
state
:started
,
value:
Iteration
::
STATE_ENUM_MAP
[
:started
]
state
:closed
,
value:
Iteration
::
STATE_ENUM_MAP
[
:closed
]
end
class
<<
self
alias_method
:with_state
,
:with_state_enum
alias_method
:with_states
,
:with_state_enums
end
end
class_methods
do
def
reference_pattern
# NOTE: The id pattern only matches when all characters on the expression
# are digits, so it will match *iteration:2 but not *iteration:2.1 because that's probably a
# iteration name and we want it to be matched as such.
@reference_pattern
||=
%r{
(
#{
::
Project
.
reference_pattern
}
)?
#{
::
Regexp
.
escape
(
reference_prefix
)
}
(?:
(?<iteration_id>
\d
+(?!
\S\w
)
\b
# Integer-based iteration id, or
) |
(?<iteration_name>
[^"
\s
]+
\b
| # String-based single-word iteration title, or
"[^"]+" # String-based multi-word iteration surrounded in quotes
)
)
}x
.
freeze
end
def
link_reference_pattern
@link_reference_pattern
||=
super
(
"iterations"
,
/(?<iteration>\d+)/
)
end
def
filter_by_state
(
iterations
,
state
)
case
state
when
'closed'
then
iterations
.
closed
when
'started'
then
iterations
.
started
when
'upcoming'
then
iterations
.
upcoming
when
'opened'
then
iterations
.
started
.
or
(
iterations
.
upcoming
)
when
'all'
then
iterations
else
raise
ArgumentError
,
"Unknown state filter:
#{
state
}
"
end
end
end
def
state
STATE_ENUM_MAP
.
key
(
state_enum
)
end
def
state
=
(
value
)
self
.
state_enum
=
STATE_ENUM_MAP
[
value
]
end
def
resource_parent
group
||
project
end
# Show just the title when we manage to find an iteration, without the reference pattern,
# since it's long and unsightly.
def
reference_link_text
(
from
=
nil
)
self
.
title
end
def
supports_timebox_charts?
resource_parent
&
.
feature_available?
(
:iterations
)
&&
weight_available?
end
private
def
timebox_format_reference
(
format
=
:id
)
raise
::
ArgumentError
,
_
(
'Unknown format'
)
unless
[
:id
,
:name
].
include?
(
format
)
if
format
==
:name
super
else
id
end
end
def
parent_group
group
||
project
.
group
end
def
start_or_due_dates_changed?
start_date_changed?
||
due_date_changed?
end
# ensure dates do not overlap with other Iterations in the same cadence tree
def
dates_do_not_overlap
return
unless
iterations_cadence
return
unless
iterations_cadence
.
iterations
.
where
.
not
(
id:
self
.
id
).
within_timeframe
(
start_date
,
due_date
).
exists?
# for now we only have a single default cadence within a group just to wrap the iterations into a set.
# once we introduce multiple cadences per group we need to change this message.
# related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/299312
errors
.
add
(
:base
,
s_
(
"Iteration|Dates cannot overlap with other existing Iterations within this group"
))
end
def
future_date
if
start_or_due_dates_changed?
errors
.
add
(
:start_date
,
s_
(
"Iteration|cannot be more than 500 years in the future"
))
if
start_date
>
500
.
years
.
from_now
errors
.
add
(
:due_date
,
s_
(
"Iteration|cannot be more than 500 years in the future"
))
if
due_date
>
500
.
years
.
from_now
end
end
def
no_project
return
unless
project_id
.
present?
errors
.
add
(
:project_id
,
s_
(
"is not allowed. We do not currently support project-level iterations"
))
end
def
set_past_iteration_state
# if we create an iteration in the past, we set the state to closed right away,
# no need to wait for IterationsUpdateStatusWorker to do so.
self
.
state
=
:closed
if
due_date
<
Date
.
current
end
# TODO: this method should be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296099
def
set_iterations_cadence
return
if
iterations_cadence
# For now we support only group iterations
# issue to clarify project iterations: https://gitlab.com/gitlab-org/gitlab/-/issues/299864
return
unless
group
# we need this as we use the cadence to validate the dates overlap for this iteration,
# so in the case this runs before background migration we need to first set all iterations
# in this group to a cadence before we can validate the dates overlap.
default_cadence
=
find_or_create_default_cadence
group
.
iterations
.
where
(
iterations_cadence_id:
nil
).
update_all
(
iterations_cadence_id:
default_cadence
.
id
)
self
.
iterations_cadence
=
default_cadence
end
# TODO: this method should be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296099
def
find_or_create_default_cadence
cadence_title
=
"
#{
group
.
name
}
Iterations"
start_date
=
self
.
start_date
||
Date
.
today
::
Iterations
::
Cadence
.
create_with
(
title:
cadence_title
,
start_date:
start_date
,
automatic:
false
,
# set to 0, i.e. unspecified when creating default iterations as we do validate for presence.
iterations_in_advance:
0
,
duration_in_weeks:
0
).
safe_find_or_create_by!
(
group:
group
)
end
# TODO: remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296100
def
validate_group
return
if
iterations_cadence
&
.
group_id
==
group_id
return
unless
iterations_cadence
errors
.
add
(
:group
,
s_
(
'is not valid. The iteration group has to match the iteration cadence group.'
))
end
end
end
ee/app/models/iteration.rb
deleted
100644 → 0
View file @
a277aaf4
# frozen_string_literal: true
class
Iteration
<
ApplicationRecord
include
AtomicInternalId
include
Timebox
self
.
table_name
=
'sprints'
STATE_ENUM_MAP
=
{
upcoming:
1
,
started:
2
,
closed:
3
}.
with_indifferent_access
.
freeze
attr_accessor
:skip_future_date_validation
attr_accessor
:skip_project_validation
belongs_to
:project
belongs_to
:group
belongs_to
:iterations_cadence
,
class_name:
'::Iterations::Cadence'
,
foreign_key: :iterations_cadence_id
,
inverse_of: :iterations
has_many
:issues
,
foreign_key:
'sprint_id'
has_many
:merge_requests
,
foreign_key:
'sprint_id'
has_internal_id
:iid
,
scope: :project
has_internal_id
:iid
,
scope: :group
validates
:start_date
,
presence:
true
validates
:due_date
,
presence:
true
validates
:iterations_cadence
,
presence:
true
,
unless:
->
{
project_id
.
present?
}
validate
:dates_do_not_overlap
,
if: :start_or_due_dates_changed?
validate
:future_date
,
if: :start_or_due_dates_changed?
,
unless: :skip_future_date_validation
validate
:no_project
,
unless: :skip_project_validation
validate
:validate_group
before_validation
:set_iterations_cadence
,
unless:
->
{
project_id
.
present?
}
before_create
:set_past_iteration_state
scope
:upcoming
,
->
{
with_state
(
:upcoming
)
}
scope
:started
,
->
{
with_state
(
:started
)
}
scope
:closed
,
->
{
with_state
(
:closed
)
}
scope
:within_timeframe
,
->
(
start_date
,
end_date
)
do
where
(
'start_date <= ?'
,
end_date
).
where
(
'due_date >= ?'
,
start_date
)
end
scope
:start_date_passed
,
->
{
where
(
'start_date <= ?'
,
Date
.
current
).
where
(
'due_date >= ?'
,
Date
.
current
)
}
scope
:due_date_passed
,
->
{
where
(
'due_date < ?'
,
Date
.
current
)
}
state_machine
:state_enum
,
initial: :upcoming
do
event
:start
do
transition
upcoming: :started
end
event
:close
do
transition
[
:upcoming
,
:started
]
=>
:closed
end
state
:upcoming
,
value:
Iteration
::
STATE_ENUM_MAP
[
:upcoming
]
state
:started
,
value:
Iteration
::
STATE_ENUM_MAP
[
:started
]
state
:closed
,
value:
Iteration
::
STATE_ENUM_MAP
[
:closed
]
end
class
<<
self
alias_method
:with_state
,
:with_state_enum
alias_method
:with_states
,
:with_state_enums
def
reference_prefix
'*iteration:'
end
def
reference_pattern
# NOTE: The id pattern only matches when all characters on the expression
# are digits, so it will match *iteration:2 but not *iteration:2.1 because that's probably a
# iteration name and we want it to be matched as such.
@reference_pattern
||=
%r{
(
#{
::
Project
.
reference_pattern
}
)?
#{
::
Regexp
.
escape
(
reference_prefix
)
}
(?:
(?<iteration_id>
\d
+(?!
\S\w
)
\b
# Integer-based iteration id, or
) |
(?<iteration_name>
[^"
\s
]+
\b
| # String-based single-word iteration title, or
"[^"]+" # String-based multi-word iteration surrounded in quotes
)
)
}x
.
freeze
end
def
link_reference_pattern
@link_reference_pattern
||=
super
(
"iterations"
,
/(?<iteration>\d+)/
)
end
def
filter_by_state
(
iterations
,
state
)
case
state
when
'closed'
then
iterations
.
closed
when
'started'
then
iterations
.
started
when
'upcoming'
then
iterations
.
upcoming
when
'opened'
then
iterations
.
started
.
or
(
iterations
.
upcoming
)
when
'all'
then
iterations
else
raise
ArgumentError
,
"Unknown state filter:
#{
state
}
"
end
end
end
# For Iteration
class
Predefined
None
=
::
Timebox
::
TimeboxStruct
.
new
(
'None'
,
'none'
,
::
Timebox
::
None
.
id
).
freeze
Any
=
::
Timebox
::
TimeboxStruct
.
new
(
'Any'
,
'any'
,
::
Timebox
::
Any
.
id
).
freeze
Current
=
::
Timebox
::
TimeboxStruct
.
new
(
'Current'
,
'current'
,
-
4
).
freeze
ALL
=
[
None
,
Any
,
Current
].
freeze
end
def
state
STATE_ENUM_MAP
.
key
(
state_enum
)
end
def
state
=
(
value
)
self
.
state_enum
=
STATE_ENUM_MAP
[
value
]
end
def
resource_parent
group
||
project
end
# Show just the title when we manage to find an iteration, without the reference pattern,
# since it's long and unsightly.
def
reference_link_text
(
from
=
nil
)
self
.
title
end
def
supports_timebox_charts?
resource_parent
&
.
feature_available?
(
:iterations
)
&&
weight_available?
end
private
def
timebox_format_reference
(
format
=
:id
)
raise
::
ArgumentError
,
_
(
'Unknown format'
)
unless
[
:id
,
:name
].
include?
(
format
)
if
format
==
:name
super
else
id
end
end
def
parent_group
group
||
project
.
group
end
def
start_or_due_dates_changed?
start_date_changed?
||
due_date_changed?
end
# ensure dates do not overlap with other Iterations in the same cadence tree
def
dates_do_not_overlap
return
unless
iterations_cadence
return
unless
iterations_cadence
.
iterations
.
where
.
not
(
id:
self
.
id
).
within_timeframe
(
start_date
,
due_date
).
exists?
# for now we only have a single default cadence within a group just to wrap the iterations into a set.
# once we introduce multiple cadences per group we need to change this message.
# related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/299312
errors
.
add
(
:base
,
s_
(
"Iteration|Dates cannot overlap with other existing Iterations within this group"
))
end
def
future_date
if
start_or_due_dates_changed?
errors
.
add
(
:start_date
,
s_
(
"Iteration|cannot be more than 500 years in the future"
))
if
start_date
>
500
.
years
.
from_now
errors
.
add
(
:due_date
,
s_
(
"Iteration|cannot be more than 500 years in the future"
))
if
due_date
>
500
.
years
.
from_now
end
end
def
no_project
return
unless
project_id
.
present?
errors
.
add
(
:project_id
,
s_
(
"is not allowed. We do not currently support project-level iterations"
))
end
def
set_past_iteration_state
# if we create an iteration in the past, we set the state to closed right away,
# no need to wait for IterationsUpdateStatusWorker to do so.
self
.
state
=
:closed
if
due_date
<
Date
.
current
end
# TODO: this method should be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296099
def
set_iterations_cadence
return
if
iterations_cadence
# For now we support only group iterations
# issue to clarify project iterations: https://gitlab.com/gitlab-org/gitlab/-/issues/299864
return
unless
group
# we need this as we use the cadence to validate the dates overlap for this iteration,
# so in the case this runs before background migration we need to first set all iterations
# in this group to a cadence before we can validate the dates overlap.
default_cadence
=
find_or_create_default_cadence
group
.
iterations
.
where
(
iterations_cadence_id:
nil
).
update_all
(
iterations_cadence_id:
default_cadence
.
id
)
self
.
iterations_cadence
=
default_cadence
end
# TODO: this method should be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296099
def
find_or_create_default_cadence
cadence_title
=
"
#{
group
.
name
}
Iterations"
start_date
=
self
.
start_date
||
Date
.
today
::
Iterations
::
Cadence
.
create_with
(
title:
cadence_title
,
start_date:
start_date
,
automatic:
false
,
# set to 0, i.e. unspecified when creating default iterations as we do validate for presence.
iterations_in_advance:
0
,
duration_in_weeks:
0
).
safe_find_or_create_by!
(
group:
group
)
end
# TODO: remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/296100
def
validate_group
return
if
iterations_cadence
&
.
group_id
==
group_id
return
unless
iterations_cadence
errors
.
add
(
:group
,
s_
(
'is not valid. The iteration group has to match the iteration cadence group.'
))
end
end
ee/spec/models/iteration_spec.rb
→
ee/spec/models/
ee/
iteration_spec.rb
View file @
0c387aac
File moved
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