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
9e46d9e3
Commit
9e46d9e3
authored
Aug 16, 2021
by
Ezekiel Kigbo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Minor refactor metrics requests
Refactors the metrics requests into a single request and updates specs
parent
ba6a0972
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
103 additions
and
113 deletions
+103
-113
app/assets/javascripts/api/analytics_api.js
app/assets/javascripts/api/analytics_api.js
+9
-2
app/assets/javascripts/cycle_analytics/components/value_stream_metrics.vue
...ripts/cycle_analytics/components/value_stream_metrics.vue
+2
-2
app/assets/javascripts/cycle_analytics/constants.js
app/assets/javascripts/cycle_analytics/constants.js
+5
-4
app/assets/javascripts/cycle_analytics/store/mutations.js
app/assets/javascripts/cycle_analytics/store/mutations.js
+1
-3
app/assets/javascripts/cycle_analytics/utils.js
app/assets/javascripts/cycle_analytics/utils.js
+0
-9
ee/app/assets/javascripts/analytics/cycle_analytics/constants.js
...assets/javascripts/analytics/cycle_analytics/constants.js
+6
-6
ee/app/assets/javascripts/api/analytics_api.js
ee/app/assets/javascripts/api/analytics_api.js
+6
-5
spec/frontend/cycle_analytics/store/mutations_spec.js
spec/frontend/cycle_analytics/store/mutations_spec.js
+11
-14
spec/frontend/cycle_analytics/utils_spec.js
spec/frontend/cycle_analytics/utils_spec.js
+0
-19
spec/frontend/cycle_analytics/value_stream_metrics_spec.js
spec/frontend/cycle_analytics/value_stream_metrics_spec.js
+63
-49
No files found.
app/assets/javascripts/api/analytics_api.js
View file @
9e46d9e3
...
...
@@ -7,6 +7,9 @@ const PROJECT_VSA_PATH_BASE = '/:request_path/-/analytics/value_stream_analytics
const
PROJECT_VSA_STAGES_PATH
=
`
${
PROJECT_VSA_PATH_BASE
}
/:value_stream_id/stages`
;
const
PROJECT_VSA_STAGE_DATA_PATH
=
`
${
PROJECT_VSA_STAGES_PATH
}
/:stage_id`
;
export
const
METRIC_TYPE_SUMMARY
=
'
summary
'
;
export
const
METRIC_TYPE_TIME_SUMMARY
=
'
time_summary
'
;
const
buildProjectMetricsPath
=
(
requestPath
)
=>
buildApiUrl
(
PROJECT_VSA_METRICS_BASE
).
replace
(
'
:request_path
'
,
requestPath
);
...
...
@@ -65,9 +68,13 @@ export const getValueStreamStageCounts = ({ requestPath, valueStreamId, stageId
return
axios
.
get
(
joinPaths
(
stageBase
,
'
count
'
),
{
params
});
};
export
const
getValueStreamTimeSummaryMetrics
=
(
requestPath
,
params
=
{})
=>
{
export
const
getValueStreamMetrics
=
({
endpoint
=
METRIC_TYPE_SUMMARY
,
requestPath
,
params
=
{},
})
=>
{
const
metricBase
=
buildProjectMetricsPath
(
requestPath
);
return
axios
.
get
(
joinPaths
(
metricBase
,
'
time_summary
'
),
{
params
});
return
axios
.
get
(
joinPaths
(
metricBase
,
endpoint
),
{
params
});
};
export
const
getValueStreamSummaryMetrics
=
(
requestPath
,
params
=
{})
=>
{
...
...
app/assets/javascripts/cycle_analytics/components/value_stream_metrics.vue
View file @
9e46d9e3
...
...
@@ -7,8 +7,8 @@ import { sprintf, s__ } from '~/locale';
import
{
METRICS_POPOVER_CONTENT
}
from
'
../constants
'
;
import
{
removeFlash
,
prepareTimeMetricsData
}
from
'
../utils
'
;
const
requestData
=
({
request
,
path
,
params
,
name
})
=>
{
return
request
(
path
,
params
)
const
requestData
=
({
request
,
endpoint
,
path
,
params
,
name
})
=>
{
return
request
(
{
endpoint
,
params
,
requestPath
:
path
}
)
.
then
(({
data
})
=>
data
)
.
catch
(()
=>
{
const
message
=
sprintf
(
...
...
app/assets/javascripts/cycle_analytics/constants.js
View file @
9e46d9e3
import
{
getValueStreamTimeSummaryMetrics
,
getValueStreamSummaryMetrics
,
getValueStreamMetrics
,
METRIC_TYPE_SUMMARY
,
METRIC_TYPE_TIME_SUMMARY
,
}
from
'
~/api/analytics_api
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
...
...
@@ -61,10 +62,10 @@ export const METRICS_POPOVER_CONTENT = {
};
export
const
SUMMARY_METRICS_REQUEST
=
[
{
request
:
getValueStreamSummaryMetrics
,
name
:
__
(
'
recent activity
'
)
},
{
endpoint
:
METRIC_TYPE_SUMMARY
,
name
:
__
(
'
recent activity
'
),
request
:
getValueStreamMetrics
},
];
export
const
METRICS_REQUESTS
=
[
{
request
:
getValueStreamTimeSummaryMetrics
,
name
:
__
(
'
time summary
'
)
},
{
endpoint
:
METRIC_TYPE_TIME_SUMMARY
,
name
:
__
(
'
time summary
'
),
request
:
getValueStreamMetrics
},
...
SUMMARY_METRICS_REQUEST
,
];
app/assets/javascripts/cycle_analytics/store/mutations.js
View file @
9e46d9e3
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
{
DEFAULT_DAYS_TO_DISPLAY
}
from
'
../constants
'
;
import
{
decorateData
,
formatMedianValues
,
calculateFormattedDayInPast
}
from
'
../utils
'
;
import
{
formatMedianValues
,
calculateFormattedDayInPast
}
from
'
../utils
'
;
import
*
as
types
from
'
./mutation_types
'
;
export
default
{
...
...
@@ -49,9 +49,7 @@ export default {
state
.
hasError
=
false
;
},
[
types
.
RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS
](
state
,
data
)
{
const
{
summary
}
=
decorateData
(
data
);
state
.
permissions
=
data
?.
permissions
||
{};
state
.
summary
=
summary
;
state
.
hasError
=
false
;
},
[
types
.
RECEIVE_CYCLE_ANALYTICS_DATA_ERROR
](
state
)
{
...
...
app/assets/javascripts/cycle_analytics/utils.js
View file @
9e46d9e3
...
...
@@ -16,15 +16,6 @@ export const removeFlash = (type = 'alert') => {
}
};
const
mapToSummary
=
({
value
,
...
rest
})
=>
({
...
rest
,
value
:
value
||
'
-
'
});
export
const
decorateData
=
(
data
=
{})
=>
{
const
{
summary
}
=
data
;
return
{
summary
:
summary
?.
map
((
item
)
=>
mapToSummary
(
item
))
||
[],
};
};
/**
* Takes the stages and median data, combined with the selected stage, to build an
* array which is formatted to proivde the data required for the path navigation.
...
...
ee/app/assets/javascripts/analytics/cycle_analytics/constants.js
View file @
9e46d9e3
import
{
getGroupValueStreamSummaryData
,
getGroupValueStreamTimeSummaryData
,
}
from
'
ee/api/analytics_api
'
;
import
{
getGroupValueStreamMetrics
}
from
'
ee/api/analytics_api
'
;
import
{
METRIC_TYPE_SUMMARY
,
METRIC_TYPE_TIME_SUMMARY
}
from
'
~/api/analytics_api
'
;
import
{
OVERVIEW_STAGE_ID
}
from
'
~/cycle_analytics/constants
'
;
import
{
__
}
from
'
~/locale
'
;
...
...
@@ -34,11 +32,13 @@ export const OVERVIEW_STAGE_CONFIG = {
export
const
METRICS_REQUESTS
=
[
{
request
:
getGroupValueStreamTimeSummaryData
,
endpoint
:
METRIC_TYPE_TIME_SUMMARY
,
request
:
getGroupValueStreamMetrics
,
name
:
__
(
'
time summary
'
),
},
{
request
:
getGroupValueStreamSummaryData
,
endpoint
:
METRIC_TYPE_SUMMARY
,
request
:
getGroupValueStreamMetrics
,
name
:
__
(
'
recent activity
'
),
},
];
ee/app/assets/javascripts/api/analytics_api.js
View file @
9e46d9e3
import
{
METRIC_TYPE_SUMMARY
}
from
'
~/api/analytics_api
'
;
import
{
buildApiUrl
}
from
'
~/api/api_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
joinPaths
}
from
'
~/lib/utils/url_utility
'
;
...
...
@@ -22,8 +23,8 @@ export const getGroupValueStreamStageMedian = (
return
axios
.
get
(
`
${
stageBase
}
/median`
,
{
params
});
};
export
const
getGroupValueStream
SummaryData
=
(
groupId
,
params
=
{})
=>
axios
.
get
(
joinPaths
(
buildGroupValueStreamPath
({
groupId
}),
'
summary
'
),
{
params
});
export
const
getGroupValueStreamTimeSummaryData
=
(
groupId
,
params
=
{})
=>
axios
.
get
(
joinPaths
(
buildGroupValueStreamPath
({
groupId
}),
'
time_summary
'
),
{
params
});
export
const
getGroupValueStream
Metrics
=
({
endpoint
=
METRIC_TYPE_SUMMARY
,
requestPath
:
groupId
,
params
=
{},
})
=>
axios
.
get
(
joinPaths
(
buildGroupValueStreamPath
({
groupId
}),
endpoint
),
{
params
});
spec/frontend/cycle_analytics/store/mutations_spec.js
View file @
9e46d9e3
...
...
@@ -6,8 +6,6 @@ import {
selectedStage
,
rawIssueEvents
,
issueEvents
,
rawData
,
convertedData
,
selectedValueStream
,
rawValueStreamStages
,
valueStreamStages
,
...
...
@@ -90,18 +88,17 @@ describe('Project Value Stream Analytics mutations', () => {
});
it
.
each
`
mutation | payload | stateKey | value
${
types
.
SET_DATE_RANGE
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
|
${
'
daysInPast
'
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
${
types
.
SET_DATE_RANGE
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
|
${
'
createdAfter
'
}
|
${
mockCreatedAfter
}
${
types
.
SET_DATE_RANGE
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
|
${
'
createdBefore
'
}
|
${
mockCreatedBefore
}
${
types
.
SET_LOADING
}
|
${
true
}
|
${
'
isLoading
'
}
|
${
true
}
${
types
.
SET_LOADING
}
|
${
false
}
|
${
'
isLoading
'
}
|
${
false
}
${
types
.
SET_SELECTED_VALUE_STREAM
}
|
${
selectedValueStream
}
|
${
'
selectedValueStream
'
}
|
${
selectedValueStream
}
${
types
.
RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS
}
|
${
rawData
}
|
${
'
summary
'
}
|
${
convertedData
.
summary
}
${
types
.
RECEIVE_VALUE_STREAMS_SUCCESS
}
|
${[
selectedValueStream
]}
|
${
'
valueStreams
'
}
|
${[
selectedValueStream
]}
${
types
.
RECEIVE_VALUE_STREAM_STAGES_SUCCESS
}
|
${{
stages
:
rawValueStreamStages
}
} |
${
'
stages
'
}
|
${
valueStreamStages
}
${
types
.
RECEIVE_STAGE_MEDIANS_SUCCESS
}
|
${
rawStageMedians
}
|
${
'
medians
'
}
|
${
formattedStageMedians
}
${
types
.
RECEIVE_STAGE_COUNTS_SUCCESS
}
|
${
rawStageCounts
}
|
${
'
stageCounts
'
}
|
${
stageCounts
}
mutation | payload | stateKey | value
${
types
.
SET_DATE_RANGE
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
|
${
'
daysInPast
'
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
${
types
.
SET_DATE_RANGE
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
|
${
'
createdAfter
'
}
|
${
mockCreatedAfter
}
${
types
.
SET_DATE_RANGE
}
|
${
DEFAULT_DAYS_TO_DISPLAY
}
|
${
'
createdBefore
'
}
|
${
mockCreatedBefore
}
${
types
.
SET_LOADING
}
|
${
true
}
|
${
'
isLoading
'
}
|
${
true
}
${
types
.
SET_LOADING
}
|
${
false
}
|
${
'
isLoading
'
}
|
${
false
}
${
types
.
SET_SELECTED_VALUE_STREAM
}
|
${
selectedValueStream
}
|
${
'
selectedValueStream
'
}
|
${
selectedValueStream
}
${
types
.
RECEIVE_VALUE_STREAMS_SUCCESS
}
|
${[
selectedValueStream
]}
|
${
'
valueStreams
'
}
|
${[
selectedValueStream
]}
${
types
.
RECEIVE_VALUE_STREAM_STAGES_SUCCESS
}
|
${{
stages
:
rawValueStreamStages
}
} |
${
'
stages
'
}
|
${
valueStreamStages
}
${
types
.
RECEIVE_STAGE_MEDIANS_SUCCESS
}
|
${
rawStageMedians
}
|
${
'
medians
'
}
|
${
formattedStageMedians
}
${
types
.
RECEIVE_STAGE_COUNTS_SUCCESS
}
|
${
rawStageCounts
}
|
${
'
stageCounts
'
}
|
${
stageCounts
}
`
(
'
$mutation with $payload will set $stateKey to $value
'
,
({
mutation
,
payload
,
stateKey
,
value
})
=>
{
...
...
spec/frontend/cycle_analytics/utils_spec.js
View file @
9e46d9e3
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
import
{
decorateData
,
transformStagesForPathNavigation
,
timeSummaryForPathNavigation
,
medianTimeToParsedSeconds
,
...
...
@@ -12,8 +11,6 @@ import {
import
{
slugify
}
from
'
~/lib/utils/text_utility
'
;
import
{
selectedStage
,
rawData
,
convertedData
,
allowedStages
,
stageMedians
,
pathNavIssueMetric
,
...
...
@@ -22,22 +19,6 @@ import {
}
from
'
./mock_data
'
;
describe
(
'
Value stream analytics utils
'
,
()
=>
{
describe
(
'
decorateData
'
,
()
=>
{
const
result
=
decorateData
(
rawData
);
it
(
'
returns the summary data
'
,
()
=>
{
expect
(
result
.
summary
).
toEqual
(
convertedData
.
summary
);
});
it
(
'
returns `-` for summary data that has no value
'
,
()
=>
{
const
singleSummaryResult
=
decorateData
({
stats
:
[],
permissions
:
{
issue
:
true
},
summary
:
[{
value
:
null
,
title
:
'
Commits
'
}],
});
expect
(
singleSummaryResult
.
summary
).
toEqual
([{
value
:
'
-
'
,
title
:
'
Commits
'
}]);
});
});
describe
(
'
transformStagesForPathNavigation
'
,
()
=>
{
const
stages
=
allowedStages
;
const
response
=
transformStagesForPathNavigation
({
...
...
spec/frontend/cycle_analytics/value_stream_metrics_spec.js
View file @
9e46d9e3
...
...
@@ -2,6 +2,7 @@ import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import
{
GlSingleStat
}
from
'
@gitlab/ui/dist/charts
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
METRIC_TYPE_SUMMARY
}
from
'
~/api/analytics_api
'
;
import
ValueStreamMetrics
from
'
~/cycle_analytics/components/value_stream_metrics.vue
'
;
import
createFlash
from
'
~/flash
'
;
import
{
group
,
metricsData
}
from
'
./mock_data
'
;
...
...
@@ -14,84 +15,97 @@ describe('ValueStreamMetrics', () => {
const
{
full_path
:
requestPath
}
=
group
;
const
fakeReqName
=
'
Mock metrics
'
;
const
metricsRequestFactory
=
()
=>
({
request
:
mockGetValueStreamSummaryMetrics
,
endpoint
:
METRIC_TYPE_SUMMARY
,
name
:
fakeReqName
,
});
const
createComponent
=
({
requestParams
=
{}
}
=
{})
=>
{
return
shallowMount
(
ValueStreamMetrics
,
{
propsData
:
{
requestPath
,
requestParams
,
requests
:
[
{
request
:
mockGetValueStreamSummaryMetrics
,
name
:
fakeReqName
}
],
requests
:
[
metricsRequestFactory
()
],
},
});
};
const
findMetrics
=
()
=>
wrapper
.
findAllComponents
(
GlSingleStat
);
const
expectToHaveRequest
=
(
fields
)
=>
{
expect
(
mockGetValueStreamSummaryMetrics
).
toHaveBeenCalledWith
({
endpoint
:
METRIC_TYPE_SUMMARY
,
requestPath
,
...
fields
,
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
will display a loader with pending requests
'
,
async
()
=>
{
mockGetValueStreamSummaryMetrics
=
jest
.
fn
().
mockResolvedValue
({
data
:
metricsData
});
wrapper
=
createComponent
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
find
(
GlSkeletonLoading
).
exists
()).
toBe
(
true
);
});
describe
(
'
with successful requests
'
,
()
=>
{
beforeEach
(
async
()
=>
{
beforeEach
(()
=>
{
mockGetValueStreamSummaryMetrics
=
jest
.
fn
().
mockResolvedValue
({
data
:
metricsData
});
wrapper
=
createComponent
();
await
waitForPromises
();
});
it
(
'
fetches data for the `getValueStreamSummaryMetrics` request
'
,
()
=>
{
expect
(
mockGetValueStreamSummaryMetrics
).
toHaveBeenCalledWith
(
requestPath
,
{});
});
it
(
'
will display a loader with pending requests
'
,
async
()
=>
{
await
wrapper
.
vm
.
$nextTick
();
it
.
each
`
index | value | title | unit
${
0
}
|
${
metricsData
[
0
].
value
}
|
${
metricsData
[
0
].
title
}
|
${
metricsData
[
0
].
unit
}
${
1
}
|
${
metricsData
[
1
].
value
}
|
${
metricsData
[
1
].
title
}
|
${
metricsData
[
1
].
unit
}
${
2
}
|
${
metricsData
[
2
].
value
}
|
${
metricsData
[
2
].
title
}
|
${
metricsData
[
2
].
unit
}
${
3
}
|
${
metricsData
[
3
].
value
}
|
${
metricsData
[
3
].
title
}
|
${
metricsData
[
3
].
unit
}
`
(
'
renders a single stat component for the $title with value and unit
'
,
({
index
,
value
,
title
,
unit
})
=>
{
const
metric
=
findMetrics
().
at
(
index
);
const
expectedUnit
=
unit
??
''
;
expect
(
metric
.
props
(
'
value
'
)).
toBe
(
value
);
expect
(
metric
.
props
(
'
title
'
)).
toBe
(
title
);
expect
(
metric
.
props
(
'
unit
'
)).
toBe
(
expectedUnit
);
},
);
it
(
'
will not display a loading icon
'
,
()
=>
{
expect
(
wrapper
.
find
(
GlSkeletonLoading
).
exists
()).
toBe
(
false
);
expect
(
wrapper
.
find
(
GlSkeletonLoading
).
exists
()).
toBe
(
true
);
});
describe
(
'
with
additional params
'
,
()
=>
{
describe
(
'
with
data loaded
'
,
()
=>
{
beforeEach
(
async
()
=>
{
wrapper
=
createComponent
({
requestParams
:
{
'
project_ids[]
'
:
[
1
],
created_after
:
'
2020-01-01
'
,
created_before
:
'
2020-02-01
'
,
},
});
await
waitForPromises
();
});
it
(
'
fetches data for the `getValueStreamSummaryMetrics` request
'
,
()
=>
{
expect
(
mockGetValueStreamSummaryMetrics
).
toHaveBeenCalledWith
(
requestPath
,
{
'
project_ids[]
'
:
[
1
],
created_after
:
'
2020-01-01
'
,
created_before
:
'
2020-02-01
'
,
it
(
'
fetches data from the value stream analytics endpoint
'
,
()
=>
{
expectToHaveRequest
({
params
:
{}
});
});
it
.
each
`
index | value | title | unit
${
0
}
|
${
metricsData
[
0
].
value
}
|
${
metricsData
[
0
].
title
}
|
${
metricsData
[
0
].
unit
}
${
1
}
|
${
metricsData
[
1
].
value
}
|
${
metricsData
[
1
].
title
}
|
${
metricsData
[
1
].
unit
}
${
2
}
|
${
metricsData
[
2
].
value
}
|
${
metricsData
[
2
].
title
}
|
${
metricsData
[
2
].
unit
}
${
3
}
|
${
metricsData
[
3
].
value
}
|
${
metricsData
[
3
].
title
}
|
${
metricsData
[
3
].
unit
}
`
(
'
renders a single stat component for the $title with value and unit
'
,
({
index
,
value
,
title
,
unit
})
=>
{
const
metric
=
findMetrics
().
at
(
index
);
expect
(
metric
.
props
()).
toMatchObject
({
value
,
title
,
unit
:
unit
??
''
});
},
);
it
(
'
will not display a loading icon
'
,
()
=>
{
expect
(
wrapper
.
find
(
GlSkeletonLoading
).
exists
()).
toBe
(
false
);
});
describe
(
'
with additional params
'
,
()
=>
{
beforeEach
(
async
()
=>
{
wrapper
=
createComponent
({
requestParams
:
{
'
project_ids[]
'
:
[
1
],
created_after
:
'
2020-01-01
'
,
created_before
:
'
2020-02-01
'
,
},
});
await
waitForPromises
();
});
it
(
'
fetches data for the `getValueStreamSummaryMetrics` request
'
,
()
=>
{
expectToHaveRequest
({
params
:
{
'
project_ids[]
'
:
[
1
],
created_after
:
'
2020-01-01
'
,
created_before
:
'
2020-02-01
'
,
},
});
});
});
});
...
...
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