diff options
30 files changed, 724 insertions, 272 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2a299ea79ef..c652b6c75e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -606,7 +606,7 @@ static-analysis: docs lint: <<: *dedicated-runner <<: *except-qa - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-docs-lint" stage: test cache: {} dependencies: [] @@ -614,8 +614,8 @@ docs lint: script: - scripts/lint-doc.sh - scripts/lint-changelog-yaml - - mv doc/ /nanoc/content/ - - cd /nanoc + - mv doc/ /tmp/gitlab-docs/content/ + - cd /tmp/gitlab-docs # Build HTML from Markdown - bundle exec nanoc # Check the internal links diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js index 9aa83ce6269..72f3f70b98f 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js @@ -79,10 +79,13 @@ export default class Todos { .then(({ data }) => { this.updateRowState(target); this.updateBadges(data); - }).catch(() => flash(__('Error updating todo status.'))); + }).catch(() => { + this.updateRowState(target, true); + return flash(__('Error updating todo status.')); + }); } - updateRowState(target) { + updateRowState(target, isInactive = false) { const row = target.closest('li'); const restoreBtn = row.querySelector('.js-undo-todo'); const doneBtn = row.querySelector('.js-done-todo'); @@ -91,7 +94,10 @@ export default class Todos { target.removeAttribute('disabled'); target.classList.remove('disabled'); - if (target === doneBtn) { + if (isInactive === true) { + restoreBtn.classList.add('hidden'); + doneBtn.classList.remove('hidden'); + } else if (target === doneBtn) { row.classList.add('done-reversible'); restoreBtn.classList.remove('hidden'); } else if (target === restoreBtn) { diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 11597c7e85e..be41dbfc61f 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -20,20 +20,24 @@ display: inline-block; overflow-x: auto; border: 0; - border-color: $gray-100; + border-color: $gl-gray-100; @supports (width: fit-content) { display: block; width: fit-content; } + tbody { + background-color: $white-light; + } + tr { th { - border-bottom: solid 2px $gray-100; + border-bottom: solid 2px $gl-gray-100; } td { - border-color: $gray-100; + border-color: $gl-gray-100; } } } diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss index 7d90452e1f4..759b4f333ca 100644 --- a/app/assets/stylesheets/framework/variables_overrides.scss +++ b/app/assets/stylesheets/framework/variables_overrides.scss @@ -18,3 +18,4 @@ $success: $green-500; $info: $blue-500; $warning: $orange-500; $danger: $red-500; +$zindex-modal-backdrop: 1040; diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss index 2e2ab8532d2..59fdbf31fe9 100644 --- a/app/assets/stylesheets/performance_bar.scss +++ b/app/assets/stylesheets/performance_bar.scss @@ -1,4 +1,5 @@ @import 'framework/variables'; +@import 'framework/variables_overrides'; @import 'peek/views/rblineprof'; #js-peek { @@ -6,7 +7,7 @@ left: 0; top: 0; width: 100%; - z-index: 1039; + z-index: #{$zindex-modal-backdrop + 1}; height: $performance-bar-height; background: $black; diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb index cb9ab35de85..26768c628ca 100644 --- a/app/controllers/groups/labels_controller.rb +++ b/app/controllers/groups/labels_controller.rb @@ -12,7 +12,8 @@ class Groups::LabelsController < Groups::ApplicationController def index respond_to do |format| format.html do - @labels = GroupLabelsFinder.new(@group, params.merge(sort: sort)).execute + @labels = GroupLabelsFinder + .new(current_user, @group, params.merge(sort: sort)).execute end format.json do render json: LabelSerializer.new.represent_appearance(available_labels) diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index a0ce3b08d9f..640038818f2 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -163,6 +163,7 @@ class Projects::LabelsController < Projects::ApplicationController project_id: @project.id, include_ancestor_groups: params[:include_ancestor_groups], search: params[:search], + subscribed: params[:subscribed], sort: sort).execute end diff --git a/app/finders/group_labels_finder.rb b/app/finders/group_labels_finder.rb index 903023033ed..a668a0f0fae 100644 --- a/app/finders/group_labels_finder.rb +++ b/app/finders/group_labels_finder.rb @@ -1,17 +1,29 @@ # frozen_string_literal: true class GroupLabelsFinder - attr_reader :group, :params + attr_reader :current_user, :group, :params - def initialize(group, params = {}) + def initialize(current_user, group, params = {}) + @current_user = current_user @group = group @params = params end def execute group.labels + .optionally_subscribed_by(subscriber_id) .optionally_search(params[:search]) .order_by(params[:sort]) .page(params[:page]) end + + private + + def subscriber_id + current_user&.id if subscribed? + end + + def subscribed? + params[:subscribed] == 'true' + end end diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 08fc2968e77..82e0b2ed9e1 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -17,6 +17,7 @@ class LabelsFinder < UnionFinder @skip_authorization = skip_authorization items = find_union(label_ids, Label) || Label.none items = with_title(items) + items = by_subscription(items) items = by_search(items) sort(items) end @@ -84,6 +85,18 @@ class LabelsFinder < UnionFinder labels.search(params[:search]) end + def by_subscription(labels) + labels.optionally_subscribed_by(subscriber_id) + end + + def subscriber_id + current_user&.id if subscribed? + end + + def subscribed? + params[:subscribed] == 'true' + end + # Gets redacted array of group ids # which can include the ancestors and descendants of the requested group. def group_ids_for(group) diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index e3b74f443f7..be1e7016a1e 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -59,8 +59,8 @@ module BoardsHelper { toggle: "dropdown", - list_labels_path: labels_filter_path(true, include_ancestor_groups: true), - labels: labels_filter_path(true, include_descendant_groups: include_descendant_groups), + list_labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_ancestor_groups: true), + labels: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: include_descendant_groups), labels_endpoint: @labels_endpoint, namespace_path: @namespace_path, project_path: @project&.path, diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 6c51739ba1a..76ed8efe2c6 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -131,20 +131,26 @@ module LabelsHelper end end - def labels_filter_path(only_group_labels = false, include_ancestor_groups: true, include_descendant_groups: false) - project = @target_project || @project - + def labels_filter_path_with_defaults(only_group_labels: false, include_ancestor_groups: true, include_descendant_groups: false) options = {} options[:include_ancestor_groups] = include_ancestor_groups if include_ancestor_groups options[:include_descendant_groups] = include_descendant_groups if include_descendant_groups + options[:only_group_labels] = only_group_labels if only_group_labels && @group + options[:format] = :json + + labels_filter_path(options) + end + + def labels_filter_path(options = {}) + project = @target_project || @project + format = options.delete(:format) || :html if project - project_labels_path(project, :json, options) + project_labels_path(project, format, options) elsif @group - options[:only_group_labels] = only_group_labels if only_group_labels - group_labels_path(@group, :json, options) + group_labels_path(@group, format, options) else - dashboard_labels_path(:json) + dashboard_labels_path(format, options) end end diff --git a/app/models/label.rb b/app/models/label.rb index 9ef57a05b3e..43b49445765 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -45,6 +45,7 @@ class Label < ActiveRecord::Base scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) } scope :order_name_asc, -> { reorder(title: :asc) } scope :order_name_desc, -> { reorder(title: :desc) } + scope :subscribed_by, ->(user_id) { joins(:subscriptions).where(subscriptions: { user_id: user_id, subscribed: true }) } def self.prioritized(project) joins(:priorities) @@ -74,6 +75,14 @@ class Label < ActiveRecord::Base joins(label_priorities) end + def self.optionally_subscribed_by(user_id) + if user_id + subscribed_by(user_id) + else + all + end + end + alias_attribute :name, :title def self.reference_prefix diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 003bd25dd06..5b78ce910b8 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -3,29 +3,23 @@ - can_admin_label = can?(current_user, :admin_label, @group) - issuables = ['issues', 'merge requests'] - search = params[:search] +- subscribed = params[:subscribed] +- labels_or_filters = @labels.exists? || search.present? || subscribed.present? - if can_admin_label - content_for(:header_content) do .nav-controls = link_to _('New label'), new_group_label_path(@group), class: "btn btn-success" -- if @labels.exists? || search.present? +- if labels_or_filters #promote-label-modal %div{ class: container_class } - .top-area.adjust - .nav-text - = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence } - .nav-controls - = form_tag group_labels_path(@group), method: :get do - .input-group - = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false } - %span.input-group-append - %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') } - = icon("search") - = render 'shared/labels/sort_dropdown' + = render 'shared/labels/nav' .labels-container.prepend-top-5 - if @labels.any? + .text-muted + = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence } .other-labels %h5= _('Labels') %ul.content-list.manage-labels-list.js-other-labels @@ -34,6 +28,9 @@ - elsif search.present? .nothing-here-block = _('No labels with such name or description') + - elsif subscribed.present? + .nothing-here-block + = _('You do not have any subscriptions yet') - else = render 'shared/empty_states/labels' diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 683dda4f166..11a05eada30 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -2,32 +2,25 @@ - page_title "Labels" - can_admin_label = can?(current_user, :admin_label, @project) - search = params[:search] +- subscribed = params[:subscribed] +- labels_or_filters = @labels.exists? || @prioritized_labels.exists? || search.present? || subscribed.present? - if can_admin_label - content_for(:header_content) do .nav-controls = link_to _('New label'), new_project_label_path(@project), class: "btn btn-success" -- if @labels.exists? || @prioritized_labels.exists? || search.present? +- if labels_or_filters #promote-label-modal %div{ class: container_class } - .top-area.adjust - .nav-text - = _('Labels can be applied to issues and merge requests.') - - .nav-controls - = form_tag project_labels_path(@project), method: :get do - .input-group - = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false } - %span.input-group-append - %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') } - = icon("search") - = render 'shared/labels/sort_dropdown' + = render 'shared/labels/nav' .labels-container.prepend-top-10 - if can_admin_label - if search.blank? %p.text-muted + = _('Labels can be applied to issues and merge requests.') + %br = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.') -# Only show it in the first page - hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1') @@ -59,7 +52,9 @@ - else .nothing-here-block = _('No labels with such name or description') - + - elsif subscribed.present? + .nothing-here-block + = _('You do not have any subscriptions yet') - else = render 'shared/empty_states/labels' diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index 28e6fe1b16d..0d2f6bb77d6 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -33,7 +33,7 @@ - if @project %board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project), "milestone-path" => milestones_filter_dropdown_path, - "label-path" => labels_filter_path, + "label-path" => labels_filter_path_with_defaults, "empty-state-svg" => image_path('illustrations/issues.svg'), ":issue-link-base" => "issueLinkBase", ":root-path" => "rootPath", diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml index 532045f3697..6138914206b 100644 --- a/app/views/shared/boards/components/sidebar/_labels.html.haml +++ b/app/views/shared/boards/components/sidebar/_labels.html.haml @@ -25,7 +25,7 @@ show_no: "true", show_any: "true", project_id: @project&.try(:id), - labels: labels_filter_path(false), + labels: labels_filter_path_with_defaults, namespace_path: @namespace_path, project_path: @project.try(:path) } } %span.dropdown-toggle-text diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml index 0b42b33581a..6eb1f8f0853 100644 --- a/app/views/shared/issuable/_label_dropdown.html.haml +++ b/app/views/shared/issuable/_label_dropdown.html.haml @@ -8,7 +8,7 @@ - classes = local_assigns.fetch(:classes, []) - selected = local_assigns.fetch(:selected, nil) - dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by label") -- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), labels: labels_filter_path, default_label: "Labels"} +- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), labels: labels_filter_path_with_defaults, default_label: "Labels"} - dropdown_data.merge!(data_options) - label_name = local_assigns.fetch(:label_name, "Labels") - no_default_styles = local_assigns.fetch(:no_default_styles, false) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 32b609eed0d..aa136af1955 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -109,7 +109,7 @@ - selected_labels.each do |label| = hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil .dropdown - %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path(false) if @project), display: 'static' } } + %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path_with_defaults if @project), display: 'static' } } %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) } = multi_label_name(selected_labels, "Labels") = icon('chevron-down', 'aria-hidden': 'true') diff --git a/app/views/shared/labels/_nav.html.haml b/app/views/shared/labels/_nav.html.haml new file mode 100644 index 00000000000..98572db738b --- /dev/null +++ b/app/views/shared/labels/_nav.html.haml @@ -0,0 +1,20 @@ +- subscribed = params[:subscribed] + +.top-area.adjust + %ul.nav-links.nav.nav-tabs + %li{ class: active_when(subscribed != 'true') }> + = link_to labels_filter_path do + = _('All') + - if current_user + %li{ class: active_when(subscribed == 'true') }> + = link_to labels_filter_path(subscribed: 'true') do + = _('Subscribed') + .nav-controls + = form_tag labels_filter_path, method: :get do + = hidden_field_tag :subscribed, params[:subscribed] + .input-group + = search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false } + %span.input-group-append + %button.btn.btn-default{ type: "submit", "aria-label" => _('Submit search') } + = icon("search") + = render 'shared/labels/sort_dropdown' diff --git a/app/views/shared/labels/_sort_dropdown.html.haml b/app/views/shared/labels/_sort_dropdown.html.haml index ff6e2947ffd..8a7d037e15b 100644 --- a/app/views/shared/labels/_sort_dropdown.html.haml +++ b/app/views/shared/labels/_sort_dropdown.html.haml @@ -6,4 +6,4 @@ %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort %li - label_sort_options_hash.each do |value, title| - = sortable_item(title, page_filter_path(sort: value, label: true), sort_title) + = sortable_item(title, page_filter_path(sort: value, label: true, subscribed: params[:subscribed]), sort_title) diff --git a/changelogs/unreleased/48222-fix-todos-status-button.yml b/changelogs/unreleased/48222-fix-todos-status-button.yml new file mode 100644 index 00000000000..2f7c79a07d0 --- /dev/null +++ b/changelogs/unreleased/48222-fix-todos-status-button.yml @@ -0,0 +1,6 @@ +--- +title: Fix the state of the Done button when there is an error in the GitLab Todos + section +merge_request: +author: marcos8896 +type: fixed diff --git a/changelogs/unreleased/50552-unable-to-close-performance-bar.yml b/changelogs/unreleased/50552-unable-to-close-performance-bar.yml new file mode 100644 index 00000000000..e3619149d2a --- /dev/null +++ b/changelogs/unreleased/50552-unable-to-close-performance-bar.yml @@ -0,0 +1,5 @@ +--- +title: Fix performance bar modal position +merge_request: 21577 +author: +type: fixed diff --git a/changelogs/unreleased/52178-markdown-table-borders.yml b/changelogs/unreleased/52178-markdown-table-borders.yml new file mode 100644 index 00000000000..965f21f2a97 --- /dev/null +++ b/changelogs/unreleased/52178-markdown-table-borders.yml @@ -0,0 +1,5 @@ +--- +title: Add borders and white background to markdown tables +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/dz-labels-subscribe-filter.yml b/changelogs/unreleased/dz-labels-subscribe-filter.yml new file mode 100644 index 00000000000..768c20c77c7 --- /dev/null +++ b/changelogs/unreleased/dz-labels-subscribe-filter.yml @@ -0,0 +1,5 @@ +--- +title: Add subscribe filter to group and project labels pages +merge_request: 21965 +author: +type: added diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index b37e7698ab4..862ee398a84 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -54,35 +54,38 @@ Parameters: { "id": 1, "iid": 1, - "target_branch": "master", - "source_branch": "test1", "project_id": 3, "title": "test1", + "description": "fixed login page css paddings", "state": "opened", "created_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "source_project_id": 2, "target_project_id": 3, - "labels": [ ], - "description": "fixed login page css paddings", + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, @@ -93,23 +96,28 @@ Parameters: "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "merge_status": "can_be_merged", "sha": "8888888888888888888888888888888888888888", "merge_commit_sha": null, "user_notes_count": 1, + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "squash": false, - "web_url": "http://example.com/example/example/merge_requests/1", + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "squash": false } ] ``` @@ -169,35 +177,38 @@ Parameters: { "id": 1, "iid": 1, - "target_branch": "master", - "source_branch": "test1", "project_id": 3, "title": "test1", + "description": "fixed login page css paddings", "state": "opened", "created_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "source_project_id": 2, "target_project_id": 3, - "labels": [ ], - "description": "fixed login page css paddings", + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, @@ -208,24 +219,28 @@ Parameters: "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "merge_status": "can_be_merged", "sha": "8888888888888888888888888888888888888888", "merge_commit_sha": null, "user_notes_count": 1, + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "squash": false, - "web_url": "http://example.com/example/example/merge_requests/1", - "discussion_locked": false, + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "squash": false } ] ``` @@ -275,35 +290,38 @@ Parameters: { "id": 1, "iid": 1, - "target_branch": "master", - "source_branch": "test1", "project_id": 3, "title": "test1", + "description": "fixed login page css paddings", "state": "opened", "created_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "source_project_id": 2, "target_project_id": 3, - "labels": [ ], - "description": "fixed login page css paddings", + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, @@ -314,23 +332,26 @@ Parameters: "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-10-22", + "start_date": "2018-09-08", + "web_url": "gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "merge_status": "can_be_merged", "sha": "8888888888888888888888888888888888888888", "merge_commit_sha": null, "user_notes_count": 1, + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "web_url": "http://example.com/example/example/merge_requests/1", - "discussion_locked": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "squash": false } ] ``` @@ -359,35 +380,38 @@ Parameters: { "id": 1, "iid": 1, - "target_branch": "master", - "source_branch": "test1", "project_id": 3, "title": "test1", - "state": "merged", + "description": "fixed login page css paddings", + "state": "opened", "created_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { - "state" : "active", - "web_url" : "https://gitlab.example.com/root", - "avatar_url" : null, - "username" : "root", - "id" : 1, - "name" : "Administrator" + "id": 1, + "name": "Administrator", + "username": "admin", + "state": "active", + "avatar_url": null, + "web_url" : "https://gitlab.example.com/admin" }, "assignee": { - "state" : "active", - "web_url" : "https://gitlab.example.com/root", - "avatar_url" : null, - "username" : "root", - "id" : 1, - "name" : "Administrator" + "id": 1, + "name": "Administrator", + "username": "admin", + "state": "active", + "avatar_url": null, + "web_url" : "https://gitlab.example.com/admin" }, "source_project_id": 2, "target_project_id": 3, - "labels": [ ], - "description": "fixed login page css paddings", + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, @@ -398,50 +422,55 @@ Parameters: "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true, "sha": "8888888888888888888888888888888888888888", - "merge_commit_sha": "9999999999999999999999999999999999999999", + "merge_commit_sha": null, "user_notes_count": 1, - "changes_count": "1", + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "squash": false, - "web_url": "http://example.com/example/example/merge_requests/1", - "discussion_locked": false, + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null }, - "closed_at": "2018-01-19T14:36:11.086Z", - "latest_build_started_at": null, - "latest_build_finished_at": null, + "squash": false, + "subscribed": false, + "changes_count": "1", + "merged_by": { + "id": 87854, + "name": "Douwe Maan", + "username": "DouweM", + "state": "active", + "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", + "web_url": "https://gitlab.com/DouweM" + }, + "merged_at": "2018-09-07T11:16:17.520Z", + "closed_by": null, + "closed_at": null, + "latest_build_started_at": "2018-09-07T07:27:38.472Z", + "latest_build_finished_at": "2018-09-07T08:07:06.012Z", "first_deployed_to_production_at": null, "pipeline": { - "id": 8, - "ref": "master", - "sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", - "status": "created" - }, - "merged_by": null, - "merged_at": null, - "closed_by": { - "state" : "active", - "web_url" : "https://gitlab.example.com/root", - "avatar_url" : null, - "username" : "root", - "id" : 1, - "name" : "Administrator" + "id": 29626725, + "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "ref": "patch-28", + "status": "success", + "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725" }, "diff_refs": { - "base_sha": "1111111111111111111111111111111111111111", - "head_sha": "2222222222222222222222222222222222222222", - "start_sha": "3333333333333333333333333333333333333333" + "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00", + "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, "diverged_commits_count": 2 } @@ -663,65 +692,99 @@ POST /projects/:id/merge_requests { "id": 1, "iid": 1, - "target_branch": "master", - "source_branch": "test1", - "project_id": 4, + "project_id": 3, "title": "test1", + "description": "fixed login page css paddings", "state": "opened", + "created_at": "2017-04-29T08:46:00Z", + "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, - "source_project_id": 3, - "target_project_id": 4, - "labels": [ ], - "description": "fixed login page css paddings", + "source_project_id": 2, + "target_project_id": 3, + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, "iid": 1, - "project_id": 4, + "project_id": 3, "title": "v2.0", "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true, "sha": "8888888888888888888888888888888888888888", "merge_commit_sha": null, - "user_notes_count": 0, - "changes_count": "1", + "user_notes_count": 1, + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "squash": false, - "web_url": "http://example.com/example/example/merge_requests/1", - "discussion_locked": false, "allow_collaboration": false, "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "squash": false, + "subscribed": false, + "changes_count": "1", + "merged_by": { + "id": 87854, + "name": "Douwe Maan", + "username": "DouweM", + "state": "active", + "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", + "web_url": "https://gitlab.com/DouweM" + }, + "merged_at": "2018-09-07T11:16:17.520Z", + "closed_by": null, + "closed_at": null, + "latest_build_started_at": "2018-09-07T07:27:38.472Z", + "latest_build_finished_at": "2018-09-07T08:07:06.012Z", + "first_deployed_to_production_at": null, + "pipeline": { + "id": 29626725, + "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "ref": "patch-28", + "status": "success", + "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725" + }, + "diff_refs": { + "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00", + "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" + }, + "diverged_commits_count": 2 } ``` @@ -756,64 +819,99 @@ Must include at least one non-required attribute from above. { "id": 1, "iid": 1, - "target_branch": "master", - "project_id": 4, + "project_id": 3, "title": "test1", + "description": "fixed login page css paddings", "state": "opened", + "created_at": "2017-04-29T08:46:00Z", + "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, - "source_project_id": 3, - "target_project_id": 4, - "labels": [ ], - "description": "description1", + "source_project_id": 2, + "target_project_id": 3, + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, "iid": 1, - "project_id": 4, + "project_id": 3, "title": "v2.0", "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true, "sha": "8888888888888888888888888888888888888888", "merge_commit_sha": null, "user_notes_count": 1, - "changes_count": "1", + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "squash": false, - "web_url": "http://example.com/example/example/merge_requests/1", - "discussion_locked": false, "allow_collaboration": false, "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "squash": false, + "subscribed": false, + "changes_count": "1", + "merged_by": { + "id": 87854, + "name": "Douwe Maan", + "username": "DouweM", + "state": "active", + "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", + "web_url": "https://gitlab.com/DouweM" + }, + "merged_at": "2018-09-07T11:16:17.520Z", + "closed_by": null, + "closed_at": null, + "latest_build_started_at": "2018-09-07T07:27:38.472Z", + "latest_build_finished_at": "2018-09-07T08:07:06.012Z", + "first_deployed_to_production_at": null, + "pipeline": { + "id": 29626725, + "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "ref": "patch-28", + "status": "success", + "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725" + }, + "diff_refs": { + "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00", + "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" + }, + "diverged_commits_count": 2 } ``` @@ -857,70 +955,106 @@ Parameters: - `merge_request_iid` (required) - Internal ID of MR - `merge_commit_message` (optional) - Custom merge commit message - `should_remove_source_branch` (optional) - if `true` removes the source branch -- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds +- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds - `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail ```json { "id": 1, "iid": 1, - "target_branch": "master", - "source_branch": "test1", "project_id": 3, "title": "test1", - "state": "merged", + "description": "fixed login page css paddings", + "state": "opened", + "created_at": "2017-04-29T08:46:00Z", + "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, - "source_project_id": 4, - "target_project_id": 4, - "labels": [ ], - "description": "fixed login page css paddings", + "source_project_id": 2, + "target_project_id": 3, + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, "iid": 1, - "project_id": 4, + "project_id": 3, "title": "v2.0", "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, "merge_when_pipeline_succeeds": true, "merge_status": "can_be_merged", - "subscribed" : true, "sha": "8888888888888888888888888888888888888888", - "merge_commit_sha": "9999999999999999999999999999999999999999", + "merge_commit_sha": null, "user_notes_count": 1, - "changes_count": "1", + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "squash": false, - "web_url": "http://example.com/example/example/merge_requests/1", - "discussion_locked": false, + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "squash": false, + "subscribed": false, + "changes_count": "1", + "merged_by": { + "id": 87854, + "name": "Douwe Maan", + "username": "DouweM", + "state": "active", + "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", + "web_url": "https://gitlab.com/DouweM" + }, + "merged_at": "2018-09-07T11:16:17.520Z", + "closed_by": null, + "closed_at": null, + "latest_build_started_at": "2018-09-07T07:27:38.472Z", + "latest_build_finished_at": "2018-09-07T08:07:06.012Z", + "first_deployed_to_production_at": null, + "pipeline": { + "id": 29626725, + "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "ref": "patch-28", + "status": "success", + "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725" + }, + "diff_refs": { + "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00", + "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" + }, + "diverged_commits_count": 2 } ``` @@ -937,69 +1071,105 @@ PUT /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_s Parameters: - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user -- `merge_request_iid` (required) - Internal ID of MR +- `merge_request_iid` (required) - Internal ID of MR ```json { "id": 1, "iid": 1, - "target_branch": "master", - "source_branch": "test1", "project_id": 3, "title": "test1", + "description": "fixed login page css paddings", "state": "opened", + "created_at": "2017-04-29T08:46:00Z", + "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, "assignee": { "id": 1, - "username": "admin", "name": "Administrator", + "username": "admin", "state": "active", "avatar_url": null, "web_url" : "https://gitlab.example.com/admin" }, - "source_project_id": 4, - "target_project_id": 4, - "labels": [ ], - "description": "fixed login page css paddings", + "source_project_id": 2, + "target_project_id": 3, + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { "id": 5, "iid": 1, - "project_id": 4, + "project_id": 3, "title": "v2.0", "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", "state": "closed", "created_at": "2015-02-02T19:49:26.013Z", "updated_at": "2015-02-02T19:49:26.013Z", - "due_date": null + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, - "merge_when_pipeline_succeeds": true, + "merge_when_pipeline_succeeds": false, "merge_status": "can_be_merged", - "subscribed" : true, "sha": "8888888888888888888888888888888888888888", "merge_commit_sha": null, "user_notes_count": 1, - "changes_count": "1", + "discussion_locked": null, "should_remove_source_branch": true, "force_remove_source_branch": false, - "squash": false, - "web_url": "http://example.com/example/example/merge_requests/1", - "discussion_locked": false, + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "time_stats": { "time_estimate": 0, "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "squash": false, + "subscribed": false, + "changes_count": "1", + "merged_by": { + "id": 87854, + "name": "Douwe Maan", + "username": "DouweM", + "state": "active", + "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", + "web_url": "https://gitlab.com/DouweM" + }, + "merged_at": "2018-09-07T11:16:17.520Z", + "closed_by": null, + "closed_at": null, + "latest_build_started_at": "2018-09-07T07:27:38.472Z", + "latest_build_finished_at": "2018-09-07T08:07:06.012Z", + "first_deployed_to_production_at": null, + "pipeline": { + "id": 29626725, + "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "ref": "patch-28", + "status": "success", + "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725" + }, + "diff_refs": { + "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00", + "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" + }, + "diverged_commits_count": 2 } ``` @@ -1067,7 +1237,7 @@ Example response when the GitLab issue tracker is used: "labels" : [], "user_notes_count": 1, "changes_count": "1" - }, + } ] ``` @@ -1104,54 +1274,101 @@ Example response: ```json { - "id": 17, + "id": 1, "iid": 1, - "project_id": 5, - "title": "Et et sequi est impedit nulla ut rem et voluptatem.", - "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.", + "project_id": 3, + "title": "test1", + "description": "fixed login page css paddings", "state": "opened", - "created_at": "2016-04-05T21:42:23.233Z", - "updated_at": "2016-04-05T22:11:52.900Z", - "target_branch": "ui-dev-kit", - "source_branch": "version-1-9", + "created_at": "2017-04-29T08:46:00Z", + "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { - "name": "Eileen Skiles", - "username": "leila", - "id": 19, + "id": 1, + "name": "Administrator", + "username": "admin", "state": "active", - "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon", - "web_url": "https://gitlab.example.com/leila" + "avatar_url": null, + "web_url" : "https://gitlab.example.com/admin" }, "assignee": { - "name": "Celine Wehner", - "username": "carli", - "id": 16, + "id": 1, + "name": "Administrator", + "username": "admin", "state": "active", - "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon", - "web_url": "https://gitlab.example.com/carli" + "avatar_url": null, + "web_url" : "https://gitlab.example.com/admin" }, - "source_project_id": 5, - "target_project_id": 5, - "labels": [], + "source_project_id": 2, + "target_project_id": 3, + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { - "id": 7, + "id": 5, "iid": 1, - "project_id": 5, + "project_id": 3, "title": "v2.0", - "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.", + "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", "state": "closed", - "created_at": "2016-04-05T21:41:40.905Z", - "updated_at": "2016-04-05T21:41:40.905Z", - "due_date": null + "created_at": "2015-02-02T19:49:26.013Z", + "updated_at": "2015-02-02T19:49:26.013Z", + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, - "merge_when_pipeline_succeeds": false, - "merge_status": "cannot_be_merged", - "subscribed": true, + "merge_when_pipeline_succeeds": true, + "merge_status": "can_be_merged", "sha": "8888888888888888888888888888888888888888", - "merge_commit_sha": null + "merge_commit_sha": null, + "user_notes_count": 1, + "discussion_locked": null, + "should_remove_source_branch": true, + "force_remove_source_branch": false, + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "squash": false, + "subscribed": false, + "changes_count": "1", + "merged_by": { + "id": 87854, + "name": "Douwe Maan", + "username": "DouweM", + "state": "active", + "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", + "web_url": "https://gitlab.com/DouweM" + }, + "merged_at": "2018-09-07T11:16:17.520Z", + "closed_by": null, + "closed_at": null, + "latest_build_started_at": "2018-09-07T07:27:38.472Z", + "latest_build_finished_at": "2018-09-07T08:07:06.012Z", + "first_deployed_to_production_at": null, + "pipeline": { + "id": 29626725, + "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "ref": "patch-28", + "status": "success", + "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725" + }, + "diff_refs": { + "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00", + "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" + }, + "diverged_commits_count": 2 } ``` @@ -1178,54 +1395,101 @@ Example response: ```json { - "id": 17, + "id": 1, "iid": 1, - "project_id": 5, - "title": "Et et sequi est impedit nulla ut rem et voluptatem.", - "description": "Consequatur velit eos rerum optio autem. Quia id officia quaerat dolorum optio. Illo laudantium aut ipsum dolorem.", + "project_id": 3, + "title": "test1", + "description": "fixed login page css paddings", "state": "opened", - "created_at": "2016-04-05T21:42:23.233Z", - "updated_at": "2016-04-05T22:11:52.900Z", - "target_branch": "ui-dev-kit", - "source_branch": "version-1-9", + "created_at": "2017-04-29T08:46:00Z", + "updated_at": "2017-04-29T08:46:00Z", + "target_branch": "master", + "source_branch": "test1", "upvotes": 0, "downvotes": 0, "author": { - "name": "Eileen Skiles", - "username": "leila", - "id": 19, + "id": 1, + "name": "Administrator", + "username": "admin", "state": "active", - "avatar_url": "http://www.gravatar.com/avatar/39ce4a2822cc896933ffbd68c1470e55?s=80&d=identicon", - "web_url": "https://gitlab.example.com/leila" + "avatar_url": null, + "web_url" : "https://gitlab.example.com/admin" }, "assignee": { - "name": "Celine Wehner", - "username": "carli", - "id": 16, + "id": 1, + "name": "Administrator", + "username": "admin", "state": "active", - "avatar_url": "http://www.gravatar.com/avatar/f4cd5605b769dd2ce405a27c6e6f2684?s=80&d=identicon", - "web_url": "https://gitlab.example.com/carli" + "avatar_url": null, + "web_url" : "https://gitlab.example.com/admin" }, - "source_project_id": 5, - "target_project_id": 5, - "labels": [], + "source_project_id": 2, + "target_project_id": 3, + "labels": [ + "Community contribution", + "Manage" + ], "work_in_progress": false, "milestone": { - "id": 7, + "id": 5, "iid": 1, - "project_id": 5, + "project_id": 3, "title": "v2.0", - "description": "Corrupti eveniet et velit occaecati dolorem est rerum aut.", + "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.", "state": "closed", - "created_at": "2016-04-05T21:41:40.905Z", - "updated_at": "2016-04-05T21:41:40.905Z", - "due_date": null + "created_at": "2015-02-02T19:49:26.013Z", + "updated_at": "2015-02-02T19:49:26.013Z", + "due_date": "2018-09-22", + "start_date": "2018-08-08", + "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1" }, - "merge_when_pipeline_succeeds": false, - "merge_status": "cannot_be_merged", - "subscribed": false, + "merge_when_pipeline_succeeds": true, + "merge_status": "can_be_merged", "sha": "8888888888888888888888888888888888888888", - "merge_commit_sha": null + "merge_commit_sha": null, + "user_notes_count": 1, + "discussion_locked": null, + "should_remove_source_branch": true, + "force_remove_source_branch": false, + "allow_collaboration": false, + "allow_maintainer_to_push": false, + "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "squash": false, + "subscribed": false, + "changes_count": "1", + "merged_by": { + "id": 87854, + "name": "Douwe Maan", + "username": "DouweM", + "state": "active", + "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png", + "web_url": "https://gitlab.com/DouweM" + }, + "merged_at": "2018-09-07T11:16:17.520Z", + "closed_by": null, + "closed_at": null, + "latest_build_started_at": "2018-09-07T07:27:38.472Z", + "latest_build_finished_at": "2018-09-07T08:07:06.012Z", + "first_deployed_to_production_at": null, + "pipeline": { + "id": 29626725, + "sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "ref": "patch-28", + "status": "success", + "web_url": "https://gitlab.example.com/my-group/my-project/pipelines/29626725" + }, + "diff_refs": { + "base_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00", + "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", + "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" + }, + "diverged_commits_count": 2 } ``` diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 92a88c1c794..b56cd5700aa 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5786,6 +5786,9 @@ msgstr "" msgid "Subscribe at project level" msgstr "" +msgid "Subscribed" +msgstr "" + msgid "Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})" msgstr "" @@ -6956,6 +6959,9 @@ msgstr "" msgid "You cannot write to this read-only GitLab instance." msgstr "" +msgid "You do not have any subscriptions yet" +msgstr "" + msgid "You don't have any applications" msgstr "" diff --git a/spec/features/groups/labels/subscription_spec.rb b/spec/features/groups/labels/subscription_spec.rb index d9543bfa97f..22b51b297a6 100644 --- a/spec/features/groups/labels/subscription_spec.rb +++ b/spec/features/groups/labels/subscription_spec.rb @@ -3,7 +3,8 @@ require 'spec_helper' describe 'Labels subscription' do let(:user) { create(:user) } let(:group) { create(:group) } - let!(:feature) { create(:group_label, group: group, title: 'feature') } + let!(:label1) { create(:group_label, group: group, title: 'foo') } + let!(:label2) { create(:group_label, group: group, title: 'bar') } context 'when signed in' do before do @@ -14,9 +15,9 @@ describe 'Labels subscription' do it 'users can subscribe/unsubscribe to group labels', :js do visit group_labels_path(group) - expect(page).to have_content('feature') + expect(page).to have_content(label1.title) - within "#group_label_#{feature.id}" do + within "#group_label_#{label1.id}" do expect(page).not_to have_button 'Unsubscribe' click_button 'Subscribe' @@ -30,15 +31,48 @@ describe 'Labels subscription' do expect(page).not_to have_button 'Unsubscribe' end end + + context 'subscription filter' do + before do + visit group_labels_path(group) + end + + it 'shows only subscribed labels' do + label1.subscribe(user) + + click_subscribed_tab + + page.within('.labels-container') do + expect(page).to have_content label1.title + end + end + + it 'shows no subscribed labels message' do + click_subscribed_tab + + page.within('.labels-container') do + expect(page).not_to have_content label1.title + expect(page).to have_content('You do not have any subscriptions yet') + end + end + end end context 'when not signed in' do - it 'users can not subscribe/unsubscribe to labels' do + before do visit group_labels_path(group) + end - expect(page).to have_content 'feature' + it 'users can not subscribe/unsubscribe to labels' do + expect(page).to have_content label1.title expect(page).not_to have_button('Subscribe') end + + it 'does not show subscribed tab' do + page.within('.nav-tabs') do + expect(page).not_to have_link 'Subscribed' + end + end end def click_link_on_dropdown(text) @@ -48,4 +82,10 @@ describe 'Labels subscription' do find('a.js-subscribe-button', text: text).click end end + + def click_subscribed_tab + page.within('.nav-tabs') do + click_link 'Subscribed' + end + end end diff --git a/spec/finders/group_labels_finder_spec.rb b/spec/finders/group_labels_finder_spec.rb index ef68fc105e4..7bdd312eff0 100644 --- a/spec/finders/group_labels_finder_spec.rb +++ b/spec/finders/group_labels_finder_spec.rb @@ -4,29 +4,38 @@ require 'spec_helper' describe GroupLabelsFinder, '#execute' do let!(:group) { create(:group) } + let!(:user) { create(:user) } let!(:label1) { create(:group_label, title: 'Foo', description: 'Lorem ipsum', group: group) } let!(:label2) { create(:group_label, title: 'Bar', description: 'Fusce consequat', group: group) } it 'returns all group labels sorted by name if no params' do - result = described_class.new(group).execute + result = described_class.new(user, group).execute expect(result.to_a).to match_array([label2, label1]) end it 'returns all group labels sorted by name desc' do - result = described_class.new(group, sort: 'name_desc').execute + result = described_class.new(user, group, sort: 'name_desc').execute expect(result.to_a).to match_array([label2, label1]) end - it 'returns group labels that march search' do - result = described_class.new(group, search: 'Foo').execute + it 'returns group labels that match search' do + result = described_class.new(user, group, search: 'Foo').execute expect(result.to_a).to match_array([label1]) end + it 'returns group labels user subscribed to' do + label2.subscribe(user) + + result = described_class.new(user, group, subscribed: 'true').execute + + expect(result.to_a).to match_array([label2]) + end + it 'returns second page of labels' do - result = described_class.new(group, page: '2').execute + result = described_class.new(user, group, page: '2').execute expect(result.to_a).to match_array([]) end diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index f5cec8e349a..9abc52aa664 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -210,5 +210,15 @@ describe LabelsFinder do expect(finder.execute).to eq [project_label_1] end end + + context 'filter by subscription' do + it 'returns labels user subscribed to' do + project_label_1.subscribe(user) + + finder = described_class.new(user, subscribed: 'true') + + expect(finder.execute).to eq [project_label_1] + end + end end end diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 99670af786a..3fc6c06b7fa 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -155,4 +155,40 @@ describe Label do expect(described_class.search('feature')).to be_empty end end + + describe '.subscribed_by' do + let!(:user) { create(:user) } + let!(:label) { create(:label) } + let!(:label2) { create(:label) } + + before do + label.subscribe(user) + end + + it 'returns subscribed labels' do + expect(described_class.subscribed_by(user.id)).to eq([label]) + end + + it 'returns nothing' do + expect(described_class.subscribed_by(0)).to be_empty + end + end + + describe '.optionally_subscribed_by' do + let!(:user) { create(:user) } + let!(:label) { create(:label) } + let!(:label2) { create(:label) } + + before do + label.subscribe(user) + end + + it 'returns subscribed labels' do + expect(described_class.optionally_subscribed_by(user.id)).to eq([label]) + end + + it 'returns all labels if user_id is nil' do + expect(described_class.optionally_subscribed_by(nil)).to match_array([label, label2]) + end + end end |