Commit b7b473f3 authored by Alexander Turinske's avatar Alexander Turinske

Create agent-specific vulnerability list

- use vulnerability-list component
- pass up additional data
- update tests
parent 1b49113d
...@@ -51,16 +51,7 @@ export default { ...@@ -51,16 +51,7 @@ export default {
TokenTable, TokenTable,
ActivityEvents, ActivityEvents,
}, },
props: { inject: ['agentName', 'projectPath'],
agentName: {
required: true,
type: String,
},
projectPath: {
required: true,
type: String,
},
},
data() { data() {
return { return {
cursor: { cursor: {
...@@ -131,12 +122,12 @@ export default { ...@@ -131,12 +122,12 @@ export default {
</p> </p>
<gl-tabs sync-active-tab-with-query-params lazy> <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"> <gl-tab :title="$options.i18n.activity" query-param-value="activity">
<activity-events :agent-name="agentName" :project-path="projectPath" /> <activity-events :agent-name="agentName" :project-path="projectPath" />
</gl-tab> </gl-tab>
<slot name="ee-security-tab"></slot>
<gl-tab query-param-value="tokens"> <gl-tab query-param-value="tokens">
<template #title> <template #title>
<span data-testid="cluster-agent-token-count"> <span data-testid="cluster-agent-token-count">
......
{"__schema":{"types":[{"kind":"UNION","name":"VulnerabilityLocation","possibleTypes":[{"name":"VulnerabilityLocationContainerScanning"},{"name":"VulnerabilityLocationDast"},{"name":"VulnerabilityLocationDependencyScanning"},{"name":"VulnerabilityLocationSast"},{"name":"VulnerabilityLocationSecretDetection"}]}]}}
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';
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,
});
const defaultClient = createDefaultClient(
{},
{
cacheConfig: {
fragmentMatcher,
},
},
);
export default new VueApollo({
defaultClient,
});
import Vue from 'vue'; 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'; import AgentShowPage from 'ee_else_ce/clusters/agents/components/show.vue';
import apolloProvider from './graphql/provider';
Vue.use(VueApollo);
export default () => { export default () => {
const el = document.querySelector('#js-cluster-agent-details'); const el = document.querySelector('#js-cluster-agent-details');
...@@ -12,20 +9,26 @@ export default () => { ...@@ -12,20 +9,26 @@ export default () => {
return null; return null;
} }
const defaultClient = createDefaultClient(); const {
const { agentName, projectPath, activityEmptyStateImage } = el.dataset; activityEmptyStateImage,
agentName,
dashboardDocumentation,
emptyStateSvgPath,
projectPath,
} = el.dataset;
return new Vue({ return new Vue({
el, el,
apolloProvider: new VueApollo({ defaultClient }), apolloProvider,
provide: { agentName, projectPath, activityEmptyStateImage }, provide: {
render(createElement) { activityEmptyStateImage,
return createElement(AgentShowPage, {
props: {
agentName, agentName,
dashboardDocumentation,
emptyStateSvgPath,
projectPath, projectPath,
}, },
}); render(createElement) {
return createElement(AgentShowPage);
}, },
}); });
}; };
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
module Projects::ClusterAgentsHelper module Projects::ClusterAgentsHelper
def js_cluster_agent_details_data(agent_name, project) 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, agent_name: agent_name,
project_path: project.full_path, dashboard_documentation: help_page_path('user/application_security/security_dashboard/index'),
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
end end
...@@ -3,23 +3,18 @@ import { GlTab } from '@gitlab/ui'; ...@@ -3,23 +3,18 @@ import { GlTab } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AgentShowPage from '~/clusters/agents/components/show.vue'; import AgentShowPage from '~/clusters/agents/components/show.vue';
import AgentVulnerabilityReport from 'ee/security_dashboard/components/agent/agent_vulnerability_report.vue';
export default { export default {
i18n: { i18n: {
securityTabTitle: s__('ClusterAgents|Security'), securityTabTitle: s__('ClusterAgents|Security'),
}, },
components: { AgentShowPage, GlTab }, components: {
mixins: [glFeatureFlagMixin()], AgentShowPage,
props: { GlTab,
agentName: { AgentVulnerabilityReport,
required: true,
type: String,
},
projectPath: {
required: true,
type: String,
},
}, },
mixins: [glFeatureFlagMixin()],
computed: { computed: {
showSecurityTab() { showSecurityTab() {
return ( return (
...@@ -33,8 +28,9 @@ export default { ...@@ -33,8 +28,9 @@ export default {
<template> <template>
<agent-show-page v-bind="$props"> <agent-show-page v-bind="$props">
<template v-if="showSecurityTab" #ee-security-tab> <template v-if="showSecurityTab" #ee-security-tab>
<!-- Placeholder for https://gitlab.com/gitlab-org/gitlab/-/issues/343912--> <gl-tab :title="$options.i18n.securityTabTitle">
<gl-tab :title="$options.i18n.securityTabTitle"><div></div></gl-tab> <agent-vulnerability-report />
</gl-tab>
</template> </template>
</agent-show-page> </agent-show-page>
</template> </template>
<script>
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,
},
provide() {
return {
dashboardType: DASHBOARD_TYPES.PROJECT,
canAdminVulnerability: true,
fullPath: this.projectPath,
canViewFalsePositive: false,
hasJiraVulnerabilitiesIntegrationEnabled: false,
};
},
inject: ['projectPath'],
data() {
return {
graphqlFilters: undefined,
};
},
computed: {
fieldsToShow() {
return FIELD_PRESETS[REPORT_TAB.OPERATIONAL];
},
filtersToShow() {
return FILTER_PRESETS[REPORT_TAB.OPERATIONAL];
},
},
methods: {
updateGraphqlFilters(graphqlFilters) {
this.graphqlFilters = graphqlFilters;
this.graphqlFilters.reportType = REPORT_TYPE_PRESETS.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="fieldsToShow"
:filters="graphqlFilters"
/>
</div>
</template>
...@@ -3,24 +3,19 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -3,24 +3,19 @@ import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ClusterAgentShow from 'ee/clusters/agents/components/show.vue'; import ClusterAgentShow from 'ee/clusters/agents/components/show.vue';
import CEClusterAgentShowPage from '~/clusters/agents/components/show.vue';
describe('ClusterAgentShow', () => { describe('ClusterAgentShow', () => {
let wrapper; let wrapper;
const agentName = 'best-agent';
const projectPath = 'path/to/project';
const createWrapper = ({ glFeatures = {} } = {}) => { const createWrapper = ({ glFeatures = {} } = {}) => {
wrapper = extendedWrapper( wrapper = extendedWrapper(
shallowMount(ClusterAgentShow, { shallowMount(ClusterAgentShow, {
propsData: { agentName, projectPath },
provide: { glFeatures }, provide: { glFeatures },
}), }),
); );
}; };
const findTab = () => wrapper.findComponent(GlTab); const findTab = () => wrapper.findComponent(GlTab);
const findCEClusterAgentShowPage = () => wrapper.findComponent(CEClusterAgentShowPage);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -36,7 +31,6 @@ describe('ClusterAgentShow', () => { ...@@ -36,7 +31,6 @@ describe('ClusterAgentShow', () => {
`('$title', async ({ glFeatures, tabStatus }) => { `('$title', async ({ glFeatures, tabStatus }) => {
createWrapper({ glFeatures }); createWrapper({ glFeatures });
await nextTick(); await nextTick();
expect(findCEClusterAgentShowPage().props()).toStrictEqual({ agentName, projectPath });
expect(findTab().exists()).toBe(tabStatus); expect(findTab().exists()).toBe(tabStatus);
}); });
}); });
......
import { shallowMount } from '@vue/test-utils';
import VulnerabilityListGraphql from 'ee/security_dashboard/components/shared/vulnerability_report/vulnerability_list_graphql.vue';
import VulnerabilityFilters from 'ee/security_dashboard/components/shared/vulnerability_report/vulnerability_filters.vue';
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 });
};
const findVulnerabilityFilters = () => wrapper.findComponent(VulnerabilityFilters);
const findVulnerabilityList = () => wrapper.findComponent(VulnerabilityListGraphql);
afterEach(() => {
wrapper.destroy();
});
it('renders', () => {
createWrapper();
expect(findVulnerabilityFilters().exists()).toBe(true);
expect(findVulnerabilityList().exists()).toBe(true);
});
});
...@@ -17,5 +17,11 @@ RSpec.describe Projects::ClusterAgentsHelper do ...@@ -17,5 +17,11 @@ RSpec.describe Projects::ClusterAgentsHelper do
it 'returns project path' do it 'returns project path' do
expect(subject[:project_path]).to eq(project.full_path) expect(subject[:project_path]).to eq(project.full_path)
end end
it 'returns string contants' do
expect(subject[:activity_empty_state_image]).to be_kind_of(String)
expect(subject[:dashboard_documentation]).to be_kind_of(String)
expect(subject[:empty_state_svg_path]).to be_kind_of(String)
end
end 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