Skip to content
Snippets Groups Projects
Commit b041321a authored by Manoj M J's avatar Manoj M J Committed by Ash McKenzie
Browse files

Application Statistics API

This change implements Application
Statistics API
parent e3763f9c
No related branches found
No related tags found
No related merge requests found
Showing
with 319 additions and 38 deletions
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import statisticsLabels from '../constants';
export default {
components: {
GlLoadingIcon,
},
data() {
return {
statisticsLabels,
};
},
computed: {
...mapState(['isLoading', 'statistics']),
...mapGetters(['getStatistics']),
},
mounted() {
this.fetchStatistics();
},
methods: {
...mapActions(['fetchStatistics']),
},
};
</script>
<template>
<div class="info-well">
<div class="well-segment admin-well admin-well-statistics">
<h4>{{ __('Statistics') }}</h4>
<gl-loading-icon v-if="isLoading" size="md" class="my-3" />
<template v-else>
<p
v-for="statistic in getStatistics(statisticsLabels)"
:key="statistic.key"
class="js-stats"
>
{{ statistic.label }}
<span class="light float-right">{{ statistic.value }}</span>
</p>
</template>
</div>
</div>
</template>
import { s__ } from '~/locale';
const statisticsLabels = {
forks: s__('AdminStatistics|Forks'),
issues: s__('AdminStatistics|Issues'),
mergeRequests: s__('AdminStatistics|Merge Requests'),
notes: s__('AdminStatistics|Notes'),
snippets: s__('AdminStatistics|Snippets'),
sshKeys: s__('AdminStatistics|SSH Keys'),
milestones: s__('AdminStatistics|Milestones'),
activeUsers: s__('AdminStatistics|Active Users'),
};
export default statisticsLabels;
import Vue from 'vue';
import StatisticsPanelApp from './components/app.vue';
import createStore from './store';
export default function(el) {
if (!el) {
return false;
}
const store = createStore();
return new Vue({
el,
store,
components: {
StatisticsPanelApp,
},
render(h) {
return h(StatisticsPanelApp);
},
});
}
import Api from '~/api';
import { s__ } from '~/locale';
import createFlash from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import * as types from './mutation_types';
export const requestStatistics = ({ commit }) => commit(types.REQUEST_STATISTICS);
export const fetchStatistics = ({ dispatch }) => {
dispatch('requestStatistics');
Api.adminStatistics()
.then(({ data }) => {
dispatch('receiveStatisticsSuccess', convertObjectPropsToCamelCase(data, { deep: true }));
})
.catch(error => dispatch('receiveStatisticsError', error));
};
export const receiveStatisticsSuccess = ({ commit }, statistics) =>
commit(types.RECEIVE_STATISTICS_SUCCESS, statistics);
export const receiveStatisticsError = ({ commit }, error) => {
commit(types.RECEIVE_STATISTICS_ERROR, error);
createFlash(s__('AdminDashboard|Error loading the statistics. Please try again'));
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
/**
* Merges the statisticsLabels with the state's data
* and returns an array of the following form:
* [{ key: "forks", label: "Forks", value: 50 }]
*/
export const getStatistics = state => labels =>
Object.keys(labels).map(key => {
const result = {
key,
label: labels[key],
value: state.statistics && state.statistics[key] ? state.statistics[key] : null,
};
return result;
});
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
import state from './state';
Vue.use(Vuex);
export default () =>
new Vuex.Store({
actions,
getters,
mutations,
state: state(),
});
export const REQUEST_STATISTICS = 'REQUEST_STATISTICS';
export const RECEIVE_STATISTICS_SUCCESS = 'RECEIVE_STATISTICS_SUCCESS';
export const RECEIVE_STATISTICS_ERROR = 'RECEIVE_STATISTICS_ERROR';
import * as types from './mutation_types';
export default {
[types.REQUEST_STATISTICS](state) {
state.isLoading = true;
},
[types.RECEIVE_STATISTICS_SUCCESS](state, data) {
state.isLoading = false;
state.error = null;
state.statistics = data;
},
[types.RECEIVE_STATISTICS_ERROR](state, error) {
state.isLoading = false;
state.error = error;
},
};
export default () => ({
error: null,
isLoading: false,
statistics: null,
});
Loading
Loading
@@ -36,6 +36,7 @@ const Api = {
branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch',
createBranchPath: '/api/:version/projects/:id/repository/branches',
releasesPath: '/api/:version/projects/:id/releases',
adminStatisticsPath: 'api/:version/application/statistics',
 
group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
Loading
Loading
@@ -376,6 +377,11 @@ const Api = {
return axios.get(url);
},
 
adminStatistics() {
const url = Api.buildUrl(this.adminStatisticsPath);
return axios.get(url);
},
buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
},
Loading
Loading
import initAdmin from './admin';
import initAdminStatisticsPanel from '../../admin/statistics_panel/index';
 
document.addEventListener('DOMContentLoaded', initAdmin());
document.addEventListener('DOMContentLoaded', () => {
const statisticsPanelContainer = document.getElementById('js-admin-statistics-container');
initAdmin();
initAdminStatisticsPanel(statisticsPanelContainer);
});
Loading
Loading
@@ -3,8 +3,7 @@
class Admin::DashboardController < Admin::ApplicationController
include CountHelper
 
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
MergeRequest, Note, Snippet, Key, Milestone].freeze
COUNTED_ITEMS = [Project, User, Group].freeze
 
