Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
0d47287e
Commit
0d47287e
authored
May 19, 2020
by
Phil Hughes
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '214386-project-dropdown-filter' into 'master'
Add project filter See merge request gitlab-org/gitlab!31444
parents
2bb589b2
f41fd4b4
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
216 additions
and
68 deletions
+216
-68
ee/app/assets/javascripts/security_dashboard/components/first_class_group_security_dashboard.vue
...board/components/first_class_group_security_dashboard.vue
+6
-1
ee/app/assets/javascripts/security_dashboard/components/first_class_group_security_dashboard_vulnerabilities.vue
.../first_class_group_security_dashboard_vulnerabilities.vue
+1
-0
ee/app/assets/javascripts/security_dashboard/components/first_class_instance_security_dashboard.vue
...rd/components/first_class_instance_security_dashboard.vue
+11
-1
ee/app/assets/javascripts/security_dashboard/components/first_class_instance_security_dashboard_vulnerabilities.vue
...rst_class_instance_security_dashboard_vulnerabilities.vue
+1
-0
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
...ashboard/components/first_class_vulnerability_filters.vue
+18
-2
ee/app/assets/javascripts/security_dashboard/constants.js
ee/app/assets/javascripts/security_dashboard/constants.js
+0
-33
ee/app/assets/javascripts/security_dashboard/graphql/group_vulnerabilities.graphql
.../security_dashboard/graphql/group_vulnerabilities.graphql
+13
-5
ee/app/assets/javascripts/security_dashboard/graphql/instance_vulnerabilities.graphql
...curity_dashboard/graphql/instance_vulnerabilities.graphql
+13
-3
ee/app/assets/javascripts/security_dashboard/graphql/project.fragment.graphql
...ripts/security_dashboard/graphql/project.fragment.graphql
+4
-0
ee/app/assets/javascripts/security_dashboard/helpers.js
ee/app/assets/javascripts/security_dashboard/helpers.js
+49
-0
ee/changelogs/unreleased/214386-project-dropdown-filter.yml
ee/changelogs/unreleased/214386-project-dropdown-filter.yml
+5
-0
ee/spec/frontend/security_dashboard/components/first_class_group_security_dashboard_spec.js
...d/components/first_class_group_security_dashboard_spec.js
+9
-1
ee/spec/frontend/security_dashboard/components/first_class_instance_security_dashboard_spec.js
...omponents/first_class_instance_security_dashboard_spec.js
+9
-1
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
...oard/components/first_class_vulnerability_filters_spec.js
+77
-21
No files found.
ee/app/assets/javascripts/security_dashboard/components/first_class_group_security_dashboard.vue
View file @
0d47287e
...
...
@@ -32,12 +32,16 @@ export default {
data
()
{
return
{
filters
:
{},
projects
:
[],
};
},
methods
:
{
handleFilterChange
(
filters
)
{
this
.
filters
=
filters
;
},
handleProjectsFetch
(
projects
)
{
this
.
projects
=
projects
;
},
},
};
</
script
>
...
...
@@ -45,13 +49,14 @@ export default {
<
template
>
<security-dashboard-layout>
<template
#header
>
<filters
@
filterChange=
"handleFilterChange"
/>
<filters
:projects=
"projects"
@
filterChange=
"handleFilterChange"
/>
</
template
>
<group-security-vulnerabilities
:dashboard-documentation=
"dashboardDocumentation"
:empty-state-svg-path=
"emptyStateSvgPath"
:group-full-path=
"groupFullPath"
:filters=
"filters"
@
projectFetch=
"handleProjectsFetch"
/>
<
template
#aside
>
<vulnerability-severity
:endpoint=
"vulnerableProjectsEndpoint"
/>
...
...
ee/app/assets/javascripts/security_dashboard/components/first_class_group_security_dashboard_vulnerabilities.vue
View file @
0d47287e
...
...
@@ -51,6 +51,7 @@ export default {
},
update
:
({
group
})
=>
group
.
vulnerabilities
.
nodes
,
result
({
data
})
{
this
.
$emit
(
'
projectFetch
'
,
data
.
group
.
projects
.
nodes
);
this
.
pageInfo
=
data
.
group
.
vulnerabilities
.
pageInfo
;
},
error
()
{
...
...
ee/app/assets/javascripts/security_dashboard/components/first_class_instance_security_dashboard.vue
View file @
0d47287e
...
...
@@ -52,6 +52,7 @@ export default {
data
()
{
return
{
filters
:
{},
graphqlProjectList
:
[],
// TODO: Rename me to projects once we back the project selector with GraphQL as well
showProjectSelector
:
false
,
};
},
...
...
@@ -94,6 +95,9 @@ export default {
toggleProjectSelector
()
{
this
.
showProjectSelector
=
!
this
.
showProjectSelector
;
},
handleProjectFetch
(
projects
)
{
this
.
graphqlProjectList
=
projects
;
},
},
};
</
script
>
...
...
@@ -111,7 +115,12 @@ export default {
>
{{
toggleButtonProps
.
text
}}
</gl-button
>
</header>
<filters
v-if=
"shouldShowDashboard"
@
filterChange=
"handleFilterChange"
/>
<filters
v-if=
"shouldShowDashboard"
:projects=
"graphqlProjectList"
@
filterChange=
"handleFilterChange"
@
projectFetch=
"handleProjectFetch"
/>
</
template
>
<instance-security-vulnerabilities
v-if=
"shouldShowDashboard"
...
...
@@ -119,6 +128,7 @@ export default {
:dashboard-documentation=
"dashboardDocumentation"
:empty-state-svg-path=
"emptyStateSvgPath"
:filters=
"filters"
@
projectFetch=
"handleProjectFetch"
/>
<gl-empty-state
v-else-if=
"shouldShowEmptyState"
...
...
ee/app/assets/javascripts/security_dashboard/components/first_class_instance_security_dashboard_vulnerabilities.vue
View file @
0d47287e
...
...
@@ -57,6 +57,7 @@ export default {
result
({
data
,
loading
})
{
this
.
isFirstResultLoading
=
loading
;
this
.
pageInfo
=
data
.
vulnerabilities
.
pageInfo
;
this
.
$emit
(
'
projectFetch
'
,
data
.
instanceSecurityDashboard
.
projects
.
nodes
);
},
error
()
{
this
.
errorLoadingVulnerabilities
=
true
;
...
...
ee/app/assets/javascripts/security_dashboard/components/first_class_vulnerability_filters.vue
View file @
0d47287e
<
script
>
import
{
initFirstClassVulnerabilityFilters
}
from
'
ee/security_dashboard/constant
s
'
;
import
{
initFirstClassVulnerabilityFilters
,
mapProjects
}
from
'
ee/security_dashboard/helper
s
'
;
import
{
ALL
}
from
'
ee/security_dashboard/store/modules/filters/constants
'
;
import
{
setFilter
}
from
'
ee/security_dashboard/store/modules/filters/utils
'
;
import
DashboardFilter
from
'
ee/security_dashboard/components/filter.vue
'
;
...
...
@@ -8,11 +8,27 @@ export default {
components
:
{
DashboardFilter
,
},
props
:
{
projects
:
{
type
:
Array
,
required
:
false
,
default
:
undefined
},
},
data
()
{
return
{
filters
:
initFirstClassVulnerabilityFilters
(),
filters
:
initFirstClassVulnerabilityFilters
(
this
.
projects
),
};
},
watch
:
{
/**
* Initially the project list empty. We fetch them dynamically from GraphQL while
* fetching the list of vulnerabilities. We display the project filter with the base
* option and when the projects are fetched we add them to the list.
*/
projects
(
newProjects
,
oldProjects
)
{
if
(
oldProjects
.
length
===
0
)
{
const
projectFilter
=
this
.
filters
[
3
];
projectFilter
.
options
=
[
projectFilter
.
options
[
0
],
...
mapProjects
(
this
.
projects
)];
}
},
},
methods
:
{
setFilter
(
options
)
{
const
selectedFilters
=
{};
...
...
ee/app/assets/javascripts/security_dashboard/constants.js
deleted
100644 → 0
View file @
2bb589b2
import
{
s__
}
from
'
~/locale
'
;
import
{
ALL
,
BASE_FILTERS
}
from
'
ee/security_dashboard/store/modules/filters/constants
'
;
import
{
REPORT_TYPES
,
SEVERITY_LEVELS
}
from
'
ee/security_dashboard/store/constants
'
;
import
{
VULNERABILITY_STATES
}
from
'
ee/vulnerabilities/constants
'
;
const
parseOptions
=
obj
=>
Object
.
entries
(
obj
).
map
(([
id
,
name
])
=>
({
id
:
id
.
toUpperCase
(),
name
}));
export
const
initFirstClassVulnerabilityFilters
=
()
=>
[
{
name
:
s__
(
'
SecurityReports|Status
'
),
id
:
'
state
'
,
options
:
[
{
id
:
ALL
,
name
:
s__
(
'
VulnerabilityStatusTypes|All
'
)
},
...
parseOptions
(
VULNERABILITY_STATES
),
],
selection
:
new
Set
([
ALL
]),
},
{
name
:
s__
(
'
SecurityReports|Severity
'
),
id
:
'
severity
'
,
options
:
[
BASE_FILTERS
.
severity
,
...
parseOptions
(
SEVERITY_LEVELS
)],
selection
:
new
Set
([
ALL
]),
},
{
name
:
s__
(
'
SecurityReports|Report type
'
),
id
:
'
reportType
'
,
options
:
[
BASE_FILTERS
.
report_type
,
...
parseOptions
(
REPORT_TYPES
)],
selection
:
new
Set
([
ALL
]),
},
];
export
default
()
=>
({});
ee/app/assets/javascripts/security_dashboard/graphql/group_vulnerabilities.graphql
View file @
0d47287e
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "ee/vulnerabilities/graphql/vulnerability.fragment.graphql"
#import "./project.fragment.graphql"
query
group
(
$fullPath
:
ID
!,
$after
:
String
,
$first
:
Int
,
$fullPath
:
ID
!
$after
:
String
$first
:
Int
$projectId
:
[
ID
!]
$severity
:
[
VulnerabilitySeverity
!]
$reportType
:
[
VulnerabilityReportType
!]
$state
:
[
VulnerabilityState
!]
)
{
group
(
fullPath
:
$fullPath
)
{
projects
(
hasVulnerabilities
:
true
)
{
nodes
{
...
Project
}
}
vulnerabilities
(
after
:
$after
,
first
:
$first
,
after
:
$after
first
:
$first
severity
:
$severity
reportType
:
$reportType
state
:
$state
projectId
:
$projectId
){
nodes
{
...
Vulnerability
...
...
ee/app/assets/javascripts/security_dashboard/graphql/instance_vulnerabilities.graphql
View file @
0d47287e
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
#import "ee/vulnerabilities/graphql/vulnerability.fragment.graphql"
#import "./project.fragment.graphql"
query
instance
(
$after
:
String
,
$after
:
String
$first
:
Int
$projectId
:
[
ID
!]
$severity
:
[
VulnerabilitySeverity
!]
$reportType
:
[
VulnerabilityReportType
!]
$state
:
[
VulnerabilityState
!]
)
{
instanceSecurityDashboard
{
projects
{
nodes
{
...
Project
}
}
}
vulnerabilities
(
after
:
$after
,
first
:
$first
,
after
:
$after
first
:
$first
severity
:
$severity
reportType
:
$reportType
state
:
$state
projectId
:
$projectId
)
{
nodes
{
...
Vulnerability
...
...
ee/app/assets/javascripts/security_dashboard/graphql/project.fragment.graphql
0 → 100644
View file @
0d47287e
fragment
Project
on
Project
{
id
name
}
ee/app/assets/javascripts/security_dashboard/helpers.js
0 → 100644
View file @
0d47287e
import
{
s__
}
from
'
~/locale
'
;
import
{
ALL
,
BASE_FILTERS
}
from
'
ee/security_dashboard/store/modules/filters/constants
'
;
import
{
REPORT_TYPES
,
SEVERITY_LEVELS
}
from
'
ee/security_dashboard/store/constants
'
;
import
{
VULNERABILITY_STATES
}
from
'
ee/vulnerabilities/constants
'
;
const
parseOptions
=
obj
=>
Object
.
entries
(
obj
).
map
(([
id
,
name
])
=>
({
id
:
id
.
toUpperCase
(),
name
}));
export
const
mapProjects
=
projects
=>
projects
.
map
(
p
=>
({
id
:
p
.
id
.
split
(
'
/
'
).
pop
(),
name
:
p
.
name
}));
export
const
initFirstClassVulnerabilityFilters
=
projects
=>
{
const
filters
=
[
{
name
:
s__
(
'
SecurityReports|Status
'
),
id
:
'
state
'
,
options
:
[
{
id
:
ALL
,
name
:
s__
(
'
VulnerabilityStatusTypes|All
'
)
},
...
parseOptions
(
VULNERABILITY_STATES
),
],
selection
:
new
Set
([
ALL
]),
},
{
name
:
s__
(
'
SecurityReports|Severity
'
),
id
:
'
severity
'
,
options
:
[
BASE_FILTERS
.
severity
,
...
parseOptions
(
SEVERITY_LEVELS
)],
selection
:
new
Set
([
ALL
]),
},
{
name
:
s__
(
'
SecurityReports|Report type
'
),
id
:
'
reportType
'
,
options
:
[
BASE_FILTERS
.
report_type
,
...
parseOptions
(
REPORT_TYPES
)],
selection
:
new
Set
([
ALL
]),
},
];
if
(
Array
.
isArray
(
projects
))
{
filters
.
push
({
name
:
s__
(
'
SecurityReports|Project
'
),
id
:
'
projectId
'
,
options
:
[
BASE_FILTERS
.
project_id
,
...
mapProjects
(
projects
)],
selection
:
new
Set
([
ALL
]),
});
}
return
filters
;
};
export
default
()
=>
({});
ee/changelogs/unreleased/214386-project-dropdown-filter.yml
0 → 100644
View file @
0d47287e
---
title
:
Add project filter
merge_request
:
31444
author
:
type
:
added
ee/spec/frontend/security_dashboard/components/first_class_group_security_dashboard_spec.js
View file @
0d47287e
...
...
@@ -52,7 +52,15 @@ describe('First Class Group Dashboard Component', () => {
expect
(
findFilters
().
exists
()).
toBe
(
true
);
});
it
(
'
it responds to the filterChange event
'
,
()
=>
{
it
(
'
responds to the projectFetch event
'
,
()
=>
{
const
projects
=
[{
id
:
1
,
name
:
'
GitLab Org
'
}];
findGroupVulnerabilities
().
vm
.
$listeners
.
projectFetch
(
projects
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
findFilters
().
props
(
'
projects
'
)).
toEqual
(
projects
);
});
});
it
(
'
responds to the filterChange event
'
,
()
=>
{
const
filters
=
{
severity
:
'
critical
'
};
findFilters
().
vm
.
$listeners
.
filterChange
(
filters
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
...
...
ee/spec/frontend/security_dashboard/components/first_class_instance_security_dashboard_spec.js
View file @
0d47287e
...
...
@@ -92,7 +92,15 @@ describe('First Class Instance Dashboard Component', () => {
expect
(
findFilters
().
exists
()).
toBe
(
true
);
});
it
(
'
it responds to the filterChange event
'
,
()
=>
{
it
(
'
responds to the projectFetch event
'
,
()
=>
{
const
projects
=
[{
id
:
1
,
name
:
'
GitLab Org
'
}];
findInstanceVulnerabilities
().
vm
.
$listeners
.
projectFetch
(
projects
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
findFilters
().
props
(
'
projects
'
)).
toEqual
(
projects
);
});
});
it
(
'
responds to the filterChange event
'
,
()
=>
{
const
filters
=
{
severity
:
'
critical
'
};
findFilters
().
vm
.
$listeners
.
filterChange
(
filters
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
...
...
ee/spec/frontend/security_dashboard/components/first_class_vulnerability_filters_spec.js
View file @
0d47287e
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
initFirstClassVulnerabilityFilters
}
from
'
ee/security_dashboard/
constant
s
'
;
import
{
initFirstClassVulnerabilityFilters
}
from
'
ee/security_dashboard/
helper
s
'
;
import
Filters
from
'
ee/security_dashboard/components/first_class_vulnerability_filters.vue
'
;
import
Filter
from
'
ee/security_dashboard/components/filter.vue
'
;
describe
(
'
First class vulnerability filters component
'
,
()
=>
{
let
wrapper
;
let
filters
;
const
projects
=
[
{
id
:
'
gid://gitlab/Project/11
'
,
name
:
'
GitLab Org
'
},
{
id
:
'
gid://gitlab/Project/12
'
,
name
:
'
GitLab Com
'
},
];
const
findFilters
=
()
=>
wrapper
.
findAll
(
Filter
);
const
findFirstFilter
=
()
=>
findFilters
().
at
(
0
);
const
findLastFilter
=
()
=>
findFilters
().
at
(
filters
.
length
-
1
);
const
createComponent
=
()
=>
{
wrapper
=
shallowMount
(
Filters
);
const
createComponent
=
(
{
propsData
}
=
{}
)
=>
{
return
shallowMount
(
Filters
,
{
propsData
}
);
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
on render
'
,
()
=>
{
describe
(
'
on render
without project filter
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
wrapper
=
createComponent
();
filters
=
initFirstClassVulnerabilityFilters
();
});
...
...
@@ -41,29 +47,79 @@ describe('First class vulnerability filters component', () => {
expect
(
stub
).
toHaveBeenCalledWith
(
options
);
});
});
describe
(
'
when setFilter is called
'
,
()
=>
{
let
filterId
;
let
optionId
;
describe
(
'
when project filter is populated dynamically
'
,
()
=>
{
beforeEach
(()
=>
{
filters
=
initFirstClassVulnerabilityFilters
([]);
wrapper
=
createComponent
({
propsData
:
{
projects
:
[]
}
});
});
beforeEach
(()
=>
{
filterId
=
filters
[
0
].
id
;
optionId
=
filters
[
0
].
options
[
1
].
id
;
it
(
'
should render the project filter with one option
'
,
()
=>
{
expect
(
findLastFilter
().
props
(
'
filter
'
)).
toEqual
({
id
:
'
projectId
'
,
name
:
'
Project
'
,
options
:
[{
id
:
'
all
'
,
name
:
'
All projects
'
}],
selection
:
new
Set
([
'
all
'
]),
});
});
wrapper
.
vm
.
setFilter
({
filterId
,
optionId
});
it
(
'
should set the projects dynamically
'
,
()
=>
{
wrapper
.
setProps
({
projects
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
findLastFilter
().
props
(
'
filter
'
)).
toEqual
(
expect
.
objectContaining
({
options
:
[
{
id
:
'
all
'
,
name
:
'
All projects
'
},
{
id
:
'
11
'
,
name
:
'
GitLab Org
'
},
{
id
:
'
12
'
,
name
:
'
GitLab Com
'
},
],
}),
);
});
});
});
it
(
'
should set the filters locally
'
,
()
=>
{
const
expectedFilters
=
initFirstClassVulnerabilityFilters
();
expectedFilters
[
0
].
selection
=
new
Set
([
optionId
]);
describe
(
'
when project filter is ready on mount
'
,
()
=>
{
beforeEach
(()
=>
{
filters
=
initFirstClassVulnerabilityFilters
([]);
wrapper
=
createComponent
({
propsData
:
{
projects
}
});
});
expect
(
wrapper
.
vm
.
filters
).
toEqual
(
expectedFilters
);
});
it
(
'
should set the projects dynamically
'
,
()
=>
{
expect
(
findLastFilter
().
props
(
'
filter
'
)).
toEqual
(
expect
.
objectContaining
({
options
:
[
{
id
:
'
all
'
,
name
:
'
All projects
'
},
{
id
:
'
11
'
,
name
:
'
GitLab Org
'
},
{
id
:
'
12
'
,
name
:
'
GitLab Com
'
},
],
}),
);
});
});
it
(
'
should emit selected filters when a filter is set
'
,
()
=>
{
expect
(
wrapper
.
emitted
().
filterChange
).
toBeTruthy
();
expect
(
wrapper
.
emitted
().
filterChange
[
0
]).
toEqual
([{
[
filterId
]:
[
optionId
]
}]);
});
describe
(
'
when setFilter is called
'
,
()
=>
{
let
filterId
;
let
optionId
;
beforeEach
(()
=>
{
filterId
=
filters
[
0
].
id
;
optionId
=
filters
[
0
].
options
[
1
].
id
;
wrapper
=
createComponent
();
wrapper
.
vm
.
setFilter
({
filterId
,
optionId
});
});
it
(
'
should set the filters locally
'
,
()
=>
{
const
expectedFilters
=
initFirstClassVulnerabilityFilters
();
expectedFilters
[
0
].
selection
=
new
Set
([
optionId
]);
expect
(
wrapper
.
vm
.
filters
).
toEqual
(
expectedFilters
);
});
it
(
'
should emit selected filters when a filter is set
'
,
()
=>
{
expect
(
wrapper
.
emitted
().
filterChange
).
toBeTruthy
();
expect
(
wrapper
.
emitted
().
filterChange
[
0
]).
toEqual
([{
[
filterId
]:
[
optionId
]
}]);
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment