Commit 1eac2484 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '335435-fix-security-configuration-mr-buttons' into 'master'

Fix Security Configuration enable via MR buttons [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!66090
parents b047f9e5 2ea929f9
import { helpPagePath } from '~/helpers/help_page_helper';
import { __, s__ } from '~/locale';
import configureSastMutation from '~/security_configuration/graphql/configure_sast.mutation.graphql';
import {
REPORT_TYPE_SAST,
REPORT_TYPE_DAST,
......@@ -15,6 +14,9 @@ import {
REPORT_TYPE_LICENSE_COMPLIANCE,
} from '~/vue_shared/security_reports/constants';
import configureSastMutation from '../graphql/configure_sast.mutation.graphql';
import configureSecretDetectionMutation from '../graphql/configure_secret_detection.mutation.graphql';
/**
* Translations & helpPagePaths for Static Security Configuration Page
*/
......@@ -214,6 +216,10 @@ export const securityFeatures = [
helpPath: DEPENDENCY_SCANNING_HELP_PATH,
configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH,
type: REPORT_TYPE_DEPENDENCY_SCANNING,
// This field will eventually come from the backend, the progress is
// tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/331621
canEnableByMergeRequest: window.gon.features?.secDependencyScanningUiEnable,
},
{
name: CONTAINER_SCANNING_NAME,
......@@ -235,7 +241,16 @@ export const securityFeatures = [
helpPath: SECRET_DETECTION_HELP_PATH,
configurationHelpPath: SECRET_DETECTION_CONFIG_HELP_PATH,
type: REPORT_TYPE_SECRET_DETECTION,
// This field is currently hardcoded because Secret Detection is always
// available. It will eventually come from the Backend, the progress is
// tracked in https://gitlab.com/gitlab-org/gitlab/-/issues/333113
available: true,
// This field is currently hardcoded because SAST can always be enabled via MR
// It will eventually come from the Backend, the progress is tracked in
// https://gitlab.com/gitlab-org/gitlab/-/issues/331621
canEnableByMergeRequest: true,
},
{
name: API_FUZZING_NAME,
......@@ -273,4 +288,15 @@ export const featureToMutationMap = {
},
}),
},
[REPORT_TYPE_SECRET_DETECTION]: {
mutationId: 'configureSecretDetection',
getMutationPayload: (projectPath) => ({
mutation: configureSecretDetectionMutation,
variables: {
input: {
projectPath,
},
},
}),
},
};
......@@ -46,8 +46,7 @@ export default {
return button;
},
showManageViaMr() {
const { available, configured, canEnableByMergeRequest } = this.feature;
return canEnableByMergeRequest && available && !configured;
return ManageViaMr.canRender(this.feature);
},
cardClasses() {
return { 'gl-bg-gray-10': !this.available };
......
......@@ -5,6 +5,10 @@ import { redirectTo } from '~/lib/utils/url_utility';
import { sprintf, s__ } from '~/locale';
import apolloProvider from '../provider';
function mutationSettingsForFeatureType(type) {
return featureToMutationMap[type];
}
export default {
apolloProvider,
components: {
......@@ -33,17 +37,19 @@ export default {
};
},
computed: {
featureSettings() {
return featureToMutationMap[this.feature.type];
mutationSettings() {
return mutationSettingsForFeatureType(this.feature.type);
},
},
methods: {
async mutate() {
this.isLoading = true;
try {
const mutation = this.featureSettings;
const { data } = await this.$apollo.mutate(mutation.getMutationPayload(this.projectPath));
const { errors, successPath } = data[mutation.mutationId];
const { mutationSettings } = this;
const { data } = await this.$apollo.mutate(
mutationSettings.getMutationPayload(this.projectPath),
);
const { errors, successPath } = data[mutationSettings.mutationId];
if (errors.length > 0) {
throw new Error(errors[0]);
......@@ -62,6 +68,22 @@ export default {
}
},
},
/**
* Returns a boolean representing whether this component can be rendered for
* the given feature. Useful for parent components to determine whether or
* not to render this component.
* @param {Object} feature The feature to check.
* @returns {boolean}
*/
canRender(feature) {
const { available, configured, canEnableByMergeRequest, type } = feature;
return (
canEnableByMergeRequest &&
available &&
!configured &&
Boolean(mutationSettingsForFeatureType(type))
);
},
i18n: {
buttonLabel: s__('SecurityConfiguration|Configure via Merge Request'),
noSuccessPathError: s__(
......
import { s__ } from '~/locale';
import { featureToMutationMap as featureToMutationMapCE } from '~/security_configuration/components/constants';
import {
REPORT_TYPE_DEPENDENCY_SCANNING,
REPORT_TYPE_SECRET_DETECTION,
} from '~/vue_shared/security_reports/constants';
import { REPORT_TYPE_DEPENDENCY_SCANNING } from '~/vue_shared/security_reports/constants';
import configureDependencyScanningMutation from '../graphql/configure_dependency_scanning.mutation.graphql';
import configureSecretDetectionMutation from '../graphql/configure_secret_detection.mutation.graphql';
export const SMALL = 'SMALL';
export const MEDIUM = 'MEDIUM';
......@@ -36,17 +32,6 @@ export const featureToMutationMap = {
},
}),
},
[REPORT_TYPE_SECRET_DETECTION]: {
mutationId: 'configureSecretDetection',
getMutationPayload: (projectPath) => ({
mutation: configureSecretDetectionMutation,
variables: {
input: {
projectPath,
},
},
}),
},
};
export const CONFIGURATION_SNIPPET_MODAL_ID = 'CONFIGURATION_SNIPPET_MODAL_ID';
......@@ -3,6 +3,7 @@ import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import FeatureCard from '~/security_configuration/components/feature_card.vue';
import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
import { REPORT_TYPE_SAST } from '~/vue_shared/security_reports/constants';
import { makeFeature } from './utils';
describe('FeatureCard component', () => {
......@@ -126,21 +127,23 @@ describe('FeatureCard component', () => {
describe('actions', () => {
describe.each`
context | available | configured | configurationPath | canEnableByMergeRequest | action
${'unavailable'} | ${false} | ${false} | ${null} | ${false} | ${null}
${'available'} | ${true} | ${false} | ${null} | ${false} | ${'guide'}
${'configured'} | ${true} | ${true} | ${null} | ${false} | ${'guide'}
${'available, can enable by MR'} | ${true} | ${false} | ${null} | ${true} | ${'create-mr'}
${'configured, can enable by MR'} | ${true} | ${true} | ${null} | ${true} | ${'guide'}
${'available with config path'} | ${true} | ${false} | ${'foo'} | ${false} | ${'enable'}
${'available with config path, can enable by MR'} | ${true} | ${false} | ${'foo'} | ${true} | ${'enable'}
${'configured with config path'} | ${true} | ${true} | ${'foo'} | ${false} | ${'configure'}
${'configured with config path, can enable by MR'} | ${true} | ${true} | ${'foo'} | ${true} | ${'configure'}
context | type | available | configured | configurationPath | canEnableByMergeRequest | action
${'unavailable'} | ${REPORT_TYPE_SAST} | ${false} | ${false} | ${null} | ${false} | ${null}
${'available'} | ${REPORT_TYPE_SAST} | ${true} | ${false} | ${null} | ${false} | ${'guide'}
${'configured'} | ${REPORT_TYPE_SAST} | ${true} | ${true} | ${null} | ${false} | ${'guide'}
${'available, can enable by MR'} | ${REPORT_TYPE_SAST} | ${true} | ${false} | ${null} | ${true} | ${'create-mr'}
${'available, can enable by MR, unknown type'} | ${'foo'} | ${true} | ${false} | ${null} | ${true} | ${'guide'}
${'configured, can enable by MR'} | ${REPORT_TYPE_SAST} | ${true} | ${true} | ${null} | ${true} | ${'guide'}
${'available with config path'} | ${REPORT_TYPE_SAST} | ${true} | ${false} | ${'foo'} | ${false} | ${'enable'}
${'available with config path, can enable by MR'} | ${REPORT_TYPE_SAST} | ${true} | ${false} | ${'foo'} | ${true} | ${'enable'}
${'configured with config path'} | ${REPORT_TYPE_SAST} | ${true} | ${true} | ${'foo'} | ${false} | ${'configure'}
${'configured with config path, can enable by MR'} | ${REPORT_TYPE_SAST} | ${true} | ${true} | ${'foo'} | ${true} | ${'configure'}
`(
'given $context feature',
({ available, configured, configurationPath, canEnableByMergeRequest, action }) => {
({ type, available, configured, configurationPath, canEnableByMergeRequest, action }) => {
beforeEach(() => {
feature = makeFeature({
type,
available,
configured,
configurationPath,
......
......@@ -9,6 +9,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { humanize } from '~/lib/utils/text_utility';
import { redirectTo } from '~/lib/utils/url_utility';
import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
import { REPORT_TYPE_SAST } from '~/vue_shared/security_reports/constants';
import { buildConfigureSecurityFeatureMockFactory } from './apollo_mocks';
jest.mock('~/lib/utils/url_utility');
......@@ -169,6 +170,29 @@ describe('ManageViaMr component', () => {
},
);
describe('canRender static method', () => {
it.each`
context | type | available | configured | canEnableByMergeRequest | expectedValue
${'an unconfigured feature'} | ${REPORT_TYPE_SAST} | ${true} | ${false} | ${true} | ${true}
${'a configured feature'} | ${REPORT_TYPE_SAST} | ${true} | ${true} | ${true} | ${false}
${'an unavailable feature'} | ${REPORT_TYPE_SAST} | ${false} | ${false} | ${true} | ${false}
${'a feature which cannot be enabled via MR'} | ${REPORT_TYPE_SAST} | ${true} | ${false} | ${false} | ${false}
${'an unknown feature'} | ${'foo'} | ${true} | ${false} | ${true} | ${false}
`(
'given $context returns $expectedValue',
({ type, available, configured, canEnableByMergeRequest, expectedValue }) => {
expect(
ManageViaMr.canRender({
type,
available,
configured,
canEnableByMergeRequest,
}),
).toBe(expectedValue);
},
);
});
describe('button props', () => {
it('passes the variant and category props to the GlButton', () => {
const variant = 'danger';
......
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