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
c4c1f3e1
Commit
c4c1f3e1
authored
Apr 14, 2021
by
Martin Wortschack
Committed by
Ezekiel Kigbo
Apr 14, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add URL param for linking to a specific stage in group-level VSA
parent
4b5b3064
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
156 additions
and
21 deletions
+156
-21
ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
...javascripts/analytics/cycle_analytics/components/base.vue
+3
-0
ee/app/assets/javascripts/analytics/cycle_analytics/store/actions.js
...ts/javascripts/analytics/cycle_analytics/store/actions.js
+18
-5
ee/app/assets/javascripts/analytics/cycle_analytics/utils.js
ee/app/assets/javascripts/analytics/cycle_analytics/utils.js
+1
-1
ee/app/assets/javascripts/analytics/shared/utils.js
ee/app/assets/javascripts/analytics/shared/utils.js
+2
-0
ee/lib/gitlab/analytics/cycle_analytics/request_params.rb
ee/lib/gitlab/analytics/cycle_analytics/request_params.rb
+21
-0
ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
...rontend/analytics/cycle_analytics/components/base_spec.js
+28
-1
ee/spec/frontend/analytics/cycle_analytics/store/actions_spec.js
.../frontend/analytics/cycle_analytics/store/actions_spec.js
+76
-13
ee/spec/frontend/analytics/shared/utils_spec.js
ee/spec/frontend/analytics/shared/utils_spec.js
+1
-0
ee/spec/lib/gitlab/analytics/cycle_analytics/request_params_spec.rb
...b/gitlab/analytics/cycle_analytics/request_params_spec.rb
+6
-1
No files found.
ee/app/assets/javascripts/analytics/cycle_analytics/components/base.vue
View file @
c4c1f3e1
...
...
@@ -129,6 +129,9 @@ export default {
project_ids
:
selectedProjectIds
,
created_after
:
toYmd
(
this
.
startDate
),
created_before
:
toYmd
(
this
.
endDate
),
// the `overview` stage is always the default, so dont persist the id if its selected
stage_id
:
this
.
selectedStage
?.
id
&&
!
this
.
isOverviewStageSelected
?
this
.
selectedStage
.
id
:
null
,
};
},
stageCount
()
{
...
...
ee/app/assets/javascripts/analytics/cycle_analytics/store/actions.js
View file @
c4c1f3e1
...
...
@@ -157,13 +157,13 @@ export const receiveGroupStagesError = ({ commit }, error) => {
});
};
export
const
setDefaultSelectedStage
=
({
dispatch
,
getters
,
state
:
{
featureFlags
}
=
{}
})
=>
{
export
const
setDefaultSelectedStage
=
({
state
:
{
featureFlags
},
dispatch
,
getters
})
=>
{
const
{
activeStages
=
[]
}
=
getters
;
if
(
featureFlags
?.
hasPathNavigation
)
{
return
dispatch
(
'
setSelectedStage
'
,
OVERVIEW_STAGE_CONFIG
);
}
const
{
activeStages
=
[]
}
=
getters
;
if
(
activeStages
?.
length
)
{
const
[
firstActiveStage
]
=
activeStages
;
return
Promise
.
all
([
...
...
@@ -175,13 +175,21 @@ export const setDefaultSelectedStage = ({ dispatch, getters, state: { featureFla
createFlash
({
message
:
__
(
'
There was an error while fetching value stream analytics data.
'
),
});
return
Promise
.
resolve
();
};
export
const
receiveGroupStagesSuccess
=
({
commit
,
dispatch
},
stages
)
=>
{
export
const
receiveGroupStagesSuccess
=
(
{
state
:
{
featureFlags
},
commit
,
dispatch
},
stages
,
)
=>
{
commit
(
types
.
RECEIVE_GROUP_STAGES_SUCCESS
,
stages
);
return
dispatch
(
'
setDefaultSelectedStage
'
);
if
(
!
featureFlags
?.
hasPathNavigation
)
{
return
dispatch
(
'
setDefaultSelectedStage
'
);
}
return
Promise
.
resolve
();
};
export
const
fetchGroupStagesAndEvents
=
({
dispatch
,
getters
})
=>
{
...
...
@@ -309,12 +317,17 @@ export const initializeCycleAnalytics = ({ dispatch, commit }, initialData = {})
selectedMilestone
,
selectedAssigneeList
,
selectedLabelList
,
stage
:
selectedStage
,
group
,
}
=
initialData
;
commit
(
types
.
SET_FEATURE_FLAGS
,
featureFlags
);
if
(
group
?.
fullPath
)
{
return
Promise
.
all
([
selectedStage
?
dispatch
(
'
setSelectedStage
'
,
selectedStage
)
:
dispatch
(
'
setDefaultSelectedStage
'
),
selectedStage
?.
id
?
dispatch
(
'
fetchStageData
'
,
selectedStage
.
id
)
:
Promise
.
resolve
(),
dispatch
(
'
setPaths
'
,
{
groupPath
:
group
.
fullPath
,
milestonesPath
,
labelsPath
}),
dispatch
(
'
filters/initialize
'
,
{
selectedAuthor
,
...
...
ee/app/assets/javascripts/analytics/cycle_analytics/utils.js
View file @
c4c1f3e1
...
...
@@ -438,7 +438,7 @@ export const transformStagesForPathNavigation = ({ stages, medians, selectedStag
const
formattedStages
=
stages
.
map
((
stage
)
=>
{
return
{
metric
:
medians
[
stage
?.
id
],
selected
:
stage
.
title
===
selectedStage
.
title
,
selected
:
stage
.
id
===
selectedStage
.
id
,
icon
:
null
,
...
stage
,
};
...
...
ee/app/assets/javascripts/analytics/shared/utils.js
View file @
c4c1f3e1
...
...
@@ -105,6 +105,7 @@ export const buildCycleAnalyticsInitialData = ({
labelsPath
=
''
,
milestonesPath
=
''
,
defaultStages
=
null
,
stage
=
null
,
}
=
{})
=>
({
selectedValueStream
:
buildValueStreamFromJson
(
valueStream
),
group
:
groupId
...
...
@@ -128,6 +129,7 @@ export const buildCycleAnalyticsInitialData = ({
defaultStageConfig
:
defaultStages
?
buildDefaultStagesFromJSON
(
defaultStages
).
map
(
convertObjectPropsToCamelCase
)
:
[],
stage
:
JSON
.
parse
(
stage
),
});
export
const
filterBySearchTerm
=
(
data
=
[],
searchTerm
=
''
,
filterByKey
=
'
name
'
)
=>
{
...
...
ee/lib/gitlab/analytics/cycle_analytics/request_params.rb
View file @
c4c1f3e1
...
...
@@ -7,6 +7,7 @@ module Gitlab
include
ActiveModel
::
Model
include
ActiveModel
::
Validations
include
ActiveModel
::
Attributes
include
Gitlab
::
Utils
::
StrongMemoize
MAX_RANGE_DAYS
=
180
.
days
.
freeze
DEFAULT_DATE_RANGE
=
29
.
days
# 30 including Date.today
...
...
@@ -19,6 +20,7 @@ module Gitlab
:sort
,
:direction
,
:page
,
:stage_id
,
label_name:
[].
freeze
,
assignee_username:
[].
freeze
,
project_ids:
[].
freeze
...
...
@@ -41,6 +43,7 @@ module Gitlab
attribute
:sort
attribute
:direction
attribute
:page
attribute
:stage_id
FINDER_PARAM_NAMES
.
each
do
|
param_name
|
attribute
param_name
...
...
@@ -88,6 +91,7 @@ module Gitlab
attrs
[
:milestone
]
=
milestone_title
if
milestone_title
.
present?
attrs
[
:sort
]
=
sort
if
sort
.
present?
attrs
[
:direction
]
=
direction
if
direction
.
present?
attrs
[
:stage
]
=
stage_data_attributes
.
to_json
if
stage_id
.
present?
end
end
...
...
@@ -133,6 +137,15 @@ module Gitlab
}
end
def
stage_data_attributes
return
unless
stage
{
id:
stage
.
id
||
stage
.
name
,
title:
stage
.
name
}
end
def
validate_created_before
return
if
created_after
.
nil?
||
created_before
.
nil?
...
...
@@ -154,6 +167,14 @@ module Gitlab
DEFAULT_DATE_RANGE
.
ago
end
end
def
stage
return
unless
value_stream
strong_memoize
(
:stage
)
do
::
Analytics
::
CycleAnalytics
::
StageFinder
.
new
(
parent:
group
,
stage_id:
stage_id
).
execute
if
stage_id
end
end
end
end
end
...
...
ee/spec/frontend/analytics/cycle_analytics/components/base_spec.js
View file @
c4c1f3e1
...
...
@@ -33,6 +33,7 @@ const noDataSvgPath = 'path/to/no/data';
const
noAccessSvgPath
=
'
path/to/no/access
'
;
const
currentGroup
=
convertObjectPropsToCamelCase
(
mockData
.
group
);
const
emptyStateSvgPath
=
'
path/to/empty/state
'
;
const
stage
=
null
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
...
...
@@ -60,6 +61,7 @@ const initialCycleAnalyticsState = {
createdAfter
:
mockData
.
startDate
,
createdBefore
:
mockData
.
endDate
,
group
:
currentGroup
,
stage
,
};
const
mocks
=
{
...
...
@@ -610,6 +612,7 @@ describe('Value Stream Analytics component', () => {
created_after
:
toYmd
(
mockData
.
startDate
),
created_before
:
toYmd
(
mockData
.
endDate
),
project_ids
:
null
,
stage_id
:
null
,
};
const
selectedProjectIds
=
mockData
.
selectedProjects
.
map
(({
id
})
=>
getIdFromGraphQLId
(
id
));
...
...
@@ -663,7 +666,7 @@ describe('Value Stream Analytics component', () => {
describe
(
'
with selectedProjectIds set
'
,
()
=>
{
beforeEach
(
async
()
=>
{
wrapper
=
await
createComponent
();
store
.
dispatch
(
'
setSelectedProjects
'
,
mockData
.
selectedProjects
);
await
store
.
dispatch
(
'
setSelectedProjects
'
,
mockData
.
selectedProjects
);
await
wrapper
.
vm
.
$nextTick
();
});
...
...
@@ -673,6 +676,30 @@ describe('Value Stream Analytics component', () => {
created_after
:
toYmd
(
mockData
.
startDate
),
created_before
:
toYmd
(
mockData
.
endDate
),
project_ids
:
selectedProjectIds
,
stage_id
:
1
,
});
});
});
describe
(
'
with selectedStage set
'
,
()
=>
{
const
selectedStage
=
{
title
:
'
Plan
'
,
id
:
2
,
};
beforeEach
(
async
()
=>
{
wrapper
=
await
createComponent
();
store
.
dispatch
(
'
setSelectedStage
'
,
selectedStage
);
await
wrapper
.
vm
.
$nextTick
();
});
it
(
'
sets the stage_id url parameter
'
,
async
()
=>
{
await
shouldMergeUrlParams
(
wrapper
,
{
...
defaultParams
,
created_after
:
toYmd
(
mockData
.
startDate
),
created_before
:
toYmd
(
mockData
.
endDate
),
project_ids
:
null
,
stage_id
:
2
,
});
});
});
...
...
ee/spec/frontend/analytics/cycle_analytics/store/actions_spec.js
View file @
c4c1f3e1
...
...
@@ -373,19 +373,58 @@ describe('Value Stream Analytics actions', () => {
});
describe
(
'
receiveGroupStagesSuccess
'
,
()
=>
{
it
(
`commits the
${
types
.
RECEIVE_GROUP_STAGES_SUCCESS
}
mutation and dispatches 'setDefaultSelectedStage'`
,
()
=>
{
return
testAction
(
actions
.
receiveGroupStagesSuccess
,
{
...
customizableStagesAndEvents
.
stages
},
state
,
[
{
type
:
types
.
RECEIVE_GROUP_STAGES_SUCCESS
,
payload
:
{
...
customizableStagesAndEvents
.
stages
},
describe
(
'
when the `hasPathNavigation` feature flag is enabled
'
,
()
=>
{
beforeEach
(()
=>
{
state
=
{
...
state
,
featureFlags
:
{
...
state
.
featureFlags
,
hasPathNavigation
:
true
,
},
],
[{
type
:
'
setDefaultSelectedStage
'
}],
);
};
});
it
(
`commits the
${
types
.
RECEIVE_GROUP_STAGES_SUCCESS
}
mutation'`
,
()
=>
{
return
testAction
(
actions
.
receiveGroupStagesSuccess
,
{
...
customizableStagesAndEvents
.
stages
},
state
,
[
{
type
:
types
.
RECEIVE_GROUP_STAGES_SUCCESS
,
payload
:
{
...
customizableStagesAndEvents
.
stages
},
},
],
[],
);
});
});
describe
(
'
when the `hasPathNavigation` feature flag is disabled
'
,
()
=>
{
beforeEach
(()
=>
{
state
=
{
...
state
,
featureFlags
:
{
...
state
.
featureFlags
,
hasPathNavigation
:
false
,
},
};
});
it
(
`commits the
${
types
.
RECEIVE_GROUP_STAGES_SUCCESS
}
mutation and dispatches 'setDefaultSelectedStage`
,
()
=>
{
return
testAction
(
actions
.
receiveGroupStagesSuccess
,
{
...
customizableStagesAndEvents
.
stages
},
state
,
[
{
type
:
types
.
RECEIVE_GROUP_STAGES_SUCCESS
,
payload
:
{
...
customizableStagesAndEvents
.
stages
},
},
],
[{
type
:
'
setDefaultSelectedStage
'
}],
);
});
});
});
...
...
@@ -450,7 +489,7 @@ describe('Value Stream Analytics actions', () => {
${
null
}
`
(
'
with $data will flash an error
'
,
({
data
})
=>
{
actions
.
setDefaultSelectedStage
(
{
getters
:
{
activeStages
:
data
},
dispatch
:
()
=>
{}
},
{
state
,
getters
:
{
activeStages
:
data
},
dispatch
:
()
=>
{}
},
{},
);
expect
(
createFlash
).
toHaveBeenCalledWith
({
message
:
flashErrorMessage
});
...
...
@@ -876,6 +915,30 @@ describe('Value Stream Analytics actions', () => {
expect
(
mockDispatch
).
toHaveBeenCalledWith
(
'
initializeCycleAnalyticsSuccess
'
);
});
describe
(
'
with a selected stage
'
,
()
=>
{
it
(
'
dispatches "setSelectedStage" and "fetchStageData"
'
,
async
()
=>
{
const
stage
=
{
id
:
2
,
title
:
'
plan
'
};
await
actions
.
initializeCycleAnalytics
(
store
,
{
...
initialData
,
stage
,
});
expect
(
mockDispatch
).
toHaveBeenCalledWith
(
'
setSelectedStage
'
,
stage
);
expect
(
mockDispatch
).
toHaveBeenCalledWith
(
'
fetchStageData
'
,
stage
.
id
);
});
});
describe
(
'
without a selected stage
'
,
()
=>
{
it
(
'
dispatches "setDefaultSelectedStage"
'
,
async
()
=>
{
await
actions
.
initializeCycleAnalytics
(
store
,
{
...
initialData
,
stage
:
null
,
});
expect
(
mockDispatch
).
not
.
toHaveBeenCalledWith
(
'
setSelectedStage
'
);
expect
(
mockDispatch
).
not
.
toHaveBeenCalledWith
(
'
fetchStageData
'
);
expect
(
mockDispatch
).
toHaveBeenCalledWith
(
'
setDefaultSelectedStage
'
);
});
});
it
(
'
commits "INITIALIZE_VSA"
'
,
async
()
=>
{
await
actions
.
initializeCycleAnalytics
(
store
,
initialData
);
expect
(
mockCommit
).
toHaveBeenCalledWith
(
'
INITIALIZE_VSA
'
,
initialData
);
...
...
ee/spec/frontend/analytics/shared/utils_spec.js
View file @
c4c1f3e1
...
...
@@ -90,6 +90,7 @@ describe('buildCycleAnalyticsInitialData', () => {
${
'
selectedProjects
'
}
|
${[]}
${
'
labelsPath
'
}
|
${
''
}
${
'
milestonesPath
'
}
|
${
''
}
${
'
stage
'
}
|
${
null
}
`
(
'
will set a default value for "$field" if is not present
'
,
({
field
,
value
})
=>
{
expect
(
buildCycleAnalyticsInitialData
()).
toMatchObject
({
[
field
]:
value
,
...
...
ee/spec/lib/gitlab/analytics/cycle_analytics/request_params_spec.rb
View file @
c4c1f3e1
...
...
@@ -189,12 +189,16 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RequestParams do
end
describe
'issuable filter params'
do
let_it_be
(
:stage
)
{
create
(
:cycle_analytics_group_stage
,
group:
root_group
)
}
before
do
params
.
merge!
(
milestone_title:
'title'
,
assignee_username:
[
'username1'
],
label_name:
%w[label1 label2]
,
author_username:
'author'
author_username:
'author'
,
stage_id:
stage
.
id
,
value_stream:
stage
.
value_stream
)
end
...
...
@@ -204,6 +208,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RequestParams do
it
{
expect
(
subject
[
:assignees
]).
to
eq
(
'["username1"]'
)
}
it
{
expect
(
subject
[
:labels
]).
to
eq
(
'["label1","label2"]'
)
}
it
{
expect
(
subject
[
:author
]).
to
eq
(
'author'
)
}
it
{
expect
(
subject
[
:stage
]).
to
eq
(
'{"id":1,"title":"Stage #1"}'
)
}
end
describe
'sorting params'
do
...
...
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