From 4aaf053449bfb18e4d435f41cf4a5df30cd93e36 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Fri, 15 Mar 2019 17:12:14 -0300 Subject: [PATCH 1/2] Support multi-assignees on merge requests This mainly handles the search and replace approach to using assignees instead assignee for merge requests. It reuses existing logic from Issuable, where now issues _and_ merge requests handles multiple assignees. The feature is behind a feature flag, though, there's no way to switch back and forth to using the old and new tables. That's because we'd need a "sync" approach: Once the feature is off, we'd have to sync old table with new table, once it's on, new table with old table. Using the new `merge_request_assignees` altogether will make things simpler to handle, even though it's a bit "riskier". --- .../issuable_bulk_update_actions.js | 3 - .../components/assignees/assignees.vue | 33 +++++- .../stylesheets/pages/merge_requests.scss | 10 ++ app/assets/stylesheets/pages/projects.scss | 2 + app/controllers/concerns/issuable_actions.rb | 7 +- .../concerns/issuable_collections.rb | 10 +- .../merge_requests/application_controller.rb | 2 +- app/finders/issuable_finder.rb | 30 +++-- app/finders/issues_finder.rb | 14 --- app/helpers/boards_helper.rb | 2 +- app/helpers/form_helper.rb | 39 ++++++- app/helpers/issuables_helper.rb | 11 +- app/mailers/emails/merge_requests.rb | 6 +- app/mailers/notify.rb | 2 + app/models/concerns/issuable.rb | 41 ++++--- app/models/issue.rb | 22 ---- app/models/merge_request.rb | 62 ++++------ app/models/project.rb | 4 + .../issuable_sidebar_extras_entity.rb | 2 + .../issue_sidebar_extras_entity.rb | 1 - .../merge_request_assignee_entity.rb | 7 ++ app/serializers/merge_request_basic_entity.rb | 3 +- app/serializers/merge_request_serializer.rb | 4 +- .../merge_request_sidebar_basic_entity.rb | 11 -- .../merge_request_sidebar_extras_entity.rb | 7 ++ app/services/issuable_base_service.rb | 18 ++- app/services/issues/base_service.rb | 22 +--- app/services/issues/update_service.rb | 2 +- app/services/merge_requests/base_service.rb | 6 +- app/services/merge_requests/update_service.rb | 18 +-- .../notification_recipient_service.rb | 24 ++-- app/services/notification_service.rb | 20 ++-- app/services/system_note_service.rb | 10 +- app/services/todo_service.rb | 16 +-- .../_reassigned_issuable_email.html.haml | 10 ++ .../closed_merge_request_email.text.haml | 2 +- app/views/notify/issue_due_email.html.haml | 2 +- app/views/notify/issue_due_email.text.erb | 2 +- .../merge_request_status_email.text.haml | 2 +- .../merge_request_unmergeable_email.text.haml | 2 +- .../merged_merge_request_email.text.haml | 2 +- app/views/notify/new_issue_email.html.haml | 2 +- app/views/notify/new_issue_email.text.erb | 2 +- .../new_mention_in_issue_email.text.erb | 2 +- ...ew_mention_in_merge_request_email.text.erb | 2 +- .../notify/new_merge_request_email.html.haml | 4 +- .../notify/new_merge_request_email.text.erb | 2 +- .../notify/reassigned_issue_email.html.haml | 11 +- .../reassigned_merge_request_email.html.haml | 11 +- .../reassigned_merge_request_email.text.erb | 4 +- app/views/projects/issues/_issue.html.haml | 2 +- .../merge_requests/_merge_request.html.haml | 4 +- .../components/sidebar/_assignee.html.haml | 2 +- .../shared/issuable/_assignees.html.haml | 6 +- .../issuable/_bulk_update_sidebar.html.haml | 5 +- .../issuable/_sidebar_assignees.html.haml | 56 ++------- .../form/_merge_request_assignee.html.haml | 31 ----- .../shared/issuable/form/_metadata.html.haml | 7 +- ... => _metadata_issuable_assignee.html.haml} | 6 +- db/fixtures/development/10_merge_requests.rb | 2 +- ee/app/helpers/ee/form_helper.rb | 14 +-- ee/app/models/ee/merge_request.rb | 5 + ee/app/models/license.rb | 1 + ...add_merge_request_approver_email.html.haml | 8 +- .../add_merge_request_approver_email.text.erb | 2 +- .../approved_merge_request_email.html.haml | 34 ++++-- .../approved_merge_request_email.text.haml | 2 +- .../unapproved_merge_request_email.html.haml | 34 ++++-- .../unapproved_merge_request_email.text.haml | 2 +- .../osw-multi-assignees-merge-requests-ee.yml | 5 + ee/lib/api/github/entities.rb | 4 +- .../merge_requests/drafts_controller_spec.rb | 10 +- ee/spec/features/issues/form_spec.rb | 4 +- ...user_creates_multiple_assignees_mr_spec.rb | 13 +++ .../user_edits_multiple_assignees_mr_spec.rb | 13 +++ ee/spec/mailers/notify_spec.rb | 36 ++++-- ee/spec/models/merge_request_spec.rb | 27 +++++ .../api/merge_request_approvals_spec.rb | 5 +- ee/spec/requests/api/merge_requests_spec.rb | 3 +- ee/spec/requests/api/v3/github_spec.rb | 5 +- .../draft_notes/publish_service_spec.rb | 11 +- .../services/ee/notification_service_spec.rb | 20 +++- .../quick_actions/interpret_service_spec.rb | 93 ++++++++++++++- lib/api/entities.rb | 6 +- .../reference_parser/merge_request_parser.rb | 2 +- lib/gitlab/hook_data/issuable_builder.rb | 6 +- lib/gitlab/hook_data/merge_request_builder.rb | 5 +- lib/gitlab/import_export/import_export.yml | 1 + locale/gitlab.pot | 15 --- qa/qa/page/merge_request/new.rb | 2 +- .../merge_requests_controller_spec.rb | 6 +- .../dashboard/issuables_counter_spec.rb | 4 +- .../features/dashboard/merge_requests_spec.rb | 4 +- spec/features/groups/merge_requests_spec.rb | 2 +- spec/features/issues/form_spec.rb | 4 +- .../user_creates_merge_request_spec.rb | 4 +- .../merge_request/user_creates_mr_spec.rb | 15 ++- .../merge_request/user_edits_mr_spec.rb | 18 ++- .../user_filters_by_assignees_spec.rb | 2 +- .../user_filters_by_multiple_criteria_spec.rb | 2 +- .../user_lists_merge_requests_spec.rb | 4 +- .../merge_requests/user_mass_updates_spec.rb | 3 +- .../user_uses_header_search_field_spec.rb | 4 +- spec/finders/issues_finder_spec.rb | 64 +++------- spec/finders/merge_requests_finder_spec.rb | 49 ++++++-- .../schemas/entities/merge_request_basic.json | 12 +- .../schemas/public_api/v4/merge_request.json | 5 + spec/javascripts/sidebar/assignees_spec.js | 85 ++++++++++++++ .../gitlab/hook_data/issuable_builder_spec.rb | 6 +- .../hook_data/merge_request_builder_spec.rb | 1 + spec/lib/gitlab/import_export/all_models.yml | 4 + .../import_export/safe_model_attributes.yml | 4 + spec/lib/gitlab/issuable_metadata_spec.rb | 4 +- spec/mailers/notify_spec.rb | 18 +-- spec/models/ci/pipeline_spec.rb | 8 +- spec/models/concerns/issuable_spec.rb | 7 +- spec/models/event_spec.rb | 2 +- spec/models/merge_request_spec.rb | 106 +++++++++-------- spec/models/user_spec.rb | 6 +- spec/requests/api/events_spec.rb | 4 +- spec/requests/api/merge_requests_spec.rb | 40 +++---- .../issuable/bulk_update_service_spec.rb | 16 +-- .../services/issuable/destroy_service_spec.rb | 2 +- spec/services/members/destroy_service_spec.rb | 2 +- .../merge_requests/close_service_spec.rb | 2 +- .../create_from_issue_service_spec.rb | 2 +- .../merge_requests/create_service_spec.rb | 30 ++--- .../merge_requests/ff_merge_service_spec.rb | 2 +- .../merge_requests/merge_service_spec.rb | 4 +- .../merge_to_ref_service_spec.rb | 2 +- .../merge_requests/post_merge_service_spec.rb | 2 +- .../merge_requests/reopen_service_spec.rb | 2 +- .../merge_requests/update_service_spec.rb | 42 +++---- spec/services/notification_service_spec.rb | 55 ++++----- .../quick_actions/interpret_service_spec.rb | 4 +- spec/services/system_note_service_spec.rb | 6 +- spec/services/todo_service_spec.rb | 110 +++++++++--------- spec/services/users/destroy_service_spec.rb | 4 +- .../merge_requests_finder_shared_contexts.rb | 6 +- .../shared_contexts/merge_request_create.rb | 26 +++++ .../shared_contexts/merge_request_edit.rb | 28 +++++ ...creatable_merge_request_shared_examples.rb | 31 +---- .../editable_merge_request_shared_examples.rb | 28 +---- .../multiple_assignees_mr_shared_examples.rb | 47 ++++++++ .../finders/assignees_filter_spec.rb | 49 ++++++++ .../merge_requests/edit.html.haml_spec.rb | 6 +- .../merge_requests/show.html.haml_spec.rb | 13 --- 147 files changed, 1191 insertions(+), 869 deletions(-) create mode 100644 app/serializers/merge_request_assignee_entity.rb delete mode 100644 app/serializers/merge_request_sidebar_basic_entity.rb create mode 100644 app/serializers/merge_request_sidebar_extras_entity.rb create mode 100644 app/views/notify/_reassigned_issuable_email.html.haml delete mode 100644 app/views/shared/issuable/form/_merge_request_assignee.html.haml rename app/views/shared/issuable/form/{_metadata_issue_assignee.html.haml => _metadata_issuable_assignee.html.haml} (64%) create mode 100644 ee/changelogs/unreleased/osw-multi-assignees-merge-requests-ee.yml create mode 100644 ee/spec/features/merge_request/user_creates_multiple_assignees_mr_spec.rb create mode 100644 ee/spec/features/merge_request/user_edits_multiple_assignees_mr_spec.rb create mode 100644 spec/support/shared_contexts/merge_request_create.rb create mode 100644 spec/support/shared_contexts/merge_request_edit.rb create mode 100644 spec/support/shared_examples/features/multiple_assignees_mr_shared_examples.rb create mode 100644 spec/support/shared_examples/finders/assignees_filter_spec.rb diff --git a/app/assets/javascripts/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable_bulk_update_actions.js index b844e4c5e5bc25..ccbe591a63ec70 100644 --- a/app/assets/javascripts/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable_bulk_update_actions.js @@ -81,9 +81,6 @@ export default { const formData = { update: { state_event: this.form.find('input[name="update[state_event]"]').val(), - // For Merge Requests - assignee_id: this.form.find('input[name="update[assignee_id]"]').val(), - // For Issues assignee_ids: [this.form.find('input[name="update[assignee_ids][]"]').val()], milestone_id: this.form.find('input[name="update[milestone_id]"]').val(), issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(), diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue index d1a396182b38b2..ce378e24289727 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue @@ -74,8 +74,7 @@ export default { } if (!this.users.length) { - const emptyTooltipLabel = - this.issuableType === 'issue' ? __('Assignee(s)') : __('Assignee'); + const emptyTooltipLabel = __('Assignee(s)'); names.push(emptyTooltipLabel); } @@ -90,6 +89,27 @@ export default { return counter; }, + mergeNotAllowedTooltipMessage() { + const assigneesCount = this.users.length; + + if (this.issuableType !== 'merge_request' || assigneesCount === 0) { + return null; + } + + const cannotMergeCount = this.users.filter(u => u.can_merge === false).length; + const canMergeCount = assigneesCount - cannotMergeCount; + + if (canMergeCount === assigneesCount) { + // Everyone can merge + return null; + } else if (cannotMergeCount === assigneesCount && assigneesCount > 1) { + return 'No one can merge'; + } else if (assigneesCount === 1) { + return 'Cannot merge'; + } + + return `${canMergeCount}/${assigneesCount} can merge`; + }, }, methods: { assignSelf() { @@ -154,6 +174,15 @@ export default {
+ + +