Commit a68e88e7 authored by Paul Slaughter's avatar Paul Slaughter

Merge branch '287845-create-add-compliance-framework-page' into 'master'

Create add compliance framework page

See merge request gitlab-org/gitlab!54224
parents ff21bade 7a4d00dc
<script>
import { GlAlert, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import { GlAlert, GlButton, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
......@@ -16,12 +16,17 @@ export default {
DeleteModal,
EmptyState,
GlAlert,
GlButton,
GlLoadingIcon,
ListItem,
GlTab,
GlTabs,
},
props: {
addFrameworkPath: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
......@@ -121,6 +126,7 @@ export default {
),
allTab: s__('ComplianceFrameworks|All'),
regulatedTab: s__('ComplianceFrameworks|Regulated'),
addBtn: s__('ComplianceFrameworks|Add framework'),
},
};
</script>
......@@ -135,7 +141,11 @@ export default {
{{ alertMessage }}
</gl-alert>
<gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-5" />
<empty-state v-if="isEmpty" :image-path="emptyStateSvgPath" />
<empty-state
v-if="isEmpty"
:image-path="emptyStateSvgPath"
:add-framework-path="addFrameworkPath"
/>
<gl-tabs v-if="hasFrameworks">
<gl-tab class="gl-mt-6" :title="$options.i18n.allTab">
......@@ -148,6 +158,16 @@ export default {
/>
</gl-tab>
<gl-tab disabled :title="$options.i18n.regulatedTab" />
<template #tabs-end>
<gl-button
class="gl-align-self-center gl-ml-auto"
category="primary"
variant="confirm"
:href="addFrameworkPath"
>
{{ $options.i18n.addBtn }}
</gl-button>
</template>
</gl-tabs>
<delete-modal
v-if="hasFrameworks"
......
......@@ -11,6 +11,10 @@ export default {
type: String,
required: true,
},
addFrameworkPath: {
type: String,
required: true,
},
},
i18n: {
heading: s__('ComplianceFrameworks|There are no compliance frameworks set up yet'),
......@@ -27,7 +31,7 @@ export default {
:title="$options.i18n.heading"
:description="$options.i18n.description"
:svg-path="imagePath"
primary-button-link="#"
:primary-button-link="addFrameworkPath"
:primary-button-text="$options.i18n.addButton"
compact
:svg-height="110"
......
......@@ -15,7 +15,7 @@ const createComplianceFrameworksListApp = (el) => {
return false;
}
const { emptyStateSvgPath, groupPath } = el.dataset;
const { addFrameworkPath, emptyStateSvgPath, groupPath } = el.dataset;
return new Vue({
el,
......@@ -23,6 +23,7 @@ const createComplianceFrameworksListApp = (el) => {
render(createElement) {
return createElement(Form, {
props: {
addFrameworkPath,
emptyStateSvgPath,
groupPath,
},
......
import { createComplianceFrameworksFormApp } from 'ee/groups/settings/compliance_frameworks/init_form';
createComplianceFrameworksFormApp(document.getElementById('js-compliance-frameworks-form'));
# frozen_string_literal: true
class Groups::ComplianceFrameworksController < Groups::ApplicationController
extend ActiveSupport::Concern
before_action :check_group_compliance_frameworks_available!
before_action :authorize_admin_group!
feature_category :compliance_management
def new
end
protected
def check_group_compliance_frameworks_available!
render_404 unless can?(current_user, :admin_compliance_framework, group)
end
end
......@@ -4,15 +4,31 @@ module ComplianceManagement
module ComplianceFramework
module GroupSettingsHelper
def show_compliance_frameworks?
current_user.can?(:admin_compliance_framework, @group)
can?(current_user, :admin_compliance_framework, @group)
end
def compliance_frameworks_list_data
{
empty_state_svg_path: image_path('illustrations/welcome/ee_trial.svg'),
group_path: @group.full_path
group_path: @group.full_path,
add_framework_path: new_group_compliance_framework_path(@group)
}
end
def compliance_frameworks_new_form_data
{
group_path: @group.full_path,
group_edit_path: edit_group_path(@group, anchor: 'js-compliance-frameworks-settings'),
graphql_field_name: ComplianceManagement::Framework.name,
pipeline_configuration_full_path_enabled: pipeline_configuration_full_path_enabled?.to_s
}
end
private
def pipeline_configuration_full_path_enabled?
can?(current_user, :admin_compliance_pipeline_configuration, @group)
end
end
end
end
......@@ -128,6 +128,11 @@ module EE
::Feature.enabled?(:ff_custom_compliance_frameworks, @subject)
end
condition(:group_level_compliance_pipeline_available) do
@subject.feature_available?(:evaluate_group_level_compliance_pipeline) &&
::Feature.enabled?(:ff_custom_compliance_frameworks, @subject, default_enabled: :yaml)
end
rule { public_group | logged_in_viewable }.policy do
enable :read_wiki
enable :download_wiki_code
......@@ -349,6 +354,7 @@ module EE
end
rule { can?(:owner_access) & compliance_framework_available }.enable :admin_compliance_framework
rule { can?(:owner_access) & group_level_compliance_pipeline_available }.enable :admin_compliance_pipeline_configuration
end
override :lookup_access_level!
......
- add_to_breadcrumbs _('General Settings'), edit_group_path(@group)
- title = s_('ComplianceFramework|New Compliance Framework')
- page_title title
%h3.page-title= title
#js-compliance-frameworks-form{ data: compliance_frameworks_new_form_data }
......@@ -11,6 +11,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
patch :override, on: :member
end
resources :compliance_frameworks, only: [:new]
get '/analytics', to: redirect('groups/%{group_id}/-/analytics/value_stream_analytics')
resource :contribution_analytics, only: [:show]
......
......@@ -11,6 +11,7 @@ describe('ListEmptyState', () => {
wrapper = shallowMount(ListEmptyState, {
propsData: {
imagePath: 'dir/image.svg',
addFrameworkPath: 'group/framework/new',
...props,
},
});
......@@ -27,7 +28,7 @@ describe('ListEmptyState', () => {
title: 'There are no compliance frameworks set up yet',
description: 'Once you have created a compliance framework it will appear here.',
svgPath: 'dir/image.svg',
primaryButtonLink: '#',
primaryButtonLink: 'group/framework/new',
primaryButtonText: 'Add framework',
svgHeight: 110,
compact: true,
......
import { GlAlert, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import { GlAlert, GlButton, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
......@@ -31,6 +31,7 @@ describe('List', () => {
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findEmptyState = () => wrapper.find(EmptyState);
const findTabs = () => wrapper.findAll(GlTab);
const findAddBtn = () => wrapper.find(GlButton);
const findTabsContainer = () => wrapper.find(GlTabs);
const findListItems = () => wrapper.findAll(ListItem);
......@@ -47,6 +48,7 @@ describe('List', () => {
localVue,
apolloProvider: createMockApolloProvider(resolverMock),
propsData: {
addFrameworkPath: 'group/framework/new',
emptyStateSvgPath: 'dir/image.svg',
groupPath: 'group-1',
},
......@@ -118,6 +120,7 @@ describe('List', () => {
it('shows the empty state', () => {
expect(findEmptyState().exists()).toBe(true);
expect(findEmptyState().props('imagePath')).toBe('dir/image.svg');
expect(findEmptyState().props('addFrameworkPath')).toBe('group/framework/new');
});
it('does not show the other parts of the app', () => {
......@@ -155,6 +158,13 @@ describe('List', () => {
expect(tab.attributes('disabled')).toBe('true');
});
it('shows the add framework button', () => {
const addBtn = findAddBtn();
expect(addBtn.attributes('href')).toBe('group/framework/new');
expect(addBtn.text()).toBe('Add framework');
});
it('shows the list items with expect props', () => {
expect(findListItems()).toHaveLength(2);
......
......@@ -12,24 +12,22 @@ RSpec.describe ComplianceManagement::ComplianceFramework::GroupSettingsHelper do
end
describe '#show_compliance_frameworks?' do
using RSpec::Parameterized::TableSyntax
subject { helper.show_compliance_frameworks? }
where(:feature_flag_enabled, :license_feature_enabled, :result) do
true | true | true
false | true | false
true | false | false
false | false | false
context 'the user has permission' do
before do
allow(helper).to receive(:can?).with(current_user, :admin_compliance_framework, group).and_return(true)
end
with_them do
before do
stub_feature_flags(ff_custom_compliance_frameworks: feature_flag_enabled)
stub_licensed_features(custom_compliance_frameworks: license_feature_enabled)
it { is_expected.to be true }
end
it 'returns the correct value' do
expect(helper.show_compliance_frameworks?).to eql(result)
context 'the user does not have permission' do
before do
allow(helper).to receive(:can?).with(current_user, :admin_compliance_framework, group).and_return(false)
end
it { is_expected.to be false }
end
end
......@@ -37,8 +35,36 @@ RSpec.describe ComplianceManagement::ComplianceFramework::GroupSettingsHelper do
it 'returns the correct data' do
expect(helper.compliance_frameworks_list_data).to contain_exactly(
[:empty_state_svg_path, ActionController::Base.helpers.image_path('illustrations/welcome/ee_trial.svg')],
[:group_path, group.full_path]
[:group_path, group.full_path],
[:add_framework_path, new_group_compliance_framework_path(group)]
)
end
end
describe '#compliance_frameworks_new_form_data' do
subject { helper.compliance_frameworks_new_form_data }
shared_examples 'returns the correct data' do |pipeline_configuration_enabled|
before do
allow(helper).to receive(:can?).with(current_user, :admin_compliance_pipeline_configuration, group).and_return(pipeline_configuration_enabled)
end
it {
is_expected.to contain_exactly(
[:group_path, group.full_path],
[:group_edit_path, edit_group_path(group, anchor: 'js-compliance-frameworks-settings')],
[:graphql_field_name, ComplianceManagement::Framework.name],
[:pipeline_configuration_full_path_enabled, pipeline_configuration_enabled.to_s]
)
}
end
context 'the user has pipeline configuration permission' do
it_behaves_like 'returns the correct data', [true]
end
context 'the user does not have pipeline configuration permission' do
it_behaves_like 'returns the correct data', [false]
end
end
end
......@@ -1447,12 +1447,12 @@ RSpec.describe GroupPolicy do
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
end
describe ':admin_compliance_framework' do
describe 'compliance framework permissions' do
shared_context 'compliance framework permissions' do
using RSpec::Parameterized::TableSyntax
let(:policy) { :admin_compliance_framework }
where(:role, :licensed, :feature_flag, :allowed) do
:owner | true | true | true
:owner | true | false | false
......@@ -1469,12 +1469,26 @@ RSpec.describe GroupPolicy do
let(:current_user) { public_send(role) }
before do
stub_licensed_features(custom_compliance_frameworks: licensed)
stub_licensed_features(licensed_feature => licensed)
stub_feature_flags(ff_custom_compliance_frameworks: feature_flag)
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
context ':admin_compliance_framework' do
let(:policy) { :admin_compliance_framework }
let(:licensed_feature) { :custom_compliance_frameworks }
include_context 'compliance framework permissions'
end
context ':admin_compliance_pipeline_configuration' do
let(:policy) { :admin_compliance_pipeline_configuration }
let(:licensed_feature) { :evaluate_group_level_compliance_pipeline }
include_context 'compliance framework permissions'
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'group compliance frameworks' do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
before do
login_as(user)
end
context 'when compliance frameworks feature is disabled' do
before do
stub_feature_flags(ff_custom_compliance_frameworks: false)
stub_licensed_features(custom_compliance_frameworks: false)
end
describe 'GET /groups/:group/-/compliance_frameworks/new' do
it 'returns 404 not found' do
get new_group_compliance_framework_path(group)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when compliance frameworks feature is enabled' do
before do
stub_feature_flags(ff_custom_compliance_frameworks: true)
stub_licensed_features(custom_compliance_frameworks: true)
end
describe 'GET /groups/:group/-/compliance_frameworks/new' do
it 'renders template' do
group.add_owner(user)
get new_group_compliance_framework_path(group)
expect(response).to render_template 'groups/compliance_frameworks/new'
end
context 'with unauthorized user' do
it 'returns 404 not found' do
get new_group_compliance_framework_path(group)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'groups/compliance_frameworks/new.html.haml' do
let_it_be(:group) { build(:group) }
let_it_be(:user) { build(:user) }
before do
assign(:group, group)
allow(view).to receive(:current_user).and_return(user)
allow(user).to receive(:can?).with(:admin_compliance_pipeline_configuration, group).and_return(true)
end
it 'shows the compliance frameworks form', :aggregate_failures do
render
expect(rendered).to have_content('New Compliance Framework')
expect(rendered).to have_css('#js-compliance-frameworks-form')
end
end
......@@ -7689,6 +7689,9 @@ msgstr ""
msgid "ComplianceFramework|HIPAA - Health Insurance Portability and Accountability Act"
msgstr ""
msgid "ComplianceFramework|New Compliance Framework"
msgstr ""
msgid "ComplianceFramework|PCI-DSS"
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