diff --git a/app/models/issue.rb b/app/models/issue.rb index 91d4b78f7c88f2ddc9b6f418317c676426061fc9..5c91656cbc025107589dc8e5ec776eb2c6e7c154 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -135,7 +135,7 @@ def most_recent scope :with_prometheus_alert_events, -> { joins(:issues_prometheus_alert_events) } scope :with_self_managed_prometheus_alert_events, -> { joins(:issues_self_managed_prometheus_alert_events) } scope :with_api_entity_associations, -> { - preload(:timelogs, :closed_by, :assignees, :author, :labels, + preload(:timelogs, :closed_by, :assignees, :author, :labels, :issuable_severity, milestone: { project: [:route, { namespace: :route }] }, project: [:route, { namespace: :route }]) } diff --git a/doc/api/issues.md b/doc/api/issues.md index ef0727e1c135d9f3039ca2502c1960c3f9047389..e82aa8da8ede932123adf1dae8c6f6a3ccf9d2a5 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -165,6 +165,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links":{ "self":"http://gitlab.example.com/api/v4/projects/1/issues/76", "notes":"http://gitlab.example.com/api/v4/projects/1/issues/76/notes", @@ -390,6 +391,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links":{ "self":"http://gitlab.example.com/api/v4/projects/4/issues/41", "notes":"http://gitlab.example.com/api/v4/projects/4/issues/41/notes", @@ -598,6 +600,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links":{ "self":"http://gitlab.example.com/api/v4/projects/4/issues/41", "notes":"http://gitlab.example.com/api/v4/projects/4/issues/41/notes", @@ -755,6 +758,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "task_completion_status": { "count": 0, "completed_count": 0 @@ -917,6 +921,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links": { "self": "http://gitlab.example.com/api/v4/projects/1/issues/2", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes", @@ -1064,6 +1069,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links": { "self": "http://gitlab.example.com/api/v4/projects/1/issues/2", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes", @@ -1238,6 +1244,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links": { "self": "http://gitlab.example.com/api/v4/projects/1/issues/2", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes", @@ -1421,6 +1428,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links": { "self": "http://gitlab.example.com/api/v4/projects/1/issues/2", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes", @@ -1560,6 +1568,7 @@ Example response: "confidential":false, "discussion_locked":null, "issue_type":"issue", + "severity": "UNKNOWN", "web_url":"https://gitlab.example.com/namespace1/project2/-/issues/1", "time_stats":{ "time_estimate":0, @@ -1669,6 +1678,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "_links": { "self": "http://gitlab.example.com/api/v4/projects/1/issues/2", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes", @@ -1797,6 +1807,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "task_completion_status":{ "count":0, "completed_count":0 @@ -1906,6 +1917,7 @@ Example response: "confidential": false, "discussion_locked": false, "issue_type": "issue", + "severity": "UNKNOWN", "task_completion_status":{ "count":0, "completed_count":0 diff --git a/lib/api/entities/issue.rb b/lib/api/entities/issue.rb index e2506cc596e253fc6088496630cfc94e1799d498..f87ef093cd8bab8917d3888085272d2cea9e9cd4 100644 --- a/lib/api/entities/issue.rb +++ b/lib/api/entities/issue.rb @@ -35,6 +35,10 @@ class Issue < IssueBasic issue end + expose :severity, + format_with: :upcase, + documentation: { type: "String", desc: "One of #{::IssuableSeverity.severities.keys.map(&:upcase)}" } + # Calculating the value of subscribed field triggers Markdown # processing. We can't do that for multiple issues / merge # requests in a single API request. diff --git a/spec/fixtures/api/schemas/public_api/v4/issue.json b/spec/fixtures/api/schemas/public_api/v4/issue.json index 3173a8ebfb5eca11c8ccf1d15840eeda695098c4..90b368b52267b5ac64a60fb24905c69041dbe2c9 100644 --- a/spec/fixtures/api/schemas/public_api/v4/issue.json +++ b/spec/fixtures/api/schemas/public_api/v4/issue.json @@ -86,6 +86,7 @@ "due_date": { "type": ["string", "null"] }, "confidential": { "type": "boolean" }, "web_url": { "type": "uri" }, + "severity": { "type": "string", "enum": ["UNKNOWN", "LOW", "MEDIUM", "HIGH", "CRITICAL"] }, "time_stats": { "time_estimate": { "type": "integer" }, "total_time_spent": { "type": "integer" }, diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb index c5e57b5b18b0e421fd32ef98fc2affdecd71e820..1419d39981ac0157f475be49ed5916e0a7d1d76f 100644 --- a/spec/requests/api/issues/issues_spec.rb +++ b/spec/requests/api/issues/issues_spec.rb @@ -554,6 +554,27 @@ end end + context 'with incident issues' do + let_it_be(:incident) { create(:incident, project: project) } + + it 'avoids N+1 queries' do + get api('/issues', user) # warm up + + control = ActiveRecord::QueryRecorder.new do + get api('/issues', user) + end + + create(:incident, project: project) + create(:incident, project: project) + + expect do + get api('/issues', user) + end.not_to exceed_query_limit(control) + # 2 pre-existed issues + 3 incidents + expect(json_response.count).to eq(5) + end + end + context 'filter by labels or label_name param' do context 'N+1' do let(:label_b) { create(:label, title: 'foo', project: project) }