Commit a3a6a5c0 authored by Filipa Lacerda's avatar Filipa Lacerda

Adds total usage in storage quotas

Renders the storage usage total in the
usage quotas page
Updates the graphql query and adds it to the
vue application

Adds jsdoc to the query update function
parent d84472a1
<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,
});
...@@ -16553,6 +16553,9 @@ msgstr "" ...@@ -16553,6 +16553,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