Commit 0508ef6c authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'add-vulnerabilities-to-group-type' into 'master'

Add vulnerabilities field to GroupType

See merge request gitlab-org/gitlab!27944
parents f83af0f4 1c95ccca
...@@ -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