Commit 0cc72c6f authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch 'nicolasdular/storage-quota-usage-graph' into 'master'

Show usage graph for each storage type

See merge request gitlab-org/gitlab!31649
parents 6f2cb18a 12aebfff
.storage-type-usage {
&:first-child {
@include gl-rounded-top-left-base;
@include gl-rounded-bottom-left-base;
}
&:last-child {
@include gl-rounded-top-right-base;
@include gl-rounded-bottom-right-base;
}
&:not(:first-child) {
@include gl-border-l-1;
@include gl-border-l-solid;
@include gl-border-white;
}
&:not(:last-child) {
@include gl-border-r-1;
@include gl-border-r-solid;
@include gl-border-white;
}
}
<script> <script>
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import Project from './project.vue'; import Project from './project.vue';
import UsageGraph from './usage_graph.vue';
import query from '../queries/storage.graphql'; import query from '../queries/storage.graphql';
import { numberToHumanSize } from '~/lib/utils/number_utils'; import { numberToHumanSize } from '~/lib/utils/number_utils';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -10,6 +11,7 @@ export default { ...@@ -10,6 +11,7 @@ export default {
Project, Project,
GlLink, GlLink,
Icon, Icon,
UsageGraph,
}, },
props: { props: {
namespacePath: { namespacePath: {
...@@ -41,6 +43,7 @@ export default { ...@@ -41,6 +43,7 @@ export default {
data.namespace.rootStorageStatistics && data.namespace.rootStorageStatistics.storageSize data.namespace.rootStorageStatistics && data.namespace.rootStorageStatistics.storageSize
? numberToHumanSize(data.namespace.rootStorageStatistics.storageSize) ? numberToHumanSize(data.namespace.rootStorageStatistics.storageSize)
: 'N/A', : 'N/A',
rootStorageStatistics: data.namespace.rootStorageStatistics,
}), }),
}, },
}, },
...@@ -53,22 +56,28 @@ export default { ...@@ -53,22 +56,28 @@ export default {
</script> </script>
<template> <template>
<div> <div>
<div class="pipeline-quota container-fluid"> <div class="pipeline-quota container-fluid py-4 px-2 m-0">
<div class="row"> <div class="row py-0">
<div class="col-sm-6"> <div class="col-sm-12">
<strong>{{ s__('UsageQuota|Usage since') }}</strong> <strong>{{ s__('UsageQuota|Storage usage:') }}</strong>
<div> <span class="js-total-usage">
<span class="js-total-usage"> {{ namespace.totalUsage }}
{{ namespace.totalUsage }} <gl-link
<gl-link :href="helpPagePath"
:href="helpPagePath" target="_blank"
target="_blank" :aria-label="s__('UsageQuota|Usage quotas help link')"
:aria-label="__('Usage quotas help link')" >
> <icon name="question" :size="12" />
<icon name="question" :size="12" /> </gl-link>
</gl-link> </span>
</span> </div>
</div> </div>
<div class="row py-0">
<div class="col-sm-12">
<usage-graph
v-if="namespace.rootStorageStatistics"
:root-storage-statistics="namespace.rootStorageStatistics"
/>
</div> </div>
</div> </div>
</div> </div>
......
<script>
import { s__ } from '~/locale';
import { numberToHumanSize } from '~/lib/utils/number_utils';
export default {
props: {
rootStorageStatistics: {
required: true,
type: Object,
},
},
computed: {
storageTypes() {
const {
buildArtifactsSize,
lfsObjectsSize,
packagesSize,
repositorySize,
storageSize,
wikiSize,
} = this.rootStorageStatistics;
if (storageSize === 0) {
return null;
}
return [
{
name: s__('UsageQuota|Repositories'),
percentage: this.sizePercentage(repositorySize),
class: 'gl-bg-data-viz-blue-500',
size: repositorySize,
},
{
name: s__('UsageQuota|LFS Objects'),
percentage: this.sizePercentage(lfsObjectsSize),
class: 'gl-bg-data-viz-orange-600',
size: lfsObjectsSize,
},
{
name: s__('UsageQuota|Packages'),
percentage: this.sizePercentage(packagesSize),
class: 'gl-bg-data-viz-aqua-500',
size: packagesSize,
},
{
name: s__('UsageQuota|Build Artifacts'),
percentage: this.sizePercentage(buildArtifactsSize),
class: 'gl-bg-data-viz-green-600',
size: buildArtifactsSize,
},
{
name: s__('UsageQuota|Wikis'),
percentage: this.sizePercentage(wikiSize),
class: 'gl-bg-data-viz-magenta-500',
size: wikiSize,
},
]
.filter(data => data.size !== 0)
.sort((a, b) => b.size - a.size);
},
},
methods: {
formatSize(size) {
return numberToHumanSize(size);
},
sizePercentage(size) {
const { storageSize } = this.rootStorageStatistics;
return (size / storageSize) * 100;
},
},
};
</script>
<template>
<div v-if="storageTypes" class="gl-display-flex gl-flex-direction-column w-100">
<div class="gl-h-6 my-3">
<div
v-for="storageType in storageTypes"
:key="storageType.name"
class="storage-type-usage gl-h-full gl-display-inline-block"
:class="storageType.class"
:style="{ width: `${storageType.percentage}%` }"
></div>
</div>
<div class="row py-0">
<div
v-for="storageType in storageTypes"
:key="storageType.name"
class="col-md-auto gl-display-flex gl-align-items-center"
data-testid="storage-type"
>
<div class="gl-h-2 gl-w-5 gl-mr-2 gl-display-inline-block" :class="storageType.class"></div>
<span class="gl-mr-2 gl-font-weight-bold gl-font-sm">
{{ storageType.name }}
</span>
<span class="gl-text-gray-700 gl-font-sm">
{{ formatSize(storageType.size) }}
</span>
</div>
</div>
</div>
</template>
...@@ -3,6 +3,11 @@ query getStorageCounter($fullPath: ID!) { ...@@ -3,6 +3,11 @@ query getStorageCounter($fullPath: ID!) {
id id
rootStorageStatistics { rootStorageStatistics {
storageSize storageSize
repositorySize
lfsObjectsSize
buildArtifactsSize
packagesSize
wikiSize
} }
projects(includeSubgroups: true) { projects(includeSubgroups: true) {
edges { edges {
......
---
title: Show usage graph for each storage type
merge_request: 31649
author:
type: added
import { shallowMount } from '@vue/test-utils';
import UsageGraph from 'ee/storage_counter/components/usage_graph.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
const data = {
wikiSize: 5000,
repositorySize: 4000,
packagesSize: 3000,
lfsObjectsSize: 2000,
buildArtifactsSize: 1000,
storageSize: 15000,
};
let wrapper;
function mountComponent(rootStorageStatistics) {
wrapper = shallowMount(UsageGraph, {
propsData: {
rootStorageStatistics,
},
});
}
describe('Storage Counter usage graph component', () => {
beforeEach(() => {
mountComponent(data);
});
afterEach(() => {
wrapper.destroy();
});
it('renders the legend in order', () => {
const types = wrapper.findAll('[data-testid="storage-type"]');
expect(types.at(0).text()).toContain('Wikis');
expect(types.at(1).text()).toContain('Repositories');
expect(types.at(2).text()).toContain('Packages');
expect(types.at(3).text()).toContain('LFS Objects');
expect(types.at(4).text()).toContain('Build Artifacts');
});
it('renders formatted data in the legend', () => {
expect(wrapper.text()).toContain(numberToHumanSize(data.buildArtifactsSize));
expect(wrapper.text()).toContain(numberToHumanSize(data.lfsObjectsSize));
expect(wrapper.text()).toContain(numberToHumanSize(data.packagesSize));
expect(wrapper.text()).toContain(numberToHumanSize(data.repositorySize));
expect(wrapper.text()).toContain(numberToHumanSize(data.wikiSize));
});
describe('when storage type is not used', () => {
beforeEach(() => {
data.wikiSize = 0;
mountComponent(data);
});
it('filters the storage type', () => {
expect(wrapper.text()).not.toContain('Wikis');
});
});
describe('when there is no storage usage', () => {
beforeEach(() => {
mountComponent({ storageSize: 0 });
});
it('it does not render', () => {
expect(wrapper.html()).toEqual('');
});
});
});
...@@ -23566,9 +23566,6 @@ msgstr "" ...@@ -23566,9 +23566,6 @@ 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 ""
...@@ -23578,12 +23575,18 @@ msgstr "" ...@@ -23578,12 +23575,18 @@ msgstr ""
msgid "UsageQuota|Artifacts" msgid "UsageQuota|Artifacts"
msgstr "" msgstr ""
msgid "UsageQuota|Build Artifacts"
msgstr ""
msgid "UsageQuota|Buy additional minutes" msgid "UsageQuota|Buy additional minutes"
msgstr "" msgstr ""
msgid "UsageQuota|Current period usage" msgid "UsageQuota|Current period usage"
msgstr "" msgstr ""
msgid "UsageQuota|LFS Objects"
msgstr ""
msgid "UsageQuota|LFS Storage" msgid "UsageQuota|LFS Storage"
msgstr "" msgstr ""
...@@ -23593,12 +23596,18 @@ msgstr "" ...@@ -23593,12 +23596,18 @@ msgstr ""
msgid "UsageQuota|Pipelines" msgid "UsageQuota|Pipelines"
msgstr "" msgstr ""
msgid "UsageQuota|Repositories"
msgstr ""
msgid "UsageQuota|Repository" msgid "UsageQuota|Repository"
msgstr "" msgstr ""
msgid "UsageQuota|Storage" msgid "UsageQuota|Storage"
msgstr "" msgstr ""
msgid "UsageQuota|Storage usage:"
msgstr ""
msgid "UsageQuota|This namespace has no projects which use shared runners" msgid "UsageQuota|This namespace has no projects which use shared runners"
msgstr "" msgstr ""
...@@ -23617,12 +23626,18 @@ msgstr "" ...@@ -23617,12 +23626,18 @@ msgstr ""
msgid "UsageQuota|Usage of resources across your projects" msgid "UsageQuota|Usage of resources across your projects"
msgstr "" msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
msgid "UsageQuota|Usage since" msgid "UsageQuota|Usage since"
msgstr "" msgstr ""
msgid "UsageQuota|Wiki" msgid "UsageQuota|Wiki"
msgstr "" msgstr ""
msgid "UsageQuota|Wikis"
msgstr ""
msgid "Use %{code_start}::%{code_end} to create a %{link_start}scoped label set%{link_end} (eg. %{code_start}priority::1%{code_end})" msgid "Use %{code_start}::%{code_end} to create a %{link_start}scoped label set%{link_end} (eg. %{code_start}priority::1%{code_end})"
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