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
ffdce821
Commit
ffdce821
authored
Mar 11, 2020
by
Martin Wortschack
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add group activity analytics card
- Render metric card for issues count and MR count
parent
0c30b235
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
262 additions
and
1 deletion
+262
-1
ee/app/assets/javascripts/analytics/group_analytics/components/group_activity_card.vue
...lytics/group_analytics/components/group_activity_card.vue
+56
-1
ee/app/assets/javascripts/api.js
ee/app/assets/javascripts/api.js
+12
-0
ee/spec/frontend/analytics/group_analytics/components/__snapshots__/group_activity_card_spec.js.snap
...components/__snapshots__/group_activity_card_spec.js.snap
+60
-0
ee/spec/frontend/analytics/group_analytics/components/group_activity_card_spec.js
...cs/group_analytics/components/group_activity_card_spec.js
+86
-0
ee/spec/frontend/api_spec.js
ee/spec/frontend/api_spec.js
+36
-0
locale/gitlab.pot
locale/gitlab.pot
+12
-0
No files found.
ee/app/assets/javascripts/analytics/group_analytics/components/group_activity_card.vue
View file @
ffdce821
<
script
>
import
Api
from
'
ee/api
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
createFlash
from
'
~/flash
'
;
import
MetricCard
from
'
../../shared/components/metric_card.vue
'
;
export
default
{
name
:
'
GroupActivityCard
'
,
components
:
{
MetricCard
,
},
props
:
{
groupFullPath
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
isLoading
:
false
,
metrics
:
{
mergeRequests
:
{
value
:
null
,
label
:
s__
(
'
GroupActivyMetrics|Merge Requests created
'
)
},
issues
:
{
value
:
null
,
label
:
s__
(
'
GroupActivyMetrics|Issues created
'
)
},
},
};
},
computed
:
{
metricsArray
()
{
return
Object
.
entries
(
this
.
metrics
).
map
(([
key
,
obj
])
=>
{
const
{
value
,
label
}
=
obj
;
return
{
key
,
value
,
label
,
};
});
},
},
created
()
{
this
.
fetchMetrics
(
this
.
groupFullPath
);
},
methods
:
{
fetchMetrics
(
groupPath
)
{
this
.
isLoading
=
true
;
return
Promise
.
all
([
Api
.
groupActivityMergeRequestsCount
(
groupPath
),
Api
.
groupActivityIssuesCount
(
groupPath
),
])
.
then
(([
mrResponse
,
issuesResponse
])
=>
{
this
.
metrics
.
mergeRequests
.
value
=
mrResponse
.
data
.
merge_requests_count
;
this
.
metrics
.
issues
.
value
=
issuesResponse
.
data
.
issues_count
;
this
.
isLoading
=
false
;
})
.
catch
(()
=>
{
createFlash
(
__
(
'
Failed to load group activity metrics. Please try again.
'
));
this
.
isLoading
=
false
;
});
},
},
};
</
script
>
<
template
>
<div></div>
<metric-card
:title=
"s__('GroupActivyMetrics|Recent activity (last 90 days)')"
:metrics=
"metricsArray"
:is-loading=
"isLoading"
/>
</
template
>
ee/app/assets/javascripts/api.js
View file @
ffdce821
...
...
@@ -24,6 +24,8 @@ export default {
'
/-/analytics/value_stream_analytics/stages/:stage_id/duration_chart
'
,
cycleAnalyticsGroupLabelsPath
:
'
/api/:version/groups/:namespace_path/labels
'
,
codeReviewAnalyticsPath
:
'
/api/:version/analytics/code_review
'
,
groupActivityIssuesPath
:
'
/api/:version/analytics/group_activity/issues_count
'
,
groupActivityMergeRequestsPath
:
'
/api/:version/analytics/group_activity/merge_requests_count
'
,
countriesPath
:
'
/-/countries
'
,
countryStatesPath
:
'
/-/country_states
'
,
paymentFormPath
:
'
/-/subscriptions/payment_form
'
,
...
...
@@ -216,6 +218,16 @@ export default {
return
axios
.
get
(
url
,
{
params
});
},
groupActivityMergeRequestsCount
(
groupPath
)
{
const
url
=
Api
.
buildUrl
(
this
.
groupActivityMergeRequestsPath
);
return
axios
.
get
(
url
,
{
params
:
{
group_path
:
groupPath
}
});
},
groupActivityIssuesCount
(
groupPath
)
{
const
url
=
Api
.
buildUrl
(
this
.
groupActivityIssuesPath
);
return
axios
.
get
(
url
,
{
params
:
{
group_path
:
groupPath
}
});
},
getGeoReplicableItems
(
replicable
,
params
=
{})
{
const
url
=
Api
.
buildUrl
(
this
.
geoReplicationPath
).
replace
(
'
:replicable
'
,
replicable
);
return
axios
.
get
(
url
,
{
params
});
...
...
ee/spec/frontend/analytics/group_analytics/components/__snapshots__/group_activity_card_spec.js.snap
0 → 100644
View file @
ffdce821
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GroupActivity component matches the snapshot 1`] = `
<div
class="card"
>
<!---->
<div
class="card-header"
>
<strong>
Recent activity (last 90 days)
</strong>
</div>
<div
class="card-body"
>
<!---->
<!---->
<div
class="d-flex"
>
<div
class="flex-grow text-center"
>
<h3
class="my-2"
>
10
</h3>
<p
class="text-secondary gl-font-size-small mb-2"
>
Merge Requests created
</p>
</div>
<div
class="flex-grow text-center"
>
<h3
class="my-2"
>
20
</h3>
<p
class="text-secondary gl-font-size-small mb-2"
>
Issues created
</p>
</div>
</div>
</div>
<!---->
<!---->
</div>
`;
ee/spec/frontend/analytics/group_analytics/components/group_activity_card_spec.js
0 → 100644
View file @
ffdce821
import
{
mount
}
from
'
@vue/test-utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
Api
from
'
ee/api
'
;
import
GroupActivityCard
from
'
ee/analytics/group_analytics/components/group_activity_card.vue
'
;
import
MetricCard
from
'
ee/analytics/shared/components/metric_card.vue
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
const
TEST_GROUP_ID
=
'
gitlab-org
'
;
const
TEST_MERGE_REQUESTS_COUNT
=
{
data
:
{
merge_requests_count
:
10
}
};
const
TEST_ISSUES_COUNT
=
{
data
:
{
issues_count
:
20
}
};
describe
(
'
GroupActivity component
'
,
()
=>
{
let
wrapper
;
let
mock
;
const
createComponent
=
()
=>
{
wrapper
=
mount
(
GroupActivityCard
,
{
propsData
:
{
groupFullPath
:
TEST_GROUP_ID
,
},
});
};
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
jest
.
spyOn
(
Api
,
'
groupActivityMergeRequestsCount
'
)
.
mockReturnValue
(
Promise
.
resolve
(
TEST_MERGE_REQUESTS_COUNT
));
jest
.
spyOn
(
Api
,
'
groupActivityIssuesCount
'
).
mockReturnValue
(
Promise
.
resolve
(
TEST_ISSUES_COUNT
));
});
afterEach
(()
=>
{
wrapper
.
destroy
();
mock
.
restore
();
});
const
findMetricCard
=
()
=>
wrapper
.
find
(
MetricCard
);
it
(
'
matches the snapshot
'
,
()
=>
{
createComponent
();
return
wrapper
.
vm
.
$nextTick
()
.
then
(
waitForPromises
)
.
then
(()
=>
{
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
});
it
(
'
fetches MR and issue count and updates isLoading properly
'
,
()
=>
{
createComponent
();
expect
(
wrapper
.
vm
.
isLoading
).
toBe
(
true
);
return
wrapper
.
vm
.
$nextTick
()
.
then
(()
=>
{
expect
(
Api
.
groupActivityMergeRequestsCount
).
toHaveBeenCalledWith
(
TEST_GROUP_ID
);
expect
(
Api
.
groupActivityIssuesCount
).
toHaveBeenCalledWith
(
TEST_GROUP_ID
);
waitForPromises
();
})
.
then
(()
=>
{
expect
(
wrapper
.
vm
.
isLoading
).
toBe
(
false
);
expect
(
wrapper
.
vm
.
metrics
.
mergeRequests
.
value
).
toBe
(
10
);
expect
(
wrapper
.
vm
.
metrics
.
issues
.
value
).
toBe
(
20
);
});
});
it
(
'
passes the metrics array to the metric card
'
,
()
=>
{
createComponent
();
return
wrapper
.
vm
.
$nextTick
()
.
then
(
waitForPromises
)
.
then
(()
=>
{
expect
(
findMetricCard
().
props
(
'
metrics
'
)).
toEqual
([
{
key
:
'
mergeRequests
'
,
value
:
10
,
label
:
'
Merge Requests created
'
},
{
key
:
'
issues
'
,
value
:
20
,
label
:
'
Issues created
'
},
]);
});
});
});
ee/spec/frontend/api_spec.js
View file @
ffdce821
...
...
@@ -550,6 +550,42 @@ describe('Api', () => {
});
});
describe
(
'
GroupActivityAnalytics
'
,
()
=>
{
const
groupId
=
'
gitlab-org
'
;
describe
(
'
groupActivityMergeRequestsCount
'
,
()
=>
{
it
(
'
fetches the number of MRs created for a given group
'
,
()
=>
{
const
response
=
{
merge_requests_count
:
10
};
const
expectedUrl
=
`
${
dummyUrlRoot
}
/api/
${
dummyApiVersion
}
/analytics/group_activity/merge_requests_count`
;
jest
.
spyOn
(
Api
,
'
buildUrl
'
).
mockReturnValue
(
expectedUrl
);
jest
.
spyOn
(
axios
,
'
get
'
);
mock
.
onGet
(
expectedUrl
).
reply
(
200
,
response
);
return
Api
.
groupActivityMergeRequestsCount
(
groupId
).
then
(({
data
})
=>
{
expect
(
data
).
toEqual
(
response
);
expect
(
axios
.
get
).
toHaveBeenCalledWith
(
expectedUrl
,
{
params
:
{
group_path
:
groupId
}
});
});
});
});
describe
(
'
groupActivityIssuesCount
'
,
()
=>
{
it
(
'
fetches the number of issues created for a given group
'
,
()
=>
{
const
response
=
{
issues_count
:
20
};
const
expectedUrl
=
`
${
dummyUrlRoot
}
/api/
${
dummyApiVersion
}
/analytics/group_activity/issues_count`
;
jest
.
spyOn
(
Api
,
'
buildUrl
'
).
mockReturnValue
(
expectedUrl
);
jest
.
spyOn
(
axios
,
'
get
'
);
mock
.
onGet
(
expectedUrl
).
replyOnce
(
200
,
response
);
return
Api
.
groupActivityIssuesCount
(
groupId
).
then
(({
data
})
=>
{
expect
(
data
).
toEqual
(
response
);
expect
(
axios
.
get
).
toHaveBeenCalledWith
(
expectedUrl
,
{
params
:
{
group_path
:
groupId
}
});
});
});
});
});
describe
(
'
GeoReplicable
'
,
()
=>
{
let
expectedUrl
;
let
apiResponse
;
...
...
locale/gitlab.pot
View file @
ffdce821
...
...
@@ -8458,6 +8458,9 @@ msgstr ""
msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to load group activity metrics. Please try again."
msgstr ""
msgid "Failed to load groups & users."
msgstr ""
...
...
@@ -10015,6 +10018,15 @@ msgstr ""
msgid "Group: %{name}"
msgstr ""
msgid "GroupActivyMetrics|Issues created"
msgstr ""
msgid "GroupActivyMetrics|Merge Requests created"
msgstr ""
msgid "GroupActivyMetrics|Recent activity (last 90 days)"
msgstr ""
msgid "GroupRoadmap|%{dateWord} – No end date"
msgstr ""
...
...
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