Commit ac325495 authored by Jiaan Louw's avatar Jiaan Louw Committed by Jose Ivan Vargas

Update compliance report local resolver to match new format

Applies the new GraphQL format to the local resolver and updates the
compliance report components to the new format.
parent 6eb89613
......@@ -115,21 +115,21 @@ export default {
this.updateUrlQuery({ ...this.urlQuery, sort: this.sortParam });
},
toggleDrawer(rows) {
const { mergeRequest, project } = rows[0] || {};
const { mergeRequest } = rows[0] || {};
if (!mergeRequest || this.isCurrentDrawer(mergeRequest)) {
this.closeDrawer();
} else {
this.openDrawer(mergeRequest, project);
this.openDrawer(mergeRequest);
}
},
isCurrentDrawer(mergeRequest) {
return this.showDrawer && mergeRequest.id === this.drawerMergeRequest.id;
},
openDrawer(mergeRequest, project) {
openDrawer(mergeRequest) {
this.showDrawer = true;
this.drawerMergeRequest = mergeRequest;
this.drawerProject = project;
this.drawerProject = mergeRequest.project;
},
closeDrawer() {
this.showDrawer = false;
......@@ -161,7 +161,7 @@ export default {
},
fields: [
{
key: 'severity',
key: 'severityLevel',
label: __('Severity'),
thClass: thWidthClass(10),
sortable: true,
......@@ -259,8 +259,8 @@ export default {
@row-selected="toggleDrawer"
@sort-changed="handleSortChanged"
>
<template #cell(severity)="{ item: { severity } }">
<severity-badge class="gl-reset-text-align!" :severity="severity" />
<template #cell(severityLevel)="{ item: { severityLevel } }">
<severity-badge class="gl-reset-text-align!" :severity="severityLevel" />
</template>
<template #cell(violationReason)="{ item: { reason, violatingUser } }">
<violation-reason :reason="reason" :user="violatingUser" />
......
......@@ -38,11 +38,11 @@ export default {
},
computed: {
defaultStartDate() {
const startDate = this.defaultQuery.createdAfter;
const startDate = this.defaultQuery.mergedAfter;
return startDate ? parsePikadayDate(startDate) : getDateInPast(CURRENT_DATE, 30);
},
defaultEndDate() {
const endDate = this.defaultQuery.createdBefore;
const endDate = this.defaultQuery.mergedBefore;
return endDate ? parsePikadayDate(endDate) : CURRENT_DATE;
},
},
......@@ -74,8 +74,8 @@ export default {
},
dateRangeChanged({ startDate = this.defaultStartDate, endDate = this.defaultEndDate }) {
this.updateFilter({
createdAfter: pikadayToString(startDate),
createdBefore: pikadayToString(endDate),
mergedAfter: pikadayToString(startDate),
mergedBefore: pikadayToString(endDate),
});
},
updateFilter(query) {
......
<script>
import { MERGE_REQUEST_VIOLATION_REASONS, MERGE_REQUEST_VIOLATION_MESSAGES } from '../../constants';
import { MERGE_REQUEST_VIOLATION_MESSAGES } from '../../constants';
import UserAvatar from '../shared/user_avatar.vue';
export default {
......@@ -8,8 +8,9 @@ export default {
},
props: {
reason: {
type: Number,
type: String,
required: true,
validator: (reason) => Object.keys(MERGE_REQUEST_VIOLATION_MESSAGES).includes(reason),
},
user: {
type: Object,
......@@ -18,11 +19,8 @@ export default {
},
},
computed: {
violation() {
return MERGE_REQUEST_VIOLATION_REASONS[this.reason];
},
violationMessage() {
return MERGE_REQUEST_VIOLATION_MESSAGES[this.violation];
return MERGE_REQUEST_VIOLATION_MESSAGES[this.reason];
},
},
};
......
......@@ -16,27 +16,14 @@ export const GRAPHQL_PAGE_SIZE = 20;
export const COMPLIANCE_DRAWER_CONTAINER_CLASS = '.content-wrapper';
const VIOLATION_TYPE_APPROVED_BY_AUTHOR = 'approved_by_author';
const VIOLATION_TYPE_APPROVED_BY_COMMITTER = 'approved_by_committer';
const VIOLATION_TYPE_APPROVED_BY_INSUFFICIENT_USERS = 'approved_by_insufficient_users';
export const MERGE_REQUEST_VIOLATION_REASONS = {
0: VIOLATION_TYPE_APPROVED_BY_AUTHOR,
1: VIOLATION_TYPE_APPROVED_BY_COMMITTER,
2: VIOLATION_TYPE_APPROVED_BY_INSUFFICIENT_USERS,
};
const APPROVED_BY_COMMITTER = 'APPROVED_BY_COMMITTER';
const APPROVED_BY_INSUFFICIENT_USERS = 'APPROVED_BY_INSUFFICIENT_USERS';
const APPROVED_BY_MERGE_REQUEST_AUTHOR = 'APPROVED_BY_MERGE_REQUEST_AUTHOR';
export const MERGE_REQUEST_VIOLATION_MESSAGES = {
[VIOLATION_TYPE_APPROVED_BY_AUTHOR]: s__('ComplianceReport|Approved by author'),
[VIOLATION_TYPE_APPROVED_BY_COMMITTER]: s__('ComplianceReport|Approved by committer'),
[VIOLATION_TYPE_APPROVED_BY_INSUFFICIENT_USERS]: s__('ComplianceReport|Less than 2 approvers'),
};
export const MERGE_REQUEST_VIOLATION_SEVERITY_LEVELS = {
1: 'high',
2: 'medium',
3: 'low',
4: 'info',
[APPROVED_BY_COMMITTER]: s__('ComplianceReport|Approved by committer'),
[APPROVED_BY_INSUFFICIENT_USERS]: s__('ComplianceReport|Less than 2 approvers'),
[APPROVED_BY_MERGE_REQUEST_AUTHOR]: s__('ComplianceReport|Approved by author'),
};
export const DEFAULT_SORT = 'SEVERITY_DESC';
export const DEFAULT_SORT = 'SEVERITY_LEVEL_DESC';
......@@ -21,7 +21,7 @@ query getComplianceViolations(
mergeRequestViolations {
nodes {
id
severity
severityLevel
reason
violatingUser {
id
......@@ -42,7 +42,7 @@ query getComplianceViolations(
avatarUrl
webUrl
}
mergedBy {
mergeUser {
id
name
username
......@@ -76,24 +76,24 @@ query getComplianceViolations(
webUrl
}
}
reference
ref
fullRef: reference(full: true)
sourceBranch
sourceBranchExists
targetBranch
targetBranchExists
}
project {
id
avatarUrl
name
webUrl
complianceFrameworks {
nodes {
id
name
description
color
project {
id
avatarUrl
name
webUrl
complianceFrameworks {
nodes {
id
name
description
color
}
}
}
}
......
import { MERGE_REQUEST_VIOLATION_SEVERITY_LEVELS } from '../constants';
export const mapViolations = (nodes = []) => {
return nodes.map((node) => ({
...node,
......@@ -8,11 +6,13 @@ export const mapViolations = (nodes = []) => {
committers: node.mergeRequest.committers?.nodes || [],
approvedByUsers: node.mergeRequest.approvedBy?.nodes || [],
participants: node.mergeRequest.participants?.nodes || [],
// TODO: Once the legacy dashboard is removed (https://gitlab.com/gitlab-org/gitlab/-/issues/346266) we can update the drawer to use the new attributes and remove these 2 mappings
reference: node.mergeRequest.ref,
mergedBy: node.mergeRequest.mergeUser,
project: {
...node.mergeRequest.project,
complianceFramework: node.mergeRequest.project?.complianceFrameworks?.nodes[0] || null,
},
},
project: {
...node.project,
complianceFramework: node.project?.complianceFrameworks?.nodes[0] || null,
},
severity: MERGE_REQUEST_VIOLATION_SEVERITY_LEVELS[node.severity],
}));
};
......@@ -15,8 +15,8 @@ export default {
{
__typename: 'MergeRequestViolation',
id: 1,
severity: 1,
reason: 1,
severityLevel: 'HIGH',
reason: 'APPROVED_BY_COMMITTER',
violatingUser: {
__typename: 'Violator',
id: 50,
......@@ -42,7 +42,7 @@ export default {
'https://secure.gravatar.com/avatar/7ff9b8111da2e2109e7b66f37aa632cc?s=80&d=identicon',
webUrl: 'https://gdk.localhost:3443/user6',
},
mergedBy: {
mergeUser: {
__typename: 'MergedBy',
id: 50,
name: 'John Doe6',
......@@ -93,37 +93,37 @@ export default {
],
},
fullRef: 'gitlab-shell!1',
reference: '!1',
ref: '!1',
sourceBranch: 'ut-171ad4e263',
sourceBranchExists: false,
targetBranch: 'master',
targetBranchExists: true,
},
project: {
__typename: 'Project',
id: 1,
avatarUrl: null,
name: 'Gitlab Shell',
webUrl: 'https://gdk.localhost:3443/gitlab-org/gitlab-shell',
complianceFrameworks: {
__typename: 'ComplianceFrameworks',
nodes: [
{
__typename: 'ComplianceFrameworks',
id: 1,
name: 'GDPR',
description: 'General Data Protection Regulation',
color: '#009966',
},
],
project: {
__typename: 'Project',
id: 1,
avatarUrl: null,
name: 'Gitlab Shell',
webUrl: 'https://gdk.localhost:3443/gitlab-org/gitlab-shell',
complianceFrameworks: {
__typename: 'ComplianceFrameworks',
nodes: [
{
__typename: 'ComplianceFrameworks',
id: 1,
name: 'GDPR',
description: 'General Data Protection Regulation',
color: '#009966',
},
],
},
},
},
},
{
__typename: 'MergeRequestViolation',
id: 2,
severity: 2,
reason: 2,
severityLevel: 'HIGH',
reason: 'APPROVED_BY_INSUFFICIENT_USERS',
violatingUser: {
__typename: 'Violator',
id: 50,
......@@ -149,7 +149,7 @@ export default {
'https://secure.gravatar.com/avatar/7ff9b8111da2e2109e7b66f37aa632cc?s=80&d=identicon',
webUrl: 'https://gdk.localhost:3443/user6',
},
mergedBy: {
mergeUser: {
__typename: 'MergedBy',
id: 50,
name: 'John Doe6',
......@@ -191,29 +191,29 @@ export default {
],
},
fullRef: 'gitlab-test!2',
reference: '!2',
ref: '!2',
sourceBranch: 'ut-171ad4e264',
sourceBranchExists: false,
targetBranch: 'master',
targetBranchExists: true,
},
project: {
__typename: 'Project',
id: 2,
avatarUrl: null,
name: 'Gitlab Test',
webUrl: 'https://gdk.localhost:3443/gitlab-org/gitlab-test',
complianceFrameworks: {
__typename: 'ComplianceFrameworks',
nodes: [
{
__typename: 'ComplianceFrameworks',
id: 2,
name: 'SOX',
description: 'A framework',
color: '#00FF00',
},
],
project: {
__typename: 'Project',
id: 2,
avatarUrl: null,
name: 'Gitlab Test',
webUrl: 'https://gdk.localhost:3443/gitlab-org/gitlab-test',
complianceFrameworks: {
__typename: 'ComplianceFrameworks',
nodes: [
{
__typename: 'ComplianceFrameworks',
id: 2,
name: 'SOX',
description: 'A framework',
color: '#00FF00',
},
],
},
},
},
},
......
......@@ -28,7 +28,7 @@ describe('MergeRequestDrawer component', () => {
targetBranch: null,
targetBranchUri: null,
},
project: defaultData.project,
project: defaultData.mergeRequest.project,
};
const findTitle = () => wrapper.findByTestId('dashboard-drawer-title');
......
......@@ -30,12 +30,12 @@ describe('ComplianceReport component', () => {
const mergeCommitsCsvExportPath = '/csv';
const groupPath = 'group-path';
const createdAfter = '2021-11-16';
const createdBefore = '2021-12-15';
const mergedAfter = '2021-11-16';
const mergedBefore = '2021-12-15';
const defaultQuery = {
projectIds: ['20'],
createdAfter,
createdBefore,
mergedAfter,
mergedBefore,
sort: DEFAULT_SORT,
};
const mockGraphQlError = new Error('GraphQL networkError');
......@@ -237,9 +237,9 @@ describe('ComplianceReport component', () => {
});
it('renders the violation severity badge', () => {
const { severity } = mapViolations(mockResolver().mergeRequestViolations.nodes)[0];
const { severityLevel } = mapViolations(mockResolver().mergeRequestViolations.nodes)[0];
expect(findSeverityBadge().props()).toStrictEqual({ severity });
expect(findSeverityBadge().props()).toStrictEqual({ severity: severityLevel });
});
it('renders the violation reason', () => {
......@@ -289,7 +289,7 @@ describe('ComplianceReport component', () => {
stripTypenames(drawerData.mergeRequest),
);
expect(findMergeRequestDrawer().props('project')).toStrictEqual(
stripTypenames(drawerData.project),
stripTypenames(drawerData.mergeRequest.project),
);
});
......@@ -313,7 +313,7 @@ describe('ComplianceReport component', () => {
stripTypenames(drawerData.mergeRequest),
);
expect(findMergeRequestDrawer().props('project')).toStrictEqual(
stripTypenames(drawerData.project),
stripTypenames(drawerData.mergeRequest.project),
);
});
});
......@@ -335,7 +335,7 @@ describe('ComplianceReport component', () => {
});
describe('when the filters changed', () => {
const query = { createdAfter, createdBefore, projectIds: [1, 2, 3] };
const query = { mergedAfter, mergedBefore, projectIds: [1, 2, 3] };
beforeEach(() => {
return findViolationFilter().vm.$emit('filters-changed', query);
......
......@@ -21,8 +21,8 @@ describe('ViolationFilter component', () => {
const startDate = getDateInPast(CURRENT_DATE, 20);
const endDate = getDateInPast(CURRENT_DATE, 4);
const dateRangeQuery = {
createdAfter: pikadayToString(startDate),
createdBefore: pikadayToString(endDate),
mergedAfter: pikadayToString(startDate),
mergedBefore: pikadayToString(endDate),
};
const defaultProjects = createDefaultProjects(2);
const projectsResponse = createDefaultProjectsResponse(defaultProjects);
......@@ -119,7 +119,7 @@ describe('ViolationFilter component', () => {
});
describe('with a default query', () => {
const defaultQuery = { projectIds, createdAfter: '2022-01-01', createdBefore: '2022-01-31' };
const defaultQuery = { projectIds, mergedAfter: '2022-01-01', mergedBefore: '2022-01-31' };
beforeEach(() => {
createComponent({ defaultQuery });
......
......@@ -2,18 +2,14 @@ import { shallowMount } from '@vue/test-utils';
import ViolationReason from 'ee/compliance_dashboard/components/violations/reason.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import UserAvatar from 'ee/compliance_dashboard/components/shared/user_avatar.vue';
import {
MERGE_REQUEST_VIOLATION_MESSAGES,
MERGE_REQUEST_VIOLATION_REASONS,
} from 'ee/compliance_dashboard/constants';
import { MERGE_REQUEST_VIOLATION_MESSAGES } from 'ee/compliance_dashboard/constants';
import { createUser } from '../../mock_data';
describe('ViolationReason component', () => {
let wrapper;
const user = convertObjectPropsToCamelCase(createUser(1));
const reasons = Object.keys(MERGE_REQUEST_VIOLATION_MESSAGES);
const getViolationMessage = (reason) =>
MERGE_REQUEST_VIOLATION_MESSAGES[MERGE_REQUEST_VIOLATION_REASONS[reason]];
const findAvatar = () => wrapper.findComponent(UserAvatar);
const createComponent = (propsData = {}) => {
......@@ -25,30 +21,22 @@ describe('ViolationReason component', () => {
});
describe('violation message', () => {
it.each`
reason | message
${0} | ${getViolationMessage(0)}
${1} | ${getViolationMessage(1)}
${2} | ${getViolationMessage(2)}
`(
'renders the violation message "$message" for the reason code $reason',
({ reason, message }) => {
createComponent({ reason });
expect(wrapper.text()).toContain(message);
},
);
it.each(reasons)('renders the violation message for the reason %s', (reason) => {
createComponent({ reason });
expect(wrapper.text()).toContain(MERGE_REQUEST_VIOLATION_MESSAGES[reason]);
});
});
describe('violation user', () => {
it('does not render a user avatar by default', () => {
createComponent({ reason: 0 });
createComponent({ reason: reasons[0] });
expect(findAvatar().exists()).toBe(false);
});
it('renders a user avatar when the user prop is set', () => {
createComponent({ reason: 0, user });
createComponent({ reason: reasons[0], user });
expect(findAvatar().props('user')).toBe(user);
});
......
import { mapViolations } from 'ee/compliance_dashboard/graphql/mappers';
import resolvers from 'ee/compliance_dashboard/graphql/resolvers';
import { MERGE_REQUEST_VIOLATION_SEVERITY_LEVELS } from 'ee/compliance_dashboard/constants';
describe('mapViolations', () => {
const mockViolations = resolvers.Query.group().mergeRequestViolations.nodes;
const severityLevels = Object.keys(MERGE_REQUEST_VIOLATION_SEVERITY_LEVELS).map(Number);
it.each(severityLevels)(
'maps to the expected severity level when the violation severity number is %s',
(severity) => {
const { severity: severityLevel } = mapViolations([{ ...mockViolations[0], severity }])[0];
it('returns the expected result', () => {
const { mergeRequest } = mapViolations([{ ...mockViolations[0] }])[0];
expect(severityLevel).toBe(MERGE_REQUEST_VIOLATION_SEVERITY_LEVELS[severity]);
},
);
expect(mergeRequest).toMatchObject({
reference: mergeRequest.ref,
mergedBy: mergeRequest.mergeUser,
});
});
});
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