Commit 8a8828a8 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '343912-agent-vulnerability-list' into 'master'

Create agent-specific vulnerability list

See merge request gitlab-org/gitlab!76713
parents 55bbd7d1 cdf7104d
......@@ -51,16 +51,7 @@ export default {
TokenTable,
ActivityEvents,
},
props: {
agentName: {
required: true,
type: String,
},
projectPath: {
required: true,
type: String,
},
},
inject: ['agentName', 'projectPath'],
data() {
return {
cursor: {
......@@ -131,12 +122,12 @@ export default {
</p>
<gl-tabs sync-active-tab-with-query-params lazy>
<slot name="ee-security-tab"></slot>
<gl-tab :title="$options.i18n.activity" query-param-value="activity">
<activity-events :agent-name="agentName" :project-path="projectPath" />
</gl-tab>
<slot name="ee-security-tab"></slot>
<gl-tab query-param-value="tokens">
<template #title>
<span data-testid="cluster-agent-token-count">
......
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { vulnerabilityLocationTypes } from '~/graphql_shared/fragment_types/vulnerability_location_types';
Vue.use(VueApollo);
// We create a fragment matcher so that we can create a fragment from an interface
// Without this, Apollo throws a heuristic fragment matcher warning
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData: vulnerabilityLocationTypes,
});
const defaultClient = createDefaultClient(
{},
{
cacheConfig: {
fragmentMatcher,
},
},
);
export default new VueApollo({
defaultClient,
});
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import AgentShowPage from 'ee_else_ce/clusters/agents/components/show.vue';
Vue.use(VueApollo);
import apolloProvider from './graphql/provider';
export default () => {
const el = document.querySelector('#js-cluster-agent-details');
......@@ -12,20 +9,19 @@ export default () => {
return null;
}
const defaultClient = createDefaultClient();
const { agentName, projectPath, activityEmptyStateImage } = el.dataset;
const { activityEmptyStateImage, agentName, emptyStateSvgPath, projectPath } = el.dataset;
return new Vue({
el,
apolloProvider: new VueApollo({ defaultClient }),
provide: { agentName, projectPath, activityEmptyStateImage },
render(createElement) {
return createElement(AgentShowPage, {
props: {
apolloProvider,
provide: {
activityEmptyStateImage,
agentName,
emptyStateSvgPath,
projectPath,
},
});
render(createElement) {
return createElement(AgentShowPage);
},
});
};
export const vulnerabilityLocationTypes = {
__schema: {
types: [
{
kind: 'UNION',
name: 'VulnerabilityLocation',
possibleTypes: [
{ name: 'VulnerabilityLocationContainerScanning' },
{ name: 'VulnerabilityLocationDast' },
{ name: 'VulnerabilityLocationDependencyScanning' },
{ name: 'VulnerabilityLocationSast' },
{ name: 'VulnerabilityLocationSecretDetection' },
],
},
],
},
};
......@@ -3,9 +3,10 @@
module Projects::ClusterAgentsHelper
def js_cluster_agent_details_data(agent_name, project)
{
activity_empty_state_image: image_path('illustrations/empty-state/empty-state-agents.svg'),
agent_name: agent_name,
project_path: project.full_path,
activity_empty_state_image: image_path('illustrations/empty-state/empty-state-agents.svg')
empty_state_svg_path: image_path('illustrations/operations-dashboard_empty.svg'),
project_path: project.full_path
}
end
end
......@@ -3,23 +3,18 @@ import { GlTab } from '@gitlab/ui';
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AgentShowPage from '~/clusters/agents/components/show.vue';
import AgentVulnerabilityReport from 'ee/security_dashboard/components/agent/agent_vulnerability_report.vue';
export default {
i18n: {
securityTabTitle: s__('ClusterAgents|Security'),
},
components: { AgentShowPage, GlTab },
mixins: [glFeatureFlagMixin()],
props: {
agentName: {
required: true,
type: String,
},
projectPath: {
required: true,
type: String,
},
components: {
AgentShowPage,
GlTab,
AgentVulnerabilityReport,
},
mixins: [glFeatureFlagMixin()],
computed: {
showSecurityTab() {
return (
......@@ -31,10 +26,11 @@ export default {
</script>
<template>
<agent-show-page v-bind="$props">
<agent-show-page>
<template v-if="showSecurityTab" #ee-security-tab>
<!-- Placeholder for https://gitlab.com/gitlab-org/gitlab/-/issues/343912-->
<gl-tab :title="$options.i18n.securityTabTitle"><div></div></gl-tab>
<gl-tab :title="$options.i18n.securityTabTitle">
<agent-vulnerability-report />
</gl-tab>
</template>
</agent-show-page>
</template>
<script>
import { helpPagePath } from '~/helpers/help_page_helper';
import VulnerabilityListGraphql from '../shared/vulnerability_report/vulnerability_list_graphql.vue';
import VulnerabilityFilters from '../shared/vulnerability_report/vulnerability_filters.vue';
import {
FIELD_PRESETS,
FILTER_PRESETS,
REPORT_TAB,
REPORT_TYPE_PRESETS,
} from '../shared/vulnerability_report/constants';
import projectVulnerabilitiesQuery from '../../graphql/queries/project_vulnerabilities.query.graphql';
import { DASHBOARD_TYPES } from '../../store/constants';
export default {
components: {
VulnerabilityFilters,
VulnerabilityListGraphql,
},
/**
* Normally we should only use provide when bootstrapping an application, but this is an exception
* because there is a CE/EE split in `app/assets/javascripts/clusters/agents/index.js` (where
* these values are only needed for EE) and there is necessary rename/variable naming that only
* makes sense in the context of being in the `app/assets/javascripts/security_dashboard`
* directory.
*/
provide() {
return {
dashboardDocumentation: helpPagePath('user/application_security/security_dashboard/index'),
dashboardType: DASHBOARD_TYPES.PROJECT,
canAdminVulnerability: true,
fullPath: this.projectPath,
canViewFalsePositive: false,
hasJiraVulnerabilitiesIntegrationEnabled: false,
};
},
inject: ['projectPath'],
data() {
return {
graphqlFilters: undefined,
};
},
computed: {
filtersToShow() {
return FILTER_PRESETS[REPORT_TAB.OPERATIONAL];
},
},
methods: {
updateGraphqlFilters(graphqlFilters) {
this.graphqlFilters = graphqlFilters;
this.graphqlFilters.reportType = REPORT_TYPE_PRESETS.OPERATIONAL;
},
},
fieldsToShow: FIELD_PRESETS[REPORT_TAB.OPERATIONAL],
REPORT_TAB,
projectVulnerabilitiesQuery,
};
</script>
<template>
<div>
<vulnerability-filters
:filters="filtersToShow"
class="security-dashboard-filters gl-mt-7"
@filters-changed="updateGraphqlFilters"
/>
<vulnerability-list-graphql
:query="$options.projectVulnerabilitiesQuery"
:fields="$options.fieldsToShow"
:filters="graphqlFilters"
/>
</div>
</template>
{"__schema":{"types":[{"kind":"UNION","name":"VulnerabilityLocation","possibleTypes":[{"name":"VulnerabilityLocationContainerScanning"},{"name":"VulnerabilityLocationDast"},{"name":"VulnerabilityLocationDependencyScanning"},{"name":"VulnerabilityLocationSast"},{"name":"VulnerabilityLocationSecretDetection"}]}]}}
......@@ -2,14 +2,14 @@ import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from './fragmentTypes.json';
import { vulnerabilityLocationTypes } from '~/graphql_shared/fragment_types/vulnerability_location_types';
Vue.use(VueApollo);
// We create a fragment matcher so that we can create a fragment from an interface
// Without this, Apollo throws a heuristic fragment matcher warning
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
introspectionQueryResultData: vulnerabilityLocationTypes,
});
const defaultClient = createDefaultClient(
......
......@@ -3,24 +3,19 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ClusterAgentShow from 'ee/clusters/agents/components/show.vue';
import CEClusterAgentShowPage from '~/clusters/agents/components/show.vue';
describe('ClusterAgentShow', () => {
let wrapper;
const agentName = 'best-agent';
const projectPath = 'path/to/project';
const createWrapper = ({ glFeatures = {} } = {}) => {
wrapper = extendedWrapper(
shallowMount(ClusterAgentShow, {
propsData: { agentName, projectPath },
provide: { glFeatures },
}),
);
};
const findTab = () => wrapper.findComponent(GlTab);
const findCEClusterAgentShowPage = () => wrapper.findComponent(CEClusterAgentShowPage);
afterEach(() => {
wrapper.destroy();
......@@ -36,7 +31,6 @@ describe('ClusterAgentShow', () => {
`('$title', async ({ glFeatures, tabStatus }) => {
createWrapper({ glFeatures });
await nextTick();
expect(findCEClusterAgentShowPage().props()).toStrictEqual({ agentName, projectPath });
expect(findTab().exists()).toBe(tabStatus);
});
});
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Agent vulnerability report component renders 1`] = `
<div>
<vulnerability-filters-stub
class="security-dashboard-filters gl-mt-7"
filters="[object Object],[object Object],[object Object]"
/>
<vulnerability-list-graphql-stub
fields="[object Object],[object Object],[object Object],[object Object],,[object Object]"
query="[object Object]"
/>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import AgentVulnerabilityReport from 'ee/security_dashboard/components/agent/agent_vulnerability_report.vue';
describe('Agent vulnerability report component', () => {
let wrapper;
const provide = { agentName: 'primary-agent', projectPath: '/path/to/project/' };
const createWrapper = () => {
wrapper = shallowMount(AgentVulnerabilityReport, { provide });
};
afterEach(() => {
wrapper.destroy();
});
it('renders', () => {
createWrapper();
expect(wrapper.element).toMatchSnapshot();
});
});
......@@ -19,7 +19,7 @@ describe('ClusterAgentShow', () => {
let wrapper;
useFakeDate([2021, 2, 15]);
const propsData = {
const provide = {
agentName: 'cluster-agent',
projectPath: 'path/to/project',
};
......@@ -49,7 +49,7 @@ describe('ClusterAgentShow', () => {
shallowMount(ClusterAgentShow, {
localVue,
apolloProvider,
propsData,
provide,
stubs: { GlSprintf, TimeAgoTooltip, GlTab },
}),
);
......@@ -60,7 +60,7 @@ describe('ClusterAgentShow', () => {
wrapper = extendedWrapper(
shallowMount(ClusterAgentShow, {
propsData,
provide,
mocks: { $apollo, clusterAgent },
slots,
stubs: { GlTab },
......@@ -85,7 +85,7 @@ describe('ClusterAgentShow', () => {
});
it('displays the agent name', () => {
expect(wrapper.text()).toContain(propsData.agentName);
expect(wrapper.text()).toContain(provide.agentName);
});
it('displays agent create information', () => {
......
......@@ -17,5 +17,10 @@ RSpec.describe Projects::ClusterAgentsHelper do
it 'returns project path' do
expect(subject[:project_path]).to eq(project.full_path)
end
it 'returns string contants' do
expect(subject[:activity_empty_state_image]).to be_kind_of(String)
expect(subject[:empty_state_svg_path]).to be_kind_of(String)
end
end
end
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