Commit 1c95ccca authored by Avielle Wolfe's avatar Avielle Wolfe Committed by Shinya Maeda

Add vulnerabilities field to GroupType

This field will support the first class vulnerability list on the group
security dashboard.

https://gitlab.com/gitlab-org/gitlab/-/issues/207438
parent 616db62b
...@@ -3506,6 +3506,52 @@ type Group { ...@@ -3506,6 +3506,52 @@ type Group {
""" """
visibility: String visibility: String
"""
Vulnerabilities reported on the projects in the group and its subgroups.
Available only when feature flag `first_class_vulnerabilities` is enabled
"""
vulnerabilities(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
Filter vulnerabilities by project
"""
projectId: [ID!]
"""
Filter vulnerabilities by report type
"""
reportType: [VulnerabilityReportType!]
"""
Filter vulnerabilities by severity
"""
severity: [VulnerabilitySeverity!]
"""
Filter vulnerabilities by state
"""
state: [VulnerabilityState!]
): VulnerabilityConnection
""" """
Web URL of the group Web URL of the group
""" """
......
...@@ -9932,6 +9932,131 @@ ...@@ -9932,6 +9932,131 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "vulnerabilities",
"description": "Vulnerabilities reported on the projects in the group and its subgroups. Available only when feature flag `first_class_vulnerabilities` is enabled",
"args": [
{
"name": "projectId",
"description": "Filter vulnerabilities by project",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "reportType",
"description": "Filter vulnerabilities by report type",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "VulnerabilityReportType",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "severity",
"description": "Filter vulnerabilities by severity",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "VulnerabilitySeverity",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "state",
"description": "Filter vulnerabilities by state",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "VulnerabilityState",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "VulnerabilityConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "webUrl", "name": "webUrl",
"description": "Web URL of the group", "description": "Web URL of the group",
......
...@@ -25,6 +25,13 @@ module EE ...@@ -25,6 +25,13 @@ module EE
description: 'Time logged in issues by group members', description: 'Time logged in issues by group members',
complexity: 5, complexity: 5,
resolver: ::Resolvers::TimelogResolver resolver: ::Resolvers::TimelogResolver
field :vulnerabilities,
::Types::VulnerabilityType.connection_type,
null: true,
description: 'Vulnerabilities reported on the projects in the group and its subgroups',
resolver: Resolvers::VulnerabilitiesResolver,
feature_flag: :first_class_vulnerabilities
end end
end end
end end
......
...@@ -11,6 +11,7 @@ describe GitlabSchema.types['Group'] do ...@@ -11,6 +11,7 @@ describe GitlabSchema.types['Group'] do
it { expect(described_class).to have_graphql_field(:groupTimelogsEnabled) } it { expect(described_class).to have_graphql_field(:groupTimelogsEnabled) }
it { expect(described_class).to have_graphql_field(:timelogs, complexity: 5) } it { expect(described_class).to have_graphql_field(:timelogs, complexity: 5) }
it { expect(described_class).to have_graphql_field(:vulnerabilities) }
describe 'timelogs field' do describe 'timelogs field' do
subject { described_class.fields['timelogs'] } subject { described_class.fields['timelogs'] }
...@@ -21,4 +22,64 @@ describe GitlabSchema.types['Group'] do ...@@ -21,4 +22,64 @@ describe GitlabSchema.types['Group'] do
is_expected.to have_non_null_graphql_type(Types::TimelogType.connection_type) is_expected.to have_non_null_graphql_type(Types::TimelogType.connection_type)
end end
end end
describe 'vulnerabilities' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, namespace: group) }
let_it_be(:user) { create(:user) }
let_it_be(:vulnerability) do
create(:vulnerability, :detected, :critical, project: project, title: 'A terrible one!')
end
let_it_be(:query) do
%(
query {
group(fullPath:"#{group.full_path}") {
name
vulnerabilities {
nodes {
title
severity
state
}
}
}
}
)
end
before do
group.add_developer(user)
end
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
context 'when first_class_vulnerabilities is disabled' do
before do
stub_feature_flags(first_class_vulnerabilities: false)
end
it 'is null' do
vulnerabilities = subject.dig('data', 'group', 'vulnerabilities')
expect(vulnerabilities).to be_nil
end
end
context 'when first_class_vulnerabilities is enabled' do
before do
stub_feature_flags(first_class_vulnerabilities: true)
stub_licensed_features(security_dashboard: true)
end
it "returns the vulnerabilities for all projects in the group and its subgroups" do
vulnerabilities = subject.dig('data', 'group', 'vulnerabilities', 'nodes')
expect(vulnerabilities.count).to be(1)
expect(vulnerabilities.first['title']).to eq('A terrible one!')
expect(vulnerabilities.first['state']).to eq('DETECTED')
expect(vulnerabilities.first['severity']).to eq('CRITICAL')
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