Commit 678286b8 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '14693-storage-usage' into 'master'

Adds total usage in storage quotas

Closes #14693

See merge request gitlab-org/gitlab-ee!16048
parents 697cb8d3 a3a6a5c0
<script> <script>
import { GlLink } from '@gitlab/ui';
import Project from './project.vue'; import Project from './project.vue';
import query from '../queries/storage.graphql'; import query from '../queries/storage.graphql';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import Icon from '~/vue_shared/components/icon.vue';
export default { export default {
components: { components: {
Project, Project,
GlLink,
Icon,
}, },
props: { props: {
namespacePath: { namespacePath: {
type: String, type: String,
required: true, required: true,
}, },
helpPagePath: {
type: String,
required: true,
},
}, },
apollo: { apollo: {
namespace: { namespace: {
...@@ -20,8 +29,18 @@ export default { ...@@ -20,8 +29,18 @@ export default {
fullPath: this.namespacePath, fullPath: this.namespacePath,
}; };
}, },
/**
* `rootStorageStatistics` will be sent as null until an
* event happens to trigger the storage count.
* For that reason we have to verify if `storageSize` is sent or
* if we should render N/A
*/
update: data => ({ update: data => ({
projects: data.namespace.projects.edges.map(({ node }) => node), projects: data.namespace.projects.edges.map(({ node }) => node),
totalUsage:
data.namespace.rootStorageStatistics && data.namespace.rootStorageStatistics.storageSize
? numberToHumanSize(data.namespace.rootStorageStatistics.storageSize)
: 'N/A',
}), }),
}, },
}, },
...@@ -33,19 +52,40 @@ export default { ...@@ -33,19 +52,40 @@ export default {
}; };
</script> </script>
<template> <template>
<div class="ci-table" role="grid"> <div>
<div <div class="pipeline-quota container-fluid">
class="gl-responsive-table-row table-row-header bg-gray-light pl-2 border-top mt-3 lh-100" <div class="row">
role="row" <div class="col-sm-6">
> <strong>{{ s__('UsageQuota|Usage since') }}</strong>
<div class="table-section section-70 font-weight-bold" role="columnheader"> <div>
{{ __('Project') }} <span class="js-total-usage">
</div> {{ namespace.totalUsage }}
<div class="table-section section-30 font-weight-bold" role="columnheader"> <gl-link
{{ __('Usage') }} :href="helpPagePath"
target="_blank"
:aria-label="__('Usage quotas help link')"
>
<icon name="question" :size="12" />
</gl-link>
</span>
</div>
</div>
</div> </div>
</div> </div>
<div class="ci-table" role="grid">
<div
class="gl-responsive-table-row table-row-header bg-gray-light pl-2 border-top mt-3 lh-100"
role="row"
>
<div class="table-section section-70 font-weight-bold" role="columnheader">
{{ __('Project') }}
</div>
<div class="table-section section-30 font-weight-bold" role="columnheader">
{{ __('Usage') }}
</div>
</div>
<project v-for="project in namespace.projects" :key="project.id" :project="project" /> <project v-for="project in namespace.projects" :key="project.id" :project="project" />
</div>
</div> </div>
</template> </template>
...@@ -7,7 +7,7 @@ Vue.use(VueApollo); ...@@ -7,7 +7,7 @@ Vue.use(VueApollo);
export default () => { export default () => {
const el = document.getElementById('js-storage-counter-app'); const el = document.getElementById('js-storage-counter-app');
const { namespacePath } = el.dataset; const { namespacePath, helpPagePath } = el.dataset;
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(),
...@@ -20,6 +20,7 @@ export default () => { ...@@ -20,6 +20,7 @@ export default () => {
return h(App, { return h(App, {
props: { props: {
namespacePath, namespacePath,
helpPagePath,
}, },
}); });
}, },
......
query getStorageCounter($fullPath: ID!) { query getStorageCounter($fullPath: ID!) {
namespace(fullPath: $fullPath) { namespace(fullPath: $fullPath) {
id id
rootStorageStatistics {
storageSize
}
projects(includeSubgroups: true) { projects(includeSubgroups: true) {
edges { edges {
node { node {
......
...@@ -23,5 +23,5 @@ ...@@ -23,5 +23,5 @@
= render "namespaces/pipelines_quota/list", = render "namespaces/pipelines_quota/list",
locals: { namespace: @group, projects: @projects } locals: { namespace: @group, projects: @projects }
.tab-pane#storage-quota-tab .tab-pane#storage-quota-tab
#js-storage-counter-app{ data: { namespace_path: @group.full_path } } #js-storage-counter-app{ data: { namespace_path: @group.full_path, help_page_path: help_page_path('user/group', anchor: 'storage-usage-quota-starter')} }
---
title: Adds total usage information to the usage quotas page
merge_request:
author:
type: added
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import StorageApp from 'ee/storage_counter/components/app.vue'; import StorageApp from 'ee/storage_counter/components/app.vue';
import Project from 'ee/storage_counter/components/project.vue'; import Project from 'ee/storage_counter/components/project.vue';
import { projects } from '../data'; import { projects, withRootStorageStatistics } from '../data';
describe('Storage counter app', () => { describe('Storage counter app', () => {
let wrapper; let wrapper;
...@@ -16,19 +16,47 @@ describe('Storage counter app', () => { ...@@ -16,19 +16,47 @@ describe('Storage counter app', () => {
}; };
wrapper = shallowMount(StorageApp, { wrapper = shallowMount(StorageApp, {
propsData: { namespacePath: 'h5bp' }, propsData: { namespacePath: 'h5bp', helpPagePath: 'help' },
mocks: { $apollo }, mocks: { $apollo },
sync: true,
}); });
} }
beforeEach(() => { beforeEach(() => {
createComponent(); createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders the 2 projects', () => {
wrapper.setData({ wrapper.setData({
namespace: projects, namespace: projects,
}); });
});
it('renders the 2 projects', () => {
expect(wrapper.findAll(Project).length).toEqual(2); expect(wrapper.findAll(Project).length).toEqual(2);
}); });
describe('with rootStorageStatistics information', () => {
it('renders total usage', () => {
wrapper.setData({
namespace: withRootStorageStatistics,
});
expect(wrapper.find('.js-total-usage').text()).toContain(
withRootStorageStatistics.totalUsage,
);
});
});
describe('without rootStorageStatistics information', () => {
it('renders N/A', () => {
wrapper.setData({
namespace: projects,
});
expect(wrapper.find('.js-total-usage').text()).toContain('N/A');
});
});
}); });
// eslint-disable-next-line import/prefer-default-export
export const projects = { export const projects = {
totalUsage: 'N/A',
projects: [ projects: [
{ {
id: '24', id: '24',
...@@ -35,3 +35,7 @@ export const projects = { ...@@ -35,3 +35,7 @@ export const projects = {
}, },
], ],
}; };
export const withRootStorageStatistics = Object.assign({}, projects, {
totalUsage: 3261070,
});
...@@ -16634,6 +16634,9 @@ msgstr "" ...@@ -16634,6 +16634,9 @@ msgstr ""
msgid "Usage ping is not enabled" msgid "Usage ping is not enabled"
msgstr "" msgstr ""
msgid "Usage quotas help link"
msgstr ""
msgid "Usage statistics" msgid "Usage statistics"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment