diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index c6473391477..23222cbd37c 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -12,10 +12,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
before_action :build_merge_request, except: [:create]
def new
- # n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/40934
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- define_new_vars
- end
+ define_new_vars
end
def create
diff --git a/changelogs/unreleased/38347-expose-labels-description-html.yml b/changelogs/unreleased/38347-expose-labels-description-html.yml
new file mode 100644
index 00000000000..ea3972297e3
--- /dev/null
+++ b/changelogs/unreleased/38347-expose-labels-description-html.yml
@@ -0,0 +1,5 @@
+---
+title: Expose description_html for labels
+merge_request: 21413
+author:
+type: changed
diff --git a/doc/api/epics.md b/doc/api/epics.md
index 4b20534eeed..109c12c1052 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -53,6 +53,7 @@ GET /groups/:id/epics?state=opened
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `author_id` | integer | no | Return epics created by the given user `id` |
| `labels` | string | no | Return epics matching a comma separated list of labels names. Label names from the epic group or a parent group can be used |
+| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. Introduced in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413)|
| `order_by` | string | no | Return epics ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return epics sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search epics against their `title` and `description` |
diff --git a/doc/api/group_labels.md b/doc/api/group_labels.md
index f3c3a821354..f41f3a0a402 100644
--- a/doc/api/group_labels.md
+++ b/doc/api/group_labels.md
@@ -4,6 +4,9 @@
This API supports managing of [group labels](../user/project/labels.md#project-labels-and-group-labels). It allows to list, create, update, and delete group labels. Furthermore, users can subscribe and unsubscribe to and from group labels.
+NOTE: **Note:**
+The `description_html` - was added to response JSON in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413).
+
## List group labels
Get all labels for a given group.
@@ -32,6 +35,7 @@ Example response:
"color": "#FF0000",
"text_color" : "#FFFFFF",
"description": null,
+ "description_html": null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
@@ -43,6 +47,7 @@ Example response:
"color": "#228B22",
"text_color" : "#FFFFFF",
"description": null,
+ "description_html": null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
@@ -78,6 +83,7 @@ Example response:
"color": "#FF0000",
"text_color" : "#FFFFFF",
"description": null,
+ "description_html": null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
@@ -113,6 +119,7 @@ Example response:
"color": "#FFA500",
"text_color" : "#FFFFFF",
"description": "Describes new ideas",
+ "description_html": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
@@ -149,6 +156,7 @@ Example response:
"color": "#FFA500",
"text_color" : "#FFFFFF",
"description": "Describes new ideas",
+ "description_html": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
@@ -204,6 +212,7 @@ Example response:
"color": "#FFA500",
"text_color" : "#FFFFFF",
"description": "Describes new ideas",
+ "description_html": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
@@ -239,6 +248,7 @@ Example response:
"color": "#FFA500",
"text_color" : "#FFFFFF",
"description": "Describes new ideas",
+ "description_html": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 383d190a045..3c28b55d1d6 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -47,7 +47,7 @@ GET /issues?confidential=true
| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state` | string | no | Return `all` issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
-| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:text_color`. Default is `false`. |
+| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. The `description_html` attribute was introduced in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413)|
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.
_([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
@@ -203,7 +203,7 @@ GET /groups/:id/issues?confidential=true
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
-| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:text_color`. Default is `false`. |
+| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. The `description_html` attribute was introduced in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413) |
| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.
_([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
@@ -358,7 +358,7 @@ GET /projects/:id/issues?confidential=true
| `iids[]` | integer array | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `None` lists all issues with no labels. `Any` lists all issues with at least one label. `No+Label` (Deprecated) lists all issues with no labels. Predefined names are case-insensitive. |
-| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:text_color`. Default is `false`. |
+| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. `description_html` Introduced in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413) |
| `milestone` | string | no | The milestone title. `None` lists all issues with no milestone. `Any` lists all issues that have an assigned milestone. |
| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.
_([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
diff --git a/doc/api/labels.md b/doc/api/labels.md
index 525dbe02e5f..ac5156e8c20 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -1,5 +1,8 @@
# Labels API
+NOTE: **Note:**
+The `description_html` - was added to response JSON in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413).
+
## List labels
Get all labels for a given project.
@@ -28,6 +31,7 @@ Example response:
"color" : "#d9534f",
"text_color" : "#FFFFFF",
"description": "Bug reported by user",
+ "description_html": "Bug reported by user",
"open_issues_count": 1,
"closed_issues_count": 0,
"open_merge_requests_count": 1,
@@ -41,6 +45,7 @@ Example response:
"text_color" : "#FFFFFF",
"name" : "confirmed",
"description": "Confirmed issue",
+ "description_html": "Confirmed issue",
"open_issues_count": 2,
"closed_issues_count": 5,
"open_merge_requests_count": 0,
@@ -54,6 +59,7 @@ Example response:
"color" : "#d9534f",
"text_color" : "#FFFFFF",
"description": "Critical issue. Need fix ASAP",
+ "description_html": "Critical issue. Need fix ASAP",
"open_issues_count": 1,
"closed_issues_count": 3,
"open_merge_requests_count": 1,
@@ -67,6 +73,7 @@ Example response:
"color" : "#f0ad4e",
"text_color" : "#FFFFFF",
"description": "Issue about documentation",
+ "description_html": "Issue about documentation",
"open_issues_count": 1,
"closed_issues_count": 0,
"open_merge_requests_count": 2,
@@ -80,6 +87,7 @@ Example response:
"text_color" : "#FFFFFF",
"name" : "enhancement",
"description": "Enhancement proposal",
+ "description_html": "Enhancement proposal",
"open_issues_count": 1,
"closed_issues_count": 0,
"open_merge_requests_count": 1,
@@ -117,6 +125,7 @@ Example response:
"color" : "#d9534f",
"text_color" : "#FFFFFF",
"description": "Bug reported by user",
+ "description_html": "Bug reported by user",
"open_issues_count": 1,
"closed_issues_count": 0,
"open_merge_requests_count": 1,
@@ -155,6 +164,7 @@ Example response:
"color" : "#5843AD",
"text_color" : "#FFFFFF",
"description":null,
+ "description_html":null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
@@ -214,6 +224,7 @@ Example response:
"color" : "#8E44AD",
"text_color" : "#FFFFFF",
"description": "Documentation",
+ "description_html": "Documentation",
"open_issues_count": 1,
"closed_issues_count": 0,
"open_merge_requests_count": 2,
@@ -252,6 +263,7 @@ Example response:
"name" : "documentation",
"color" : "#8E44AD",
"description": "Documentation",
+ "description_html": "Documentation",
"open_issues_count": 1,
"closed_issues_count": 0,
"open_merge_requests_count": 2,
@@ -289,6 +301,7 @@ Example response:
"color" : "#d9534f",
"text_color" : "#FFFFFF",
"description": "Bug reported by user",
+ "description_html": "Bug reported by user",
"open_issues_count": 1,
"closed_issues_count": 0,
"open_merge_requests_count": 1,
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index d17db8deeb2..c722c966273 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -45,6 +45,7 @@ Parameters:
| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. `No+Label` (Deprecated) lists all merge requests with no labels. Predefined names are case-insensitive. |
+| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. Introduced in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413) |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
| `created_before` | datetime | no | Return merge requests created on or before the given time |
| `updated_after` | datetime | no | Return merge requests updated on or after the given time |
@@ -213,6 +214,7 @@ Parameters:
| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. `No+Label` (Deprecated) lists all merge requests with no labels. Predefined names are case-insensitive. |
+| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. Introduced in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413) |
| `created_after` | datetime | no | Return merge requests created on or after the given time |
| `created_before` | datetime | no | Return merge requests created on or before the given time |
| `updated_after` | datetime | no | Return merge requests updated on or after the given time |
@@ -373,6 +375,7 @@ Parameters:
| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. `No+Label` (Deprecated) lists all merge requests with no labels. Predefined names are case-insensitive. |
+| `with_labels_details` | Boolean | no | If `true`, response will return more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. Introduced in [GitLab 12.7](https://gitlab.com/gitlab-org/gitlab/merge_requests/21413)|
| `created_after` | datetime | no | Return merge requests created on or after the given time |
| `created_before` | datetime | no | Return merge requests created on or before the given time |
| `updated_after` | datetime | no | Return merge requests updated on or after the given time |
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index c82821c3ca4..a86fb44caa1 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -779,9 +779,12 @@ module API
expose :author, :assignees, using: Entities::UserBasic
expose :source_project_id, :target_project_id
- expose :labels do |merge_request|
- # Avoids an N+1 query since labels are preloaded
- merge_request.labels.map(&:title).sort
+ expose :labels do |merge_request, options|
+ if options[:with_labels_details]
+ ::API::Entities::LabelBasic.represent(merge_request.labels.sort_by(&:title))
+ else
+ merge_request.labels.map(&:title).sort
+ end
end
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: Entities::Milestone
@@ -1166,7 +1169,7 @@ module API
end
class LabelBasic < Grape::Entity
- expose :id, :name, :color, :description, :text_color
+ expose :id, :name, :color, :description, :description_html, :text_color
end
class Label < LabelBasic
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 1ef27d9f1b1..d0772c70ffb 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -48,7 +48,7 @@ module API
end
params :issues_params do
- optional :with_labels_details, type: Boolean, desc: 'Return more label data than just lable title', default: false
+ optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
optional :state, type: String, values: %w[opened closed all], default: 'all',
desc: 'Return opened, closed, or all issues'
optional :order_by, type: String, values: Helpers::IssuesHelpers.sort_options, default: 'created_at',
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 0e161d5589a..b7c9217988e 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -73,7 +73,7 @@ module API
end
def serializer_options_for(merge_requests)
- options = { with: Entities::MergeRequestBasic, current_user: current_user }
+ options = { with: Entities::MergeRequestBasic, current_user: current_user, with_labels_details: declared_params[:with_labels_details] }
if params[:view] == 'simple'
options[:with] = Entities::MergeRequestSimple
@@ -106,6 +106,7 @@ module API
desc: 'Return merge requests sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
+ optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time'
diff --git a/spec/fixtures/api/schemas/public_api/v4/label_basic.json b/spec/fixtures/api/schemas/public_api/v4/label_basic.json
index 37bbdcb14fe..a501bc2ec56 100644
--- a/spec/fixtures/api/schemas/public_api/v4/label_basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/label_basic.json
@@ -5,6 +5,7 @@
"name",
"color",
"description",
+ "description_html",
"text_color"
],
"properties": {
@@ -15,6 +16,7 @@
"pattern": "^#[0-9A-Fa-f]{3}{1,2}$"
},
"description": { "type": ["string", "null"] },
+ "description_html": { "type": ["string", "null"] },
"text_color": {
"type": "string",
"pattern": "^#[0-9A-Fa-f]{3}{1,2}$"
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index ab42dbe7cd1..203781bb6fc 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -57,6 +57,11 @@ Object.assign(global, {
// custom-jquery-matchers was written for an old Jest version, we need to make it compatible
Object.entries(jqueryMatchers).forEach(([matcherName, matcherFactory]) => {
+ // Don't override existing Jest matcher
+ if (matcherName === 'toHaveLength') {
+ return;
+ }
+
expect.extend({
[matcherName]: matcherFactory().compare,
});
diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb
index 59aeb91edd2..e031cc9b0c6 100644
--- a/spec/requests/api/issues/get_project_issues_spec.rb
+++ b/spec/requests/api/issues/get_project_issues_spec.rb
@@ -299,6 +299,26 @@ describe API::Issues do
it_behaves_like 'labeled issues with labels and label_name params'
end
+ context 'with_labels_details' do
+ let(:label_b) { create(:label, title: 'foo', project: project) }
+ let(:label_c) { create(:label, title: 'bar', project: project) }
+
+ it 'avoids N+1 queries' do
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ get api("/projects/#{project.id}/issues?with_labels_details=true", user)
+ end.count
+
+ new_issue = create(:issue, project: project)
+ create(:label_link, label: label, target: new_issue)
+ create(:label_link, label: label_b, target: new_issue)
+ create(:label_link, label: label_c, target: new_issue)
+
+ expect do
+ get api("/projects/#{project.id}/issues?with_labels_details=true", user)
+ end.not_to exceed_all_query_limit(control_count)
+ end
+ end
+
it 'returns issues matching given search string for title' do
get api("#{base_url}/issues?search=#{issue.title}", user)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 582bf19820d..2e9031c49bb 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -88,6 +88,34 @@ describe API::MergeRequests do
expect(json_response.first['merge_commit_sha']).not_to be_nil
expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha)
end
+
+ context 'with labels_details' do
+ it 'returns labels with details' do
+ path = endpoint_path + "?with_labels_details=true"
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.last['labels'].pluck('name')).to eq([label2.title, label.title])
+ expect(json_response.last['labels'].first).to match_schema('/public_api/v4/label_basic')
+ end
+
+ it 'avoids N+1 queries' do
+ path = endpoint_path + "?with_labels_details=true"
+
+ control = ActiveRecord::QueryRecorder.new do
+ get api(path, user)
+ end.count
+
+ mr = create(:merge_request)
+ create(:label_link, label: label, target: mr)
+ create(:label_link, label: label2, target: mr)
+
+ expect do
+ get api(path, user)
+ end.not_to exceed_query_limit(control)
+ end
+ end
end
it 'returns an array of all merge_requests using simple mode' do