Commit 6d2c4a85 authored by Paul Slaughter's avatar Paul Slaughter

Merge branch 'jivanvl-copy-ci-minutes-charts-group' into 'master'

Add CI minutes usage charts to group usage quotas

See merge request gitlab-org/gitlab!80321
parents d49156b9 0157ce73
...@@ -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