# rubocop: disable CodeReuse/ActiveRecord
def index
Loading
Loading
Loading
Loading
@@ -35,41 +35,7 @@
= link_to 'New group', new_admin_group_path, class: "btn btn-success"
.row
.col-md-4
.info-well
.well-segment.admin-well.admin-well-statistics
%h4 Statistics
%p
Forks
%span.light.float-right
= approximate_fork_count_with_delimiters(@counts)
%p
Issues
%span.light.float-right
= approximate_count_with_delimiters(@counts, Issue)
%p
Merge Requests
%span.light.float-right
= approximate_count_with_delimiters(@counts, MergeRequest)
%p
Notes
%span.light.float-right
= approximate_count_with_delimiters(@counts, Note)
%p
Snippets
%span.light.float-right
= approximate_count_with_delimiters(@counts, Snippet)
%p
SSH Keys
%span.light.float-right
= approximate_count_with_delimiters(@counts, Key)
%p
Milestones
%span.light.float-right
= approximate_count_with_delimiters(@counts, Milestone)
%p
Active Users
%span.light.float-right
= number_with_delimiter(User.active.count)
#js-admin-statistics-container
.col-md-4
.info-well
.well-segment.admin-well.admin-well-features
Loading
Loading
---
title: 'Admin dashboard: Fetch and render statistics async'
merge_request: 32449
author:
type: other
Loading
Loading
@@ -126,6 +126,7 @@ The following API resources are available outside of project and group contexts
| [Runners](runners.md) | `/runners` (also available for projects) |
| [Search](search.md) | `/search` (also available for groups and projects) |
| [Settings](settings.md) | `/application/settings` |
| [Statistics](statistics.md) | `/application/statistics` |
| [Sidekiq metrics](sidekiq_metrics.md) | `/sidekiq` |
| [Suggestions](suggestions.md) | `/suggestions` |
| [System hooks](system_hooks.md) | `/hooks` |
Loading
Loading
# Application statistics API
## Get current application statistics
List the current statistics of the GitLab instance. You have to be an
administrator in order to perform this action.
NOTE: **Note:**
These statistics are approximate.
```
GET /application/statistics
```
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/application/statistics
```
Example response:
```json
{
"forks": "10",
"issues": "76",
"merge_requests": "27",
"notes": "954",
"snippets": "50",
"ssh_keys": "10",
"milestones": "40",
"users": "50",
"groups": "10",
"projects": "20",
"active_users": "50"
}
```
Loading
Loading
@@ -161,6 +161,7 @@ module API
mount ::API::Settings
mount ::API::SidekiqMetrics
mount ::API::Snippets
mount ::API::Statistics
mount ::API::Submodules
mount ::API::Subscriptions
mount ::API::Suggestions
Loading
Loading
Loading
Loading
@@ -1169,6 +1169,55 @@ module API
expose :message, :starts_at, :ends_at, :color, :font
end
 
class ApplicationStatistics < Grape::Entity
include ActionView::Helpers::NumberHelper
include CountHelper
expose :forks do |counts|
approximate_fork_count_with_delimiters(counts)
end
expose :issues do |counts|
approximate_count_with_delimiters(counts, ::Issue)
end
expose :merge_requests do |counts|
approximate_count_with_delimiters(counts, ::MergeRequest)
end
expose :notes do |counts|
approximate_count_with_delimiters(counts, ::Note)
end
expose :snippets do |counts|
approximate_count_with_delimiters(counts, ::Snippet)
end
expose :ssh_keys do |counts|
approximate_count_with_delimiters(counts, ::Key)
end
expose :milestones do |counts|
approximate_count_with_delimiters(counts, ::Milestone)
end
expose :users do |counts|
approximate_count_with_delimiters(counts, ::User)
end
expose :projects do |counts|
approximate_count_with_delimiters(counts, ::Project)
end
expose :groups do |counts|
approximate_count_with_delimiters(counts, ::Group)
end
expose :active_users do |_|
number_with_delimiter(::User.active.count)
end
end
class ApplicationSetting < Grape::Entity
def self.exposed_attributes
attributes = ::ApplicationSettingsHelper.visible_attributes
Loading
Loading
# frozen_string_literal: true
module API
class Statistics < Grape::API
before { authenticated_as_admin! }
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
MergeRequest, Note, Snippet, Key, Milestone].freeze
desc 'Get the current application statistics' do
success Entities::ApplicationStatistics
end
get "application/statistics" do
counts = Gitlab::Database::Count.approximate_counts(COUNTED_ITEMS)
present counts, with: Entities::ApplicationStatistics
end
end
end
Loading
Loading
@@ -810,6 +810,9 @@ msgstr ""
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
msgstr ""
 
msgid "AdminDashboard|Error loading the statistics. Please try again"
msgstr ""
msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
msgstr ""
 
Loading
Loading
@@ -837,6 +840,30 @@ msgstr ""
msgid "AdminSettings|When creating a new environment variable it will be protected by default."
msgstr ""
 
msgid "AdminStatistics|Active Users"
msgstr ""
msgid "AdminStatistics|Forks"
msgstr ""
msgid "AdminStatistics|Issues"
msgstr ""
msgid "AdminStatistics|Merge Requests"
msgstr ""
msgid "AdminStatistics|Milestones"
msgstr ""
msgid "AdminStatistics|Notes"
msgstr ""
msgid "AdminStatistics|SSH Keys"
msgstr ""
msgid "AdminStatistics|Snippets"
msgstr ""
msgid "AdminUsers|2FA Disabled"
msgstr ""
 
Loading
Loading
@@ -10982,6 +11009,9 @@ msgstr ""
msgid "State your message to activate"
msgstr ""
 
msgid "Statistics"
msgstr ""
msgid "Status"
msgstr ""
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment