Commit 0157ce73 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas Committed by Paul Slaughter

Add CI minutes usage charts to group usage quotas

This makes the CI minutes usage quotas charts
available on the profile page, available to groups

Changelog: added
EE: true
parent 43298757
...@@ -52,7 +52,7 @@ export default { ...@@ -52,7 +52,7 @@ export default {
]); ]);
}, },
months() { months() {
return this.minutesUsageData.map((cur) => cur.month); return this.minutesUsageData.filter((cur) => cur.minutes > 0).map((cur) => cur.month);
}, },
isDataEmpty() { isDataEmpty() {
return this.minutesUsageData.length === 0 && !this.selectedMonth; return this.minutesUsageData.length === 0 && !this.selectedMonth;
...@@ -83,7 +83,7 @@ export default { ...@@ -83,7 +83,7 @@ export default {
</script> </script>
<template> <template>
<div> <div>
<div class="gl-display-flex gl-mt-3" :class="{ 'gl-mb-3': !isDataEmpty }"> <div class="gl-display-flex gl-mt-7" :class="{ 'gl-mb-3': !isDataEmpty }">
<h5 class="gl-flex-grow-1">{{ $options.USAGE_BY_PROJECT }}</h5> <h5 class="gl-flex-grow-1">{{ $options.USAGE_BY_PROJECT }}</h5>
<gl-dropdown v-if="!isDataEmpty" :text="selectedMonth" data-testid="project-month-dropdown"> <gl-dropdown v-if="!isDataEmpty" :text="selectedMonth" data-testid="project-month-dropdown">
......
import $ from 'jquery'; import $ from 'jquery';
import SeatUsageApp from 'ee/usage_quotas/seats'; import SeatUsageApp from 'ee/usage_quotas/seats';
import initNamespaceStorage from 'ee/usage_quotas/storage/init_namespace_storage'; import initNamespaceStorage from 'ee/usage_quotas/storage/init_namespace_storage';
import initCiMinutesUsageApp from 'ee/usage_quotas/ci_minutes_usage';
import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs'; import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
const initLinkedTabs = () => { const initLinkedTabs = () => {
...@@ -23,6 +24,8 @@ const initVueApps = () => { ...@@ -23,6 +24,8 @@ const initVueApps = () => {
if (document.querySelector('#js-storage-counter-app')) { if (document.querySelector('#js-storage-counter-app')) {
initNamespaceStorage(); initNamespaceStorage();
} }
initCiMinutesUsageApp();
}; };
/** /**
......
<script>
import { formatDate } from '~/lib/utils/datetime_utility';
import { TYPE_GROUP } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import MinutesUsageMonthChart from 'ee/ci_minutes_usage/components/minutes_usage_month_chart.vue';
import MinutesUsageProjectChart from 'ee/ci_minutes_usage/components/minutes_usage_project_chart.vue';
import getCiMinutesUsageGroup from '../graphql/queries/ci_minutes_namespace.query.graphql';
export default {
components: {
MinutesUsageMonthChart,
MinutesUsageProjectChart,
},
inject: ['namespaceId'],
data() {
return {
ciMinutesUsage: [],
};
},
apollo: {
ciMinutesUsage: {
query: getCiMinutesUsageGroup,
variables() {
return {
namespaceId: convertToGraphQLId(TYPE_GROUP, this.namespaceId),
};
},
update(res) {
return res?.ciMinutesUsage?.nodes;
},
},
},
computed: {
minutesUsageDataByMonth() {
return this.ciMinutesUsage
.slice()
.sort((a, b) => {
return new Date(a.monthIso8601) - new Date(b.monthIso8601);
})
.map((cur) => [formatDate(cur.monthIso8601, 'mmm yyyy'), cur.minutes]);
},
borderStyles() {
return 'gl-border-b-solid gl-border-gray-200 gl-border-b-1';
},
},
};
</script>
<template>
<div :class="borderStyles" class="gl-my-7">
<minutes-usage-month-chart
:class="borderStyles"
:minutes-usage-data="minutesUsageDataByMonth"
/>
<minutes-usage-project-chart :minutes-usage-data="ciMinutesUsage" />
</div>
</template>
query getCiMinutesUsageGroup($namespaceId: NamespaceID) {
ciMinutesUsage(namespaceId: $namespaceId) {
nodes {
month
monthIso8601
minutes
projects {
nodes {
name
minutes
}
}
}
}
}
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import CiMinutesUsageAppGroup from './components/app.vue';
const mountCiMinutesUsageAppGroup = (el) => {
const { namespaceId } = el.dataset;
Vue.use(VueApollo);
const defaultClient = createDefaultClient();
const apolloProvider = new VueApollo({
defaultClient,
});
return new Vue({
el,
apolloProvider,
name: 'CiMinutesUsageAppGroup',
components: {
CiMinutesUsageAppGroup,
},
provide: {
namespaceId,
},
render: (createElement) => createElement(CiMinutesUsageAppGroup),
});
};
export default () => {
const el = document.getElementById('js-ci-minutes-usage-group');
return !el ? {} : mountCiMinutesUsageAppGroup(el);
};
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
.tab-pane#seats-quota-tab .tab-pane#seats-quota-tab
#js-seat-usage-app{ data: { namespace_id: @group.id, namespace_name: @group.name, seat_usage_export_path: group_seat_usage_path(@group, format: :csv), pending_members_page_path: pending_members_page_path, pending_members_count: pending_members_count } } #js-seat-usage-app{ data: { namespace_id: @group.id, namespace_name: @group.name, seat_usage_export_path: group_seat_usage_path(@group, format: :csv), pending_members_page_path: pending_members_page_path, pending_members_count: pending_members_count } }
.tab-pane#pipelines-quota-tab .tab-pane#pipelines-quota-tab
#js-ci-minutes-usage-group{ data: { namespace_id: @group.id } }
= 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
......
...@@ -47,10 +47,8 @@ describe('Minutes usage by project chart component', () => { ...@@ -47,10 +47,8 @@ describe('Minutes usage by project chart component', () => {
expect(findAlert().exists()).toBe(false); expect(findAlert().exists()).toBe(false);
}); });
it('renders the same amount of dropdown components as the backend response', () => { it('renders only the months with available minutes data', () => {
expect(findAllDropdownItems().length).toBe( expect(findAllDropdownItems().length).toBe(1);
ciMinutesUsageMockData.data.ciMinutesUsage.nodes.length,
);
}); });
}); });
......
...@@ -15,6 +15,14 @@ export const ciMinutesUsageMockData = { ...@@ -15,6 +15,14 @@ export const ciMinutesUsageMockData = {
], ],
}, },
}, },
{
month: 'July',
monthIso8601: '2021-07-01',
minutes: 0,
projects: {
nodes: [],
},
},
], ],
}, },
}, },
......
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import CiMinutesUsageAppGroup from 'ee/usage_quotas/ci_minutes_usage/components/app.vue';
import MinutesUsageMonthChart from 'ee/ci_minutes_usage/components/minutes_usage_month_chart.vue';
import MinutesUsageProjectChart from 'ee/ci_minutes_usage/components/minutes_usage_project_chart.vue';
import ciMinutesUsageGroup from 'ee/usage_quotas/ci_minutes_usage/graphql/queries/ci_minutes_namespace.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { ciMinutesUsageMockData } from '../mock_data';
Vue.use(VueApollo);
describe('CI minutes usage app groups', () => {
let wrapper;
let queryHandlerSpy;
function createMockApolloProvider() {
const requestHandlers = [[ciMinutesUsageGroup, queryHandlerSpy]];
return createMockApollo(requestHandlers);
}
function createComponent() {
const apolloProvider = createMockApolloProvider();
wrapper = shallowMount(CiMinutesUsageAppGroup, {
apolloProvider,
provide: {
namespaceId: 1,
},
});
}
const findMinutesUsageMonthChart = () => wrapper.findComponent(MinutesUsageMonthChart);
const findMinutesUsageProjectChart = () => wrapper.findComponent(MinutesUsageProjectChart);
beforeEach(() => {
queryHandlerSpy = jest.fn().mockResolvedValue(ciMinutesUsageMockData);
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('calls query with namespaceId variable', () => {
expect(queryHandlerSpy).toHaveBeenCalledWith({ namespaceId: 'gid://gitlab/Group/1' });
});
describe('after query finishes', () => {
beforeEach(async () => {
await waitForPromises();
await nextTick();
});
it('should render minutes usage month chart', () => {
expect(findMinutesUsageMonthChart().props()).toEqual({
minutesUsageData: [
['Jun 2021', 5],
['Jul 2021', 0],
],
});
});
it('should render minutes usage project chart', () => {
expect(findMinutesUsageProjectChart().props()).toEqual({
minutesUsageData: ciMinutesUsageMockData.data.ciMinutesUsage.nodes,
});
});
});
});
export const ciMinutesUsageMockData = {
data: {
ciMinutesUsage: {
nodes: [
{
month: 'July',
monthIso8601: '2021-07-01',
minutes: 0,
projects: {
nodes: [],
},
},
{
month: 'June',
monthIso8601: '2021-06-01',
minutes: 5,
projects: {
nodes: [
{
name: 'devcafe-wp-theme',
minutes: 5,
},
],
},
},
],
},
},
};
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