From 5f85444a43df1c5f025b520fdbc682cbf95d3ca0 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 25 Aug 2022 15:12:14 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab-ci.yml | 21 -- .rubocop_todo/style/redundant_self.yml | 1 - .../components/submit_dropdown.vue | 84 ++++-- .../stores/modules/batch_comments/actions.js | 6 +- .../notes/components/note_header.vue | 2 +- .../notes/components/noteable_note.vue | 2 +- .../shared/components/settings_block.vue | 6 +- .../graph/graph_component_wrapper.vue | 1 - .../components/graph/graph_view_selector.vue | 39 +-- .../components/performance_insights_modal.vue | 171 ------------- app/assets/javascripts/pipelines/constants.js | 2 - .../get_performance_insights.query.graphql | 28 -- app/assets/javascripts/pipelines/utils.js | 21 -- .../page_bundles/merge_requests.scss | 4 +- app/assets/stylesheets/pages/notes.scss | 7 +- app/controllers/admin/users_controller.rb | 2 +- .../merge_requests/drafts_controller.rb | 18 +- app/finders/deployments_finder.rb | 1 + app/graphql/types/ci/job_type.rb | 6 + app/graphql/types/deployment_type.rb | 15 ++ app/helpers/application_settings_helper.rb | 5 +- app/models/ci/trigger.rb | 6 +- app/models/concerns/approvable.rb | 3 +- app/models/deployment.rb | 4 + app/models/resource_state_event.rb | 3 +- app/models/resource_timebox_event.rb | 3 +- app/models/user.rb | 5 +- .../merge_request_noteable_entity.rb | 6 + .../_account_and_limit.html.haml | 8 +- .../application_settings/general.html.haml | 2 +- .../development/escape_gitaly_refs.yml | 8 + ...22094804_add_issues_authorization_index.rb | 15 ++ db/schema_migrations/20220822094804 | 1 + .../auth/ldap/ldap_synchronization.md | 8 +- doc/administration/pages/index.md | 6 + doc/api/graphql/reference/index.md | 7 + doc/topics/git/lfs/migrate_to_git_lfs.md | 2 +- doc/user/admin_area/moderate_users.md | 6 +- doc/user/group/import/index.md | 5 + doc/user/group/settings/import_export.md | 1 + doc/user/project/repository/branches/index.md | 3 +- doc/user/project/settings/import_export.md | 14 +- lib/api/users.rb | 2 +- lib/gitlab/encoding_helper.rb | 24 ++ lib/gitlab/git.rb | 2 +- lib/gitlab/git/repository.rb | 10 +- lib/gitlab/git/tag.rb | 2 +- lib/gitlab/gitaly_client/ref_service.rb | 4 +- .../import_export/group/import_export.yml | 1 + .../group/legacy_import_export.yml | 1 + .../import_export/project/import_export.yml | 33 +++ locale/gitlab.pot | 50 ++-- .../admin/users_controller_spec.rb | 6 +- .../merge_requests/drafts_controller_spec.rb | 34 ++- spec/features/admin/admin_settings_spec.rb | 33 ++- .../entities/merge_request_noteable.json | 6 +- .../gitlab/import_export/complex/project.json | 70 ++++- .../complex/tree/project/issues.ndjson | 2 +- .../tree/project/merge_requests.ndjson | 2 +- .../components/submit_dropdown_spec.js | 20 +- .../modules/batch_comments/actions_spec.js | 3 +- .../graph/graph_component_wrapper_spec.js | 12 +- .../graph/graph_view_selector_spec.js | 59 ----- spec/frontend/pipelines/graph/mock_data.js | 242 ------------------ .../performance_insights_modal_spec.js | 131 ---------- spec/frontend/pipelines/utils_spec.js | 44 +--- spec/graphql/types/ci/job_type_spec.rb | 15 ++ .../types/deployment_details_type_spec.rb | 2 +- spec/graphql/types/deployment_type_spec.rb | 2 +- .../application_settings_helper_spec.rb | 8 + spec/lib/gitlab/encoding_helper_spec.rb | 30 +++ spec/lib/gitlab/git/repository_spec.rb | 55 ++++ spec/lib/gitlab/git_spec.rb | 14 +- spec/lib/gitlab/import_export/all_models.yml | 11 + .../project/tree_restorer_spec.rb | 16 ++ .../import_export/safe_model_attributes.yml | 12 + spec/models/ci/trigger_spec.rb | 17 +- spec/models/deployment_spec.rb | 16 ++ spec/models/user_spec.rb | 12 +- .../environments/deployments_query_spec.rb | 141 ++++++++++ spec/requests/api/users_spec.rb | 6 +- .../deactivate_dormant_users_worker_spec.rb | 10 +- 82 files changed, 816 insertions(+), 902 deletions(-) delete mode 100644 app/assets/javascripts/pipelines/components/performance_insights_modal.vue delete mode 100644 app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql create mode 100644 config/feature_flags/development/escape_gitaly_refs.yml create mode 100644 db/post_migrate/20220822094804_add_issues_authorization_index.rb create mode 100644 db/schema_migrations/20220822094804 delete mode 100644 spec/frontend/pipelines/performance_insights_modal_spec.js diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 486da5bb906..882ae337c60 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -130,27 +130,6 @@ variables: REGISTRY_HOST: "registry.gitlab.com" REGISTRY_GROUP: "gitlab-org" - # Preparing custom clone path to reduce space used by all random forks - # on GitLab.com's Shared Runners. Our main forks - especially the security - # ones - will have this variable overwritten in the project settings, so that - # a security-related code or code using our protected variables will be never - # stored on the same path as the community forks. - # Part of the solution for the `no space left on device` problem described at - # https://gitlab.com/gitlab-org/gitlab/issues/197876. - # - # For this purpose the https://gitlab.com/gitlab-org-forks group was created - # to host a placeholder for the `/builds/gitlab-org-forks` path and ensure - # that no legitimate project will ever use it and - by mistake - execute its - # job on a shared working directory. It also requires proper configuration of - # the Runner that executes the job (which was prepared for our shared runners - # by https://ops.gitlab.net/gitlab-cookbooks/chef-repo/-/merge_requests/3977). - # - # Because of all of that PLEASE DO NOT CHANGE THE PATH. - # - # For more details and reasoning that brought this change please check - # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24887 - GIT_CLONE_PATH: "/builds/gitlab-org-forks/${CI_PROJECT_NAME}" - include: - local: .gitlab/ci/*.gitlab-ci.yml - remote: 'https://gitlab.com/gitlab-org/frontend/untamper-my-lockfile/-/raw/main/templates/merge_request_pipelines.yml' diff --git a/.rubocop_todo/style/redundant_self.yml b/.rubocop_todo/style/redundant_self.yml index f14926c0bba..8c688dc89c1 100644 --- a/.rubocop_todo/style/redundant_self.yml +++ b/.rubocop_todo/style/redundant_self.yml @@ -47,7 +47,6 @@ Style/RedundantSelf: - 'app/models/commit_status.rb' - 'app/models/compare.rb' - 'app/models/concerns/after_commit_queue.rb' - - 'app/models/concerns/approvable.rb' - 'app/models/concerns/atomic_internal_id.rb' - 'app/models/concerns/avatarable.rb' - 'app/models/concerns/awardable.rb' diff --git a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue index 54b9953270b..c5b713b5447 100644 --- a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue +++ b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue @@ -1,7 +1,16 @@ @@ -178,15 +156,6 @@ export default { - - {{ $options.i18n.performanceBtnText }} - -
{{ $options.i18n.hoverTipText }} - -
diff --git a/app/assets/javascripts/pipelines/components/performance_insights_modal.vue b/app/assets/javascripts/pipelines/components/performance_insights_modal.vue deleted file mode 100644 index fdbf0ca19bc..00000000000 --- a/app/assets/javascripts/pipelines/components/performance_insights_modal.vue +++ /dev/null @@ -1,171 +0,0 @@ - - - diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js index 7b38f870cb6..0619ecaabbf 100644 --- a/app/assets/javascripts/pipelines/constants.js +++ b/app/assets/javascripts/pipelines/constants.js @@ -109,5 +109,3 @@ export const DEFAULT_FIELDS = [ columnClass: 'gl-w-20p', }, ]; - -export const performanceModalId = 'performanceInsightsModal'; diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql deleted file mode 100644 index 25e990c8934..00000000000 --- a/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql +++ /dev/null @@ -1,28 +0,0 @@ -query getPerformanceInsightsData($fullPath: ID!, $iid: ID!) { - project(fullPath: $fullPath) { - id - pipeline(iid: $iid) { - id - jobs { - pageInfo { - hasNextPage - } - nodes { - id - duration - detailedStatus { - id - detailsPath - } - name - stage { - id - name - } - startedAt - queuedDuration - } - } - } - } -} diff --git a/app/assets/javascripts/pipelines/utils.js b/app/assets/javascripts/pipelines/utils.js index 83e00b80426..588d15495ab 100644 --- a/app/assets/javascripts/pipelines/utils.js +++ b/app/assets/javascripts/pipelines/utils.js @@ -153,24 +153,3 @@ export const getPipelineDefaultTab = (url) => { return null; }; - -export const calculateJobStats = (jobs, sortField) => { - const jobNodes = [...jobs.nodes]; - - const sorted = jobNodes.sort((a, b) => { - return b[sortField] - a[sortField]; - }); - - return sorted[0]; -}; - -export const calculateSlowestFiveJobs = (jobs) => { - const jobNodes = [...jobs.nodes]; - const limit = 5; - - return jobNodes - .sort((a, b) => { - return b.duration - a.duration; - }) - .slice(0, limit); -}; diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss index 05397906b76..baed3694306 100644 --- a/app/assets/stylesheets/page_bundles/merge_requests.scss +++ b/app/assets/stylesheets/page_bundles/merge_requests.scss @@ -771,6 +771,7 @@ $tabs-holder-z-index: 250; &.show .dropdown-menu { width: calc(100vw - 20px); max-width: 650px; + max-height: calc(100vh - 50px); .gl-new-dropdown-inner { max-height: none !important; @@ -812,8 +813,7 @@ $tabs-holder-z-index: 250; } .md-preview-holder { - max-height: 180px; - height: 180px; + max-height: 172px; } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index db07f16dfd0..4830ade72b0 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -164,7 +164,7 @@ $system-note-svg-size: 16px; } .note-body { - padding: $gl-padding-4; + padding: $gl-padding-4 $gl-padding-4 $gl-padding-4 $gl-padding-8; overflow-x: auto; overflow-y: hidden; @@ -305,7 +305,7 @@ $system-note-svg-size: 16px; height: $system-note-icon-size; border: 1px solid $gray-10; border-radius: $system-note-icon-size; - margin: -6px 20px 0 0; + margin: -6px 0 0; svg { width: $system-note-svg-size; @@ -551,6 +551,7 @@ $system-note-svg-size: 16px; .note-header { display: flex; justify-content: space-between; + align-items: center; flex-wrap: wrap; > .note-header-info, @@ -581,7 +582,7 @@ $system-note-svg-size: 16px; .note-header-info { min-width: 0; - padding-left: $gl-padding-4; + padding-left: $gl-padding-8; &.discussion { padding-bottom: 0; diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 5cc0c8f3970..1a57d271271 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -105,7 +105,7 @@ class Admin::UsersController < Admin::ApplicationController return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked? return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated? return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal? - return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: ::User::MINIMUM_INACTIVE_DAYS }) unless user.can_be_deactivated? + return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period }) unless user.can_be_deactivated? user.deactivate redirect_back_or_admin_user(notice: _("Successfully deactivated")) diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index ff6b6bfaf27..58d462e8c9f 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -49,8 +49,16 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli def publish result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true)) - if Feature.enabled?(:mr_review_submit_comment, @project) && create_note_params[:note] - Notes::CreateService.new(@project, current_user, create_note_params).execute + if Feature.enabled?(:mr_review_submit_comment, @project) + ::Notes::CreateService.new(@project, current_user, create_note_params).execute if create_note_params[:note] + + if Gitlab::Utils.to_boolean(approve_params[:approve]) + success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) + + unless success + return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error + end + end end if result[:status] == :success @@ -115,6 +123,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli end end + def approve_params + params.permit(:approve) + end + def prepare_notes_for_rendering(notes) return [] unless notes @@ -148,3 +160,5 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli access_denied! unless can?(current_user, :create_note, merge_request) end end + +Projects::MergeRequests::DraftsController.prepend_mod diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb index e5e08d2971f..5b2139cb941 100644 --- a/app/finders/deployments_finder.rb +++ b/app/finders/deployments_finder.rb @@ -211,6 +211,7 @@ class DeploymentsFinder environment: [], deployable: { job_artifacts: [], + user: [], pipeline: { project: { route: [], diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index 4ea9a016e74..ab6103d9469 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -92,6 +92,8 @@ module Types description: 'Indicates the job is stuck.' field :triggered, GraphQL::Types::Boolean, null: true, description: 'Whether the job was triggered.' + field :web_path, GraphQL::Types::String, null: true, + description: 'Web path of the job.' def kind return ::Ci::Build unless [::Ci::Build, ::Ci::Bridge].include?(object.class) @@ -181,6 +183,10 @@ module Types ::Gitlab::Routing.url_helpers.project_commits_path(object.project, ref_name) end + def web_path + ::Gitlab::Routing.url_helpers.project_job_path(object.project, object) + end + def coverage object&.coverage end diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb index 1b61001fc10..70a3a4cb574 100644 --- a/app/graphql/types/deployment_type.rb +++ b/app/graphql/types/deployment_type.rb @@ -50,5 +50,20 @@ module Types field :status, Types::DeploymentStatusEnum, description: 'Status of the deployment.' + + field :commit, + Types::CommitType, + description: 'Commit details of the deployment.', + calls_gitaly: true + + field :job, + Types::Ci::JobType, + description: 'Pipeline job of the deployment.', + method: :build + + field :triggerer, + Types::UserType, + description: 'User who executed the deployment.', + method: :deployed_by end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 3293db970c8..a9d07adb5fb 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -437,7 +437,10 @@ module ApplicationSettingsHelper :pipeline_limit_per_project_user_sha, :invitation_flow_enforcement ].tap do |settings| - settings << :deactivate_dormant_users unless Gitlab.com? + next if Gitlab.com? + + settings << :deactivate_dormant_users + settings << :deactivate_dormant_users_period end end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index c4db4754c52..1092b9c9564 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -6,6 +6,8 @@ module Ci include Limitable include IgnorableColumns + TRIGGER_TOKEN_PREFIX = 'glptt-' + ignore_column :ref, remove_with: '15.4', remove_after: '2022-08-22' self.limit_name = 'pipeline_triggers' @@ -22,7 +24,7 @@ module Ci before_validation :set_default_values def set_default_values - self.token = SecureRandom.hex(15) if self.token.blank? + self.token = "#{TRIGGER_TOKEN_PREFIX}#{SecureRandom.hex(20)}" if self.token.blank? end def last_trigger_request @@ -34,7 +36,7 @@ module Ci end def short_token - token[0...4] if token.present? + token.delete_prefix(TRIGGER_TOKEN_PREFIX)[0...4] if token.present? end def can_access_project? diff --git a/app/models/concerns/approvable.rb b/app/models/concerns/approvable.rb index 10fc5e8870c..f18a3877fe0 100644 --- a/app/models/concerns/approvable.rb +++ b/app/models/concerns/approvable.rb @@ -27,12 +27,11 @@ module Approvable scope :not_approved_by_users_with_usernames, -> (usernames) do users = User.where(username: usernames).select(:id) - self_table = self.arel_table app_table = Approval.arel_table where( Approval.where(approvals: { user_id: users }) - .where(app_table[:merge_request_id].eq(self_table[:id])) + .where(app_table[:merge_request_id].eq(arel_table[:id])) .select('true') .arel.exists.not ) diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 325754f001a..a463448c206 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -222,6 +222,10 @@ class Deployment < ApplicationRecord Ci::Build.where(id: deployable_ids) end + def build + deployable if deployable.is_a?(::Ci::Build) + end + class << self ## # FastDestroyAll concerns diff --git a/app/models/resource_state_event.rb b/app/models/resource_state_event.rb index 5588897e792..6ebb9d5f176 100644 --- a/app/models/resource_state_event.rb +++ b/app/models/resource_state_event.rb @@ -3,8 +3,9 @@ class ResourceStateEvent < ResourceEvent include IssueResourceEvent include MergeRequestResourceEvent + include Importable - validate :exactly_one_issuable + validate :exactly_one_issuable, unless: :importing? belongs_to :source_merge_request, class_name: 'MergeRequest', foreign_key: :source_merge_request_id diff --git a/app/models/resource_timebox_event.rb b/app/models/resource_timebox_event.rb index 6bde8e63d35..26bf2a225d4 100644 --- a/app/models/resource_timebox_event.rb +++ b/app/models/resource_timebox_event.rb @@ -5,8 +5,9 @@ class ResourceTimeboxEvent < ResourceEvent include IssueResourceEvent include MergeRequestResourceEvent + include Importable - validate :exactly_one_issuable + validate :exactly_one_issuable, unless: :importing? enum action: { add: 1, diff --git a/app/models/user.rb b/app/models/user.rb index 5e8ef306317..466a81a6a28 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -92,7 +92,6 @@ class User < ApplicationRecord include ForcedEmailConfirmation include RequireEmailVerification - MINIMUM_INACTIVE_DAYS = 90 MINIMUM_DAYS_CREATED = 7 # Override Devise::Models::Trackable#update_tracked_fields! @@ -488,7 +487,7 @@ class User < ApplicationRecord scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) } scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) } scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) } - scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) } + scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', Gitlab::CurrentSettings.deactivate_dormant_users_period.day.ago.to_date) } scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) } scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) } scope :by_ids_or_usernames, -> (ids, usernames) { where(username: usernames).or(where(id: ids)) } @@ -2322,7 +2321,7 @@ class User < ApplicationRecord end def no_recent_activity? - last_active_at.to_i <= MINIMUM_INACTIVE_DAYS.days.ago.to_i + last_active_at.to_i <= Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_i end def update_highest_role? diff --git a/app/serializers/merge_request_noteable_entity.rb b/app/serializers/merge_request_noteable_entity.rb index f8c8e3538da..bd0de3a72b9 100644 --- a/app/serializers/merge_request_noteable_entity.rb +++ b/app/serializers/merge_request_noteable_entity.rb @@ -40,6 +40,10 @@ class MergeRequestNoteableEntity < IssuableEntity expose :can_update do |merge_request| can?(current_user, :update_merge_request, merge_request) end + + expose :can_approve do |merge_request| + merge_request.can_be_approved_by?(current_user) + end end expose :locked_discussion_docs_path, if: -> (merge_request) { merge_request.discussion_locked? } do |merge_request| @@ -65,3 +69,5 @@ class MergeRequestNoteableEntity < IssuableEntity @presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user) # rubocop: disable CodeReuse/Presenter end end + +MergeRequestNoteableEntity.prepend_mod_with('MergeRequestNoteableEntity') diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml index 76c25e4cf5a..686f8ff7b86 100644 --- a/app/views/admin/application_settings/_account_and_limit.html.haml +++ b/app/views/admin/application_settings/_account_and_limit.html.haml @@ -52,7 +52,13 @@ = f.label :deactivate_dormant_users, _('Dormant users'), class: 'label-bold' - dormant_users_help_link = help_page_path('user/admin_area/moderate_users', anchor: 'automatically-deactivate-dormant-users') - dormant_users_help_link_start = ''.html_safe % { url: dormant_users_help_link } - = f.gitlab_ui_checkbox_component :deactivate_dormant_users, _('Deactivate dormant users after 90 days of inactivity'), help_text: _('Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}').html_safe % { link_start: dormant_users_help_link_start, link_end: ''.html_safe } + = f.gitlab_ui_checkbox_component :deactivate_dormant_users, _('Deactivate dormant users after a period of inactivity'), help_text: _('Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}').html_safe % { link_start: dormant_users_help_link_start, link_end: ''.html_safe } + .form-group + = f.label :deactivate_dormant_users_period, _('Period of inactivity (days)'), class: 'label-light' + = f.number_field :deactivate_dormant_users_period, class: 'form-control gl-form-input', min: '1' + .form-text.text-muted + = _('Period of inactivity before deactivation') + .form-group = f.label :personal_access_token_prefix, _('Personal Access Token prefix'), class: 'label-light' = f.text_field :personal_access_token_prefix, placeholder: _('Maximum 20 characters'), class: 'form-control gl-form-input' diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml index 7535980151b..cd63873a893 100644 --- a/app/views/admin/application_settings/general.html.haml +++ b/app/views/admin/application_settings/general.html.haml @@ -13,7 +13,7 @@ .settings-content = render 'visibility_and_access' -%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'account_and_limit_settings_content' } } +%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'account_and_limit_settings_content', testid: 'account-limit' } } .settings-header %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only = _('Account and limit') diff --git a/config/feature_flags/development/escape_gitaly_refs.yml b/config/feature_flags/development/escape_gitaly_refs.yml new file mode 100644 index 00000000000..bee62c669ee --- /dev/null +++ b/config/feature_flags/development/escape_gitaly_refs.yml @@ -0,0 +1,8 @@ +--- +name: escape_gitaly_refs +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91058 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366437 +milestone: '15.2' +type: development +group: group::source code +default_enabled: false diff --git a/db/post_migrate/20220822094804_add_issues_authorization_index.rb b/db/post_migrate/20220822094804_add_issues_authorization_index.rb new file mode 100644 index 00000000000..e09b5f8d93b --- /dev/null +++ b/db/post_migrate/20220822094804_add_issues_authorization_index.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddIssuesAuthorizationIndex < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + INDEX_NAME = 'idx_open_issues_on_project_and_confidential_and_author_and_id' + + def up + prepare_async_index :issues, [:project_id, :confidential, :author_id, :id], name: INDEX_NAME, where: 'state_id = 1' + end + + def down + unprepare_async_index :issues, INDEX_NAME + end +end diff --git a/db/schema_migrations/20220822094804 b/db/schema_migrations/20220822094804 new file mode 100644 index 00000000000..6e4e3b0d5e1 --- /dev/null +++ b/db/schema_migrations/20220822094804 @@ -0,0 +1 @@ +035e918bcb674fdf1300a5bccbad87806311e6de8589f2db57d7af9cd0108ee9 \ No newline at end of file diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md index 62706a9e3b9..e1f5855e066 100644 --- a/doc/administration/auth/ldap/ldap_synchronization.md +++ b/doc/administration/auth/ldap/ldap_synchronization.md @@ -38,9 +38,11 @@ fail. This means the user cannot sign in or push or pull code. The process also updates the following user information: -- Email address -- SSH public keys (if `sync_ssh_keys` is set) -- Kerberos identity (if Kerberos is enabled) +- Name. Because of a [sync issue](https://gitlab.com/gitlab-org/gitlab/-/issues/342598), `name` is not synchronized if + [**Prevent users from changing their profile name**](../../../user/admin_area/settings/account_and_limit_settings.md#disable-user-profile-name-changes) is enabled. +- Email address. +- SSH public keys if `sync_ssh_keys` is set. +- Kerberos identity if Kerberos is enabled. ### Adjust LDAP user sync schedule diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index e9d7b5087c5..bea6328ccdd 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -191,6 +191,12 @@ to use the HTTPS protocol. WARNING: Multiple wildcards for one instance is not supported. Only one wildcard per instance can be assigned. +WARNING: +GitLab Pages does not update the OAuth application if changes are made to the redirect URI. +Before you reconfigure, remove the `gitlab_pages` section from `/etc/gitlab/gitlab-secrets.json`, +then run `gitlab-ctl reconfigure`. For more information, read +[GitLab Pages does not regenerate OAuth](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3947). + ### Wildcard domains with TLS-terminating Load Balancer **Requirements:** diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 28f23900839..7698cbc8178 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -10199,6 +10199,7 @@ CI/CD variables for a GitLab instance. | `tags` | [`[String!]`](#string) | Tags for the current job. | | `triggered` | [`Boolean`](#boolean) | Whether the job was triggered. | | `userPermissions` | [`JobPermissions!`](#jobpermissions) | Permissions for the current user on the resource. | +| `webPath` | [`String`](#string) | Web path of the job. | ### `CiJobArtifact` @@ -11019,14 +11020,17 @@ The deployment of an environment. | Name | Type | Description | | ---- | ---- | ----------- | +| `commit` | [`Commit`](#commit) | Commit details of the deployment. | | `createdAt` | [`Time`](#time) | When the deployment record was created. | | `finishedAt` | [`Time`](#time) | When the deployment finished. | | `id` | [`ID`](#id) | Global ID of the deployment. | | `iid` | [`ID`](#id) | Project-level internal ID of the deployment. | +| `job` | [`CiJob`](#cijob) | Pipeline job of the deployment. | | `ref` | [`String`](#string) | Git-Ref that the deployment ran on. | | `sha` | [`String`](#string) | Git-SHA that the deployment ran on. | | `status` | [`DeploymentStatus`](#deploymentstatus) | Status of the deployment. | | `tag` | [`Boolean`](#boolean) | True or false if the deployment ran on a Git-tag. | +| `triggerer` | [`UserCore`](#usercore) | User who executed the deployment. | | `updatedAt` | [`Time`](#time) | When the deployment record was updated. | ### `DeploymentDetails` @@ -11037,14 +11041,17 @@ The details of the deployment. | Name | Type | Description | | ---- | ---- | ----------- | +| `commit` | [`Commit`](#commit) | Commit details of the deployment. | | `createdAt` | [`Time`](#time) | When the deployment record was created. | | `finishedAt` | [`Time`](#time) | When the deployment finished. | | `id` | [`ID`](#id) | Global ID of the deployment. | | `iid` | [`ID`](#id) | Project-level internal ID of the deployment. | +| `job` | [`CiJob`](#cijob) | Pipeline job of the deployment. | | `ref` | [`String`](#string) | Git-Ref that the deployment ran on. | | `sha` | [`String`](#string) | Git-SHA that the deployment ran on. | | `status` | [`DeploymentStatus`](#deploymentstatus) | Status of the deployment. | | `tag` | [`Boolean`](#boolean) | True or false if the deployment ran on a Git-tag. | +| `triggerer` | [`UserCore`](#usercore) | User who executed the deployment. | | `updatedAt` | [`Time`](#time) | When the deployment record was updated. | ### `Design` diff --git a/doc/topics/git/lfs/migrate_to_git_lfs.md b/doc/topics/git/lfs/migrate_to_git_lfs.md index 864615e7264..dda15845088 100644 --- a/doc/topics/git/lfs/migrate_to_git_lfs.md +++ b/doc/topics/git/lfs/migrate_to_git_lfs.md @@ -9,7 +9,7 @@ description: "How to migrate an existing Git repository to Git LFS with BFG." WARNING: The following documentation is deprecated. We recommend using -[`git lfs migrate`](https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-migrate.1.ronn) +[`git lfs migrate`](https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-migrate.adoc) instead of the method documented below. Using Git LFS can help you to reduce the size of your Git diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md index ab581cd3aa8..bdb2fb30b06 100644 --- a/doc/user/admin_area/moderate_users.md +++ b/doc/user/admin_area/moderate_users.md @@ -169,12 +169,13 @@ Users can also be deactivated using the [GitLab API](../../api/users.md#deactiva ### Automatically deactivate dormant users -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/320875) in GitLab 14.0. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/320875) in GitLab 14.0. +> - Customizable time period [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336747) in GitLab 15.4 Administrators can enable automatic deactivation of users who either: - Were created more than a week ago and have not signed in. -- Have no activity in the last 90 days. +- Have no activity for a specified period of time (defaults to 90 days). To do this: @@ -182,6 +183,7 @@ To do this: 1. On the left sidebar, select **Settings > General**. 1. Expand the **Account and limit** section. 1. Under **Dormant users**, check **Deactivate dormant users after 90 days of inactivity**. +1. Under **Period of inactivity (days)**, enter a period of time before deactivation. 1. Select **Save changes**. When this feature is enabled, GitLab runs a job once a day to deactivate the dormant users. diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md index e016aeb977d..0cadf89fb0c 100644 --- a/doc/user/group/import/index.md +++ b/doc/user/group/import/index.md @@ -88,6 +88,7 @@ migrated: - Board Lists - Boards - Epics ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) in 13.7) + - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) - Finisher - Group Labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) in 13.9) - Iterations ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) in 13.10) @@ -116,6 +117,8 @@ On self-managed GitLab, migrating project resources are not available by default - CI Pipelines ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339407) in GitLab 14.6) - Designs ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339421) in GitLab 15.1) - Issues ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267946) in GitLab 14.4) + - Issue resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) + - Issue resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) - Labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339419) in GitLab 14.4) - LFS Objects ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339405) in GitLab 14.8) - Members ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341886) in GitLab 14.8) @@ -123,6 +126,8 @@ On self-managed GitLab, migrating project resources are not available by default - Multiple merge request assignees ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3) - Merge request reviewers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3) - Merge request approvers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3) + - Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) + - Merge request resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) - Migrate Push Rules ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.6) - Pull Requests (including external pull requests) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339409) in GitLab 14.5) - Pipeline History ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339412) in GitLab 14.6) diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md index eb94a181647..9dce1f0f023 100644 --- a/doc/user/group/settings/import_export.md +++ b/doc/user/group/settings/import_export.md @@ -55,6 +55,7 @@ The following items are exported: - Badges - Subgroups (including all the aforementioned data) - Epics + - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) - Events - [Wikis](../../project/wiki/group.md) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9) diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md index 6da2e5fc7ee..5e5a42a061b 100644 --- a/doc/user/project/repository/branches/index.md +++ b/doc/user/project/repository/branches/index.md @@ -59,7 +59,8 @@ To compare branches in a repository: ![Delete merged branches](img/delete_merged_branches.png) This feature allows merged branches to be deleted in bulk. Only branches that -have been merged and [are not protected](../../protected_branches.md) are deleted as part of +have been merged into the project's default branch and +[are not protected](../../protected_branches.md) are deleted as part of this operation. It's particularly useful to clean up old branches that were not deleted diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index 4eeb7c5ba83..fa6fb2971d6 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -62,8 +62,18 @@ The following items are exported: - Project and wiki repositories - Project uploads - Project configuration, excluding integrations -- Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, time - tracking, and other project entities +- Issues + - Issue comments + - Issue resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) + - Issue resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) +- Merge requests + - Merge request diffs + - Merge request comments + - Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4) +- Labels +- Milestones +- Snippets +- Time tracking and other project entities - Design Management files and data - LFS objects - Issue boards diff --git a/lib/api/users.rb b/lib/api/users.rb index c93c0f601a0..adaafa08aea 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -733,7 +733,7 @@ module API unless user.can_be_deactivated? forbidden!('A blocked user cannot be deactivated by the API') if user.blocked? forbidden!('An internal user cannot be deactivated by the API') if user.internal? - forbidden!("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated") + forbidden!("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated") end if user.deactivate diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb index f26ab6e3ed1..34c674c3003 100644 --- a/lib/gitlab/encoding_helper.rb +++ b/lib/gitlab/encoding_helper.rb @@ -71,6 +71,21 @@ module Gitlab encode_utf8(data, replace: UNICODE_REPLACEMENT_CHARACTER) end + # This method escapes unsupported UTF-8 characters instead of deleting them + def encode_utf8_with_escaping!(message) + return encode!(message) if Feature.disabled?(:escape_gitaly_refs) + + message = force_encode_utf8(message) + return message if message.valid_encoding? + + unless message.valid_encoding? + message = message.chars.map { |char| char.valid_encoding? ? char : escape_chars(char) }.join + end + + # encode and clean the bad chars + message.replace clean(message) + end + def encode_utf8(message, replace: "") message = force_encode_utf8(message) return message if message.valid_encoding? @@ -145,6 +160,15 @@ module Gitlab message.force_encoding("UTF-8") end + # Escapes \x80 - \xFF characters not supported by UTF-8 + def escape_chars(char) + bytes = char.bytes + + return char unless bytes.one? + + "%#{bytes.first.to_s(16).upcase}" + end + def clean(message, replace: "") message.encode( "UTF-16BE", diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb index 4b9f2ababc8..4b877bf44da 100644 --- a/lib/gitlab/git.rb +++ b/lib/gitlab/git.rb @@ -25,7 +25,7 @@ module Gitlab include Gitlab::EncodingHelper def ref_name(ref) - encode!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '') + encode_utf8_with_escaping!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '') end def branch_name(ref) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index e920b7e5843..ddc43b1764b 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1011,16 +1011,20 @@ module Gitlab end def search_files_by_name(query, ref) - safe_query = Regexp.escape(query.sub(%r{^/*}, "")) + safe_query = query.sub(%r{^/*}, "") ref ||= root_ref return [] if empty? || safe_query.blank? - gitaly_repository_client.search_files_by_name(ref, safe_query) + gitaly_repository_client.search_files_by_name(ref, safe_query).map do |file| + Gitlab::EncodingHelper.encode_utf8(file) + end end def search_files_by_regexp(filter, ref = 'HEAD') - gitaly_repository_client.search_files_by_regexp(ref, filter) + gitaly_repository_client.search_files_by_regexp(ref, filter).map do |file| + Gitlab::EncodingHelper.encode_utf8(file) + end end def find_commits_by_message(query, ref, path, limit, offset) diff --git a/lib/gitlab/git/tag.rb b/lib/gitlab/git/tag.rb index 25895dc6728..5ed5158eeea 100644 --- a/lib/gitlab/git/tag.rb +++ b/lib/gitlab/git/tag.rb @@ -63,7 +63,7 @@ module Gitlab end def init_from_gitaly - @name = encode!(@raw_tag.name.dup) + @name = encode_utf8_with_escaping!(@raw_tag.name.dup) @target = @raw_tag.id @message = message_from_gitaly_tag diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb index 42f9c165610..0a1a61363f1 100644 --- a/lib/gitlab/gitaly_client/ref_service.rb +++ b/lib/gitlab/gitaly_client/ref_service.rb @@ -104,7 +104,7 @@ module Gitlab return unless branch target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit) - Gitlab::Git::Branch.new(@repository, encode!(branch.name.dup), branch.target_commit.id, target_commit) + Gitlab::Git::Branch.new(@repository, branch.name.dup, branch.target_commit.id, target_commit) end def find_tag(tag_name) @@ -273,7 +273,7 @@ module Gitlab message.branches.map do |gitaly_branch| Gitlab::Git::Branch.new( @repository, - encode!(gitaly_branch.name.dup), + gitaly_branch.name.dup, gitaly_branch.commit_id, commit_from_local_branches_response(gitaly_branch) ) diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml index a58469cea57..109f1f85b1c 100644 --- a/lib/gitlab/import_export/group/import_export.yml +++ b/lib/gitlab/import_export/group/import_export.yml @@ -120,6 +120,7 @@ ee: - events: - :push_event_payload - :system_note_metadata + - :resource_state_events - boards: - :board_assignee - :milestone diff --git a/lib/gitlab/import_export/group/legacy_import_export.yml b/lib/gitlab/import_export/group/legacy_import_export.yml index 985e3af1e0c..4df481822a8 100644 --- a/lib/gitlab/import_export/group/legacy_import_export.yml +++ b/lib/gitlab/import_export/group/legacy_import_export.yml @@ -114,6 +114,7 @@ ee: - :award_emoji - events: - :push_event_payload + - :resource_state_events - boards: - :board_assignee - :milestone diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index c5b8f3fd35b..6dd5f7b98ea 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -29,6 +29,9 @@ tree: - resource_label_events: - label: - :priorities + - resource_milestone_events: + - :milestone + - :resource_state_events - designs: - notes: - :author @@ -82,6 +85,9 @@ tree: - resource_label_events: - label: - :priorities + - resource_milestone_events: + - :milestone + - :resource_state_events - :external_pull_requests - ci_pipelines: - notes: @@ -721,6 +727,22 @@ included_attributes: - :build_git_strategy - :build_enabled - :security_and_compliance_enabled + resource_iteration_events: + - :user_id + - :action + - :created_at + resource_milestone_events: + - :user_id + - :action + - :created_at + - :state + resource_state_events: + - :user_id + - :state + - :created_at + - :source_commit + - :close_after_error_tracking_resolve + - :close_auto_resolve_prometheus_alert # Do not include the following attributes for the models specified. excluded_attributes: @@ -989,6 +1011,17 @@ excluded_attributes: milestone_releases: - :milestone_id - :release_id + resource_milestone_events: + - :id + - :issue_id + - :merge_request_id + - :milestone_id + resource_state_events: + - :id + - :issue_id + - :merge_request_id + - :epic_id + - :source_merge_request_id methods: notes: - :type diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 97370eaee60..38d16eb4f17 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4035,6 +4035,9 @@ msgstr "" msgid "An error occurred while adding formatted title for epic" msgstr "" +msgid "An error occurred while approving, please try again." +msgstr "" + msgid "An error occurred while authorizing your role" msgstr "" @@ -4873,6 +4876,9 @@ msgstr "" msgid "Approve a merge request" msgstr "" +msgid "Approve merge request" +msgstr "" + msgid "Approve the current merge request." msgstr "" @@ -12297,7 +12303,7 @@ msgstr "" msgid "Days to merge" msgstr "" -msgid "Deactivate dormant users after 90 days of inactivity" +msgid "Deactivate dormant users after a period of inactivity" msgstr "" msgid "Dear Administrator," @@ -16409,9 +16415,6 @@ msgstr "" msgid "February" msgstr "" -msgid "Feedback issue" -msgstr "" - msgid "Fetch and check out this merge request's feature branch:" msgstr "" @@ -28402,9 +28405,6 @@ msgstr "" msgid "Perform common operations on GitLab project" msgstr "" -msgid "Performance insights" -msgstr "" - msgid "Performance optimization" msgstr "" @@ -28480,6 +28480,12 @@ msgstr "" msgid "Period in seconds" msgstr "" +msgid "Period of inactivity (days)" +msgstr "" + +msgid "Period of inactivity before deactivation" +msgstr "" + msgid "Permalink" msgstr "" @@ -29191,18 +29197,9 @@ msgstr "" msgid "Pipeline|Failed" msgstr "" -msgid "Pipeline|Five slowest jobs" -msgstr "" - msgid "Pipeline|In progress" msgstr "" -msgid "Pipeline|Last executed job" -msgstr "" - -msgid "Pipeline|Longest queued job" -msgstr "" - msgid "Pipeline|Manual" msgstr "" @@ -29215,18 +29212,12 @@ msgstr "" msgid "Pipeline|Merged result pipeline" msgstr "" -msgid "Pipeline|Only able to show first 100 results" -msgstr "" - msgid "Pipeline|Passed" msgstr "" msgid "Pipeline|Pending" msgstr "" -msgid "Pipeline|Performance insights" -msgstr "" - msgid "Pipeline|Pipeline" msgstr "" @@ -29284,12 +29275,6 @@ msgstr "" msgid "Pipeline|Test coverage" msgstr "" -msgid "Pipeline|The last executed job is the last job to start in the pipeline." -msgstr "" - -msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner" -msgstr "" - msgid "Pipeline|This change will decrease the overall test coverage if merged." msgstr "" @@ -29320,9 +29305,6 @@ msgstr "" msgid "Pipeline|View commit" msgstr "" -msgid "Pipeline|View dependency" -msgstr "" - msgid "Pipeline|View pipeline" msgstr "" @@ -40856,6 +40838,9 @@ msgstr "" msgid "To add the entry manually, provide the following details to the application on your phone." msgstr "" +msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated." +msgstr "" + msgid "To complete registration, we need additional details from you." msgstr "" @@ -45572,6 +45557,9 @@ msgstr "" msgid "Your new comment" msgstr "" +msgid "Your password" +msgstr "" + msgid "Your password reset token has expired." msgstr "" diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 515ad9daf36..bd70787e19f 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -270,19 +270,19 @@ RSpec.describe Admin::UsersController do let(:user) { create(:user, **activity) } context 'with no recent activity' do - let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.next.days.ago } } + let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago } } it_behaves_like 'a request that deactivates the user' end context 'with recent activity' do - let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.pred.days.ago } } + let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago } } it 'does not deactivate the user' do put :deactivate, params: { id: user.username } user.reload expect(user.deactivated?).to be_falsey - expect(flash[:notice]).to eq("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated") + expect(flash[:notice]).to eq("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated") end end end diff --git a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb index b9ede84157d..ea782309f93 100644 --- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Projects::MergeRequests::DraftsController do include RepoHelpers let(:project) { create(:project, :repository) } - let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) } + let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project, author: create(:user)) } let(:user) { project.first_owner } let(:user2) { create(:user) } @@ -417,6 +417,38 @@ RSpec.describe Projects::MergeRequests::DraftsController do end end end + + context 'approve merge request' do + before do + create(:draft_note, merge_request: merge_request, author: user) + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(mr_review_submit_comment: false) + end + + it 'does not approve' do + post :publish, params: params.merge!(approve: true) + + expect(merge_request.approvals.reload.size).to be(0) + end + end + + context 'when feature flag is enabled' do + it 'approves merge request' do + post :publish, params: params.merge!(approve: true) + + expect(merge_request.approvals.reload.size).to be(1) + end + + it 'does not approve merge request' do + post :publish, params: params.merge!(approve: false) + + expect(merge_request.approvals.reload.size).to be(0) + end + end + end end describe 'DELETE #destroy' do diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 8843e13026b..0f5d9892e66 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -102,7 +102,7 @@ RSpec.describe 'Admin updates settings' do end it 'change Account and Limit Settings' do - page.within('.as-account-limit') do + page.within(find('[data-testid="account-limit"]')) do uncheck 'Gravatar enabled' click_button 'Save changes' end @@ -112,7 +112,7 @@ RSpec.describe 'Admin updates settings' do end it 'change Maximum export size' do - page.within('.as-account-limit') do + page.within(find('[data-testid="account-limit"]')) do fill_in 'Maximum export size (MB)', with: 25 click_button 'Save changes' end @@ -122,7 +122,7 @@ RSpec.describe 'Admin updates settings' do end it 'change Maximum import size' do - page.within('.as-account-limit') do + page.within(find('[data-testid="account-limit"]')) do fill_in 'Maximum import size (MB)', with: 15 click_button 'Save changes' end @@ -150,16 +150,20 @@ RSpec.describe 'Admin updates settings' do it 'does not expose the setting' do expect(page).to have_no_selector('#application_setting_deactivate_dormant_users') end + + it 'does not expose the setting' do + expect(page).to have_no_selector('#application_setting_deactivate_dormant_users_period') + end end context 'when not Gitlab.com' do let(:dot_com?) { false } - it 'change Dormant users' do - expect(page).to have_unchecked_field('Deactivate dormant users after 90 days of inactivity') + it 'changes Dormant users' do + expect(page).to have_unchecked_field('Deactivate dormant users after a period of inactivity') expect(current_settings.deactivate_dormant_users).to be_falsey - page.within('.as-account-limit') do + page.within(find('[data-testid="account-limit"]')) do check 'application_setting_deactivate_dormant_users' click_button 'Save changes' end @@ -169,7 +173,22 @@ RSpec.describe 'Admin updates settings' do page.refresh expect(current_settings.deactivate_dormant_users).to be_truthy - expect(page).to have_checked_field('Deactivate dormant users after 90 days of inactivity') + expect(page).to have_checked_field('Deactivate dormant users after a period of inactivity') + end + + it 'change Dormant users period' do + expect(page).to have_field _('Period of inactivity (days)') + + page.within(find('[data-testid="account-limit"]')) do + fill_in _('application_setting_deactivate_dormant_users_period'), with: '35' + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + + page.refresh + + expect(page).to have_field _('Period of inactivity (days)'), with: '35' end end end diff --git a/spec/fixtures/api/schemas/entities/merge_request_noteable.json b/spec/fixtures/api/schemas/entities/merge_request_noteable.json index 4ef19ed32c2..705ebcbd843 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_noteable.json +++ b/spec/fixtures/api/schemas/entities/merge_request_noteable.json @@ -22,11 +22,13 @@ "type": "object", "required": [ "can_create_note", - "can_update" + "can_update", + "can_approve" ], "properties": { "can_create_note": { "type": "boolean" }, - "can_update": { "type": "boolean" } + "can_update": { "type": "boolean" }, + "can_approve": { "type": "boolean" } }, "additionalProperties": false }, diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json index df571ab1326..f3fc69e4936 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/project.json +++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json @@ -394,7 +394,41 @@ "id": 1, "issue_id": 40, "sentry_issue_identifier": 1234567891 - } + }, + "resource_milestone_events": [ + { + "user_id": 1, + "action": "add", + "state": "opened", + "created_at": "2022-08-17T13:06:53.547Z", + "milestone": { + "title": "v4.0", + "description": "Totam quam laborum id magnam natus eaque aspernatur.", + "created_at": "2016-06-14T15:02:04.590Z", + "updated_at": "2016-06-14T15:02:04.590Z", + "state": "active", + "iid": 5 + } + } + ], + "resource_state_events": [ + { + "user_id": 1, + "created_at": "2022-08-17T13:08:16.838Z", + "state": "closed", + "source_commit": null, + "close_after_error_tracking_resolve": false, + "close_auto_resolve_prometheus_alert": false + }, + { + "user_id": 1, + "created_at": "2022-08-17T13:08:17.702Z", + "state": "reopened", + "source_commit": null, + "close_after_error_tracking_resolve": false, + "close_auto_resolve_prometheus_alert": false + } + ] }, { "id": 39, @@ -3215,6 +3249,40 @@ "created_at": "2020-01-07T11:21:21.235Z", "updated_at": "2020-01-08T11:21:21.235Z" } + ], + "resource_milestone_events": [ + { + "user_id": 1, + "action": "add", + "state": "opened", + "created_at": "2022-08-17T13:06:53.547Z", + "milestone": { + "title": "v4.0", + "description": "Totam quam laborum id magnam natus eaque aspernatur.", + "created_at": "2016-06-14T15:02:04.590Z", + "updated_at": "2016-06-14T15:02:04.590Z", + "state": "active", + "iid": 5 + } + } + ], + "resource_state_events": [ + { + "user_id": 1, + "created_at": "2022-08-17T13:08:16.838Z", + "state": "closed", + "source_commit": null, + "close_after_error_tracking_resolve": false, + "close_auto_resolve_prometheus_alert": false + }, + { + "user_id": 1, + "created_at": "2022-08-17T13:08:17.702Z", + "state": "reopened", + "source_commit": null, + "close_after_error_tracking_resolve": false, + "close_auto_resolve_prometheus_alert": false + } ] }, { diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson index 2ebd1a78783..3955107865d 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson +++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson @@ -1,4 +1,4 @@ -{"id":40,"title":"Voluptatem","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.340Z","updated_at":"2016-06-14T15:02:47.967Z","position":0,"branch_name":null,"description":"Aliquam enim illo et possimus.","state":"opened","iid":10,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"test_ee_field":"test","issue_assignees":[{"user_id":1,"issue_id":40},{"user_id":15,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":6,"issue_id":40}],"award_emoji":[{"id":1,"name":"musical_keyboard","user_id":1,"awardable_type":"Issue","awardable_id":40,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}],"zoom_meetings":[{"id":1,"project_id":5,"issue_id":40,"url":"https://zoom.us/j/123456789","issue_status":1,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z"}],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"label_links":[{"id":2,"label_id":2,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}},{"id":3,"label_id":3,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.841Z","updated_at":"2016-07-22T08:57:02.841Z","label":{"id":3,"title":"test3","color":"#428bca","group_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","project_id":null,"type":"GroupLabel","priorities":[{"id":1,"project_id":5,"label_id":1,"priority":1,"created_at":"2016-10-18T09:35:43.338Z","updated_at":"2016-10-18T09:35:43.338Z"}]}}],"notes":[{"id":351,"note":"Quo reprehenderit aliquam qui dicta impedit cupiditate eligendi.","note_html":"

something else entirely

","cached_markdown_version":917504,"noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:47.770Z","updated_at":"2016-06-14T15:02:47.770Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"clapper","user_id":1,"awardable_type":"Note","awardable_id":351,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}]},{"id":352,"note":"Est reprehenderit quas aut aspernatur autem recusandae voluptatem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:47.795Z","updated_at":"2016-06-14T15:02:47.795Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":353,"note":"Perspiciatis suscipit voluptates in eius nihil.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:47.823Z","updated_at":"2016-06-14T15:02:47.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":354,"note":"Aut vel voluptas corrupti nisi provident laboriosam magnam aut.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:47.850Z","updated_at":"2016-06-14T15:02:47.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":355,"note":"Officia dolore consequatur in saepe cum magni.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:47.876Z","updated_at":"2016-06-14T15:02:47.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":356,"note":"Cum ipsum rem voluptas eaque et ea.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:47.908Z","updated_at":"2016-06-14T15:02:47.908Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":357,"note":"Recusandae excepturi asperiores suscipit autem nostrum.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:47.937Z","updated_at":"2016-06-14T15:02:47.937Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":358,"note":"Et hic est id similique et non nesciunt voluptate.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:47.965Z","updated_at":"2016-06-14T15:02:47.965Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":244,"action":"remove","issue_id":40,"merge_request_id":null,"label_id":2,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"sentry_issue":{"id":1,"issue_id":40,"sentry_issue_identifier":1234567891}} +{"id":40,"title":"Voluptatem","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.340Z","updated_at":"2016-06-14T15:02:47.967Z","position":0,"branch_name":null,"description":"Aliquam enim illo et possimus.","state":"opened","iid":10,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"test_ee_field":"test","issue_assignees":[{"user_id":1,"issue_id":40},{"user_id":15,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":6,"issue_id":40}],"award_emoji":[{"id":1,"name":"musical_keyboard","user_id":1,"awardable_type":"Issue","awardable_id":40,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}],"zoom_meetings":[{"id":1,"project_id":5,"issue_id":40,"url":"https://zoom.us/j/123456789","issue_status":1,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z"}],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"label_links":[{"id":2,"label_id":2,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}},{"id":3,"label_id":3,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.841Z","updated_at":"2016-07-22T08:57:02.841Z","label":{"id":3,"title":"test3","color":"#428bca","group_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","project_id":null,"type":"GroupLabel","priorities":[{"id":1,"project_id":5,"label_id":1,"priority":1,"created_at":"2016-10-18T09:35:43.338Z","updated_at":"2016-10-18T09:35:43.338Z"}]}}],"notes":[{"id":351,"note":"Quo reprehenderit aliquam qui dicta impedit cupiditate eligendi.","note_html":"

something else entirely

","cached_markdown_version":917504,"noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:47.770Z","updated_at":"2016-06-14T15:02:47.770Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"clapper","user_id":1,"awardable_type":"Note","awardable_id":351,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}]},{"id":352,"note":"Est reprehenderit quas aut aspernatur autem recusandae voluptatem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:47.795Z","updated_at":"2016-06-14T15:02:47.795Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":353,"note":"Perspiciatis suscipit voluptates in eius nihil.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:47.823Z","updated_at":"2016-06-14T15:02:47.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":354,"note":"Aut vel voluptas corrupti nisi provident laboriosam magnam aut.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:47.850Z","updated_at":"2016-06-14T15:02:47.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":355,"note":"Officia dolore consequatur in saepe cum magni.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:47.876Z","updated_at":"2016-06-14T15:02:47.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":356,"note":"Cum ipsum rem voluptas eaque et ea.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:47.908Z","updated_at":"2016-06-14T15:02:47.908Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":357,"note":"Recusandae excepturi asperiores suscipit autem nostrum.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:47.937Z","updated_at":"2016-06-14T15:02:47.937Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":358,"note":"Et hic est id similique et non nesciunt voluptate.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:47.965Z","updated_at":"2016-06-14T15:02:47.965Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":244,"action":"remove","issue_id":40,"merge_request_id":null,"label_id":2,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"sentry_issue":{"id":1,"issue_id":40,"sentry_issue_identifier":1234567891},"resource_milestone_events":[{"user_id":1,"action":"add","state":"opened","created_at":"2022-08-17T13:06:53.547Z","milestone":{"title":"v4.0","description":"Totam quam laborum id magnam natus eaque aspernatur.","created_at":"2016-06-14T15:02:04.590Z","updated_at":"2016-06-14T15:02:04.590Z","state":"active","iid":5}}],"resource_state_events":[{"user_id":1,"created_at":"2022-08-17T13:08:16.838Z","state":"closed","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false},{"user_id":1,"created_at":"2022-08-17T13:08:17.702Z","state":"reopened","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false}]} {"id":39,"title":"Issue without assignees","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.233Z","updated_at":"2016-06-14T15:02:48.194Z","position":0,"branch_name":null,"description":"Voluptate vel reprehenderit facilis omnis voluptas magnam tenetur.","state":"opened","iid":9,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"issue_assignees":[],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"notes":[{"id":359,"note":"Quo eius velit quia et id quam.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.009Z","updated_at":"2016-06-14T15:02:48.009Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":360,"note":"Nulla commodi ratione cumque id autem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.032Z","updated_at":"2016-06-14T15:02:48.032Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":361,"note":"Illum non ea sed dolores corrupti.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.056Z","updated_at":"2016-06-14T15:02:48.056Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":362,"note":"Facere dolores ipsum dolorum maiores omnis occaecati ab.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.082Z","updated_at":"2016-06-14T15:02:48.082Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":363,"note":"Quod laudantium similique sint aut est ducimus.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.113Z","updated_at":"2016-06-14T15:02:48.113Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":364,"note":"Aut omnis eos esse incidunt vero reiciendis.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.139Z","updated_at":"2016-06-14T15:02:48.139Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":365,"note":"Beatae dolore et doloremque asperiores sunt.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.162Z","updated_at":"2016-06-14T15:02:48.162Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":366,"note":"Doloribus ipsam ex delectus rerum libero recusandae modi repellendus.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.192Z","updated_at":"2016-06-14T15:02:48.192Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]} {"id":38,"title":"Quasi adipisci non cupiditate dolorem quo qui earum sed.","author_id":6,"project_id":5,"created_at":"2016-06-14T15:02:08.154Z","updated_at":"2016-06-14T15:02:48.614Z","position":0,"branch_name":null,"description":"Ea recusandae neque autem tempora.","state":"closed","iid":8,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"label_links":[{"id":99,"label_id":2,"target_id":38,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"notes":[{"id":367,"note":"Accusantium fugiat et eaque quisquam esse corporis.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.235Z","updated_at":"2016-06-14T15:02:48.235Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":368,"note":"Ea labore eum nam qui laboriosam.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.261Z","updated_at":"2016-06-14T15:02:48.261Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":369,"note":"Accusantium quis sed molestiae et.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.294Z","updated_at":"2016-06-14T15:02:48.294Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":370,"note":"Corporis numquam a voluptatem pariatur asperiores dolorem delectus autem.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.523Z","updated_at":"2016-06-14T15:02:48.523Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":371,"note":"Ea accusantium maxime voluptas rerum.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.546Z","updated_at":"2016-06-14T15:02:48.546Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":372,"note":"Pariatur iusto et et excepturi similique ipsam eum.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.569Z","updated_at":"2016-06-14T15:02:48.569Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":373,"note":"Aliquam et culpa officia iste eius.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.591Z","updated_at":"2016-06-14T15:02:48.591Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":374,"note":"Ab id velit id unde laborum.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.613Z","updated_at":"2016-06-14T15:02:48.613Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]} {"id":37,"title":"Cupiditate quo aut ducimus minima molestiae vero numquam possimus.","author_id":20,"project_id":5,"created_at":"2016-06-14T15:02:08.051Z","updated_at":"2016-06-14T15:02:48.854Z","position":0,"branch_name":null,"description":"Maiores architecto quos in dolorem.","state":"opened","iid":7,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"notes":[{"id":375,"note":"Quasi fugit qui sed eligendi aut quia.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.647Z","updated_at":"2016-06-14T15:02:48.647Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":376,"note":"Esse nesciunt voluptatem ex vero est consequatur.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.674Z","updated_at":"2016-06-14T15:02:48.674Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":377,"note":"Similique qui quas non aut et velit sequi in.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.696Z","updated_at":"2016-06-14T15:02:48.696Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":378,"note":"Eveniet ut cupiditate repellendus numquam in esse eius.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.720Z","updated_at":"2016-06-14T15:02:48.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":379,"note":"Velit est dolorem adipisci rerum sed iure.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.755Z","updated_at":"2016-06-14T15:02:48.755Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":380,"note":"Voluptatem ullam ab ut illo ut quo.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.793Z","updated_at":"2016-06-14T15:02:48.793Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":381,"note":"Voluptatem impedit beatae quasi ipsa earum consectetur.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.823Z","updated_at":"2016-06-14T15:02:48.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":382,"note":"Nihil officiis eaque incidunt sunt voluptatum excepturi.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.852Z","updated_at":"2016-06-14T15:02:48.852Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]} diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson index 3388e315922..887d7ab658b 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson +++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson @@ -1,4 +1,4 @@ -{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"

something else entirely

","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}]} +{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n
  • 16ea4e20...074a2a32 - 2 commits from branch master
  • ca223a02 - readme: fix typos
\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"

something else entirely

","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}],"resource_milestone_events":[{"user_id":1,"action":"add","state":"opened","created_at":"2022-08-17T13:06:53.547Z","milestone":{"title":"v4.0","description":"Totam quam laborum id magnam natus eaque aspernatur.","created_at":"2016-06-14T15:02:04.590Z","updated_at":"2016-06-14T15:02:04.590Z","state":"active","iid":5}}],"resource_state_events":[{"user_id":1,"created_at":"2022-08-17T13:08:16.838Z","state":"closed","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false},{"user_id":1,"created_at":"2022-08-17T13:08:17.702Z","state":"reopened","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false}]} {"id":26,"target_branch":"master","source_branch":"feature","source_project_id":4,"author_id":1,"assignee_id":null,"title":"MR2","created_at":"2016-06-14T15:02:36.418Z","updated_at":"2016-06-14T15:02:57.013Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":8,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":679,"note":"Qui rerum totam nisi est.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.848Z","updated_at":"2016-06-14T15:02:56.848Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":680,"note":"Pariatur magni corrupti consequatur debitis minima error beatae voluptatem.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.871Z","updated_at":"2016-06-14T15:02:56.871Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":681,"note":"Qui quis ut modi eos rerum ratione.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.895Z","updated_at":"2016-06-14T15:02:56.895Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":682,"note":"Illum quidem expedita mollitia fugit.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.918Z","updated_at":"2016-06-14T15:02:56.918Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":683,"note":"Consectetur voluptate sit sint possimus veritatis quod.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.942Z","updated_at":"2016-06-14T15:02:56.942Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":684,"note":"Natus libero quibusdam rem assumenda deleniti accusamus sed earum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.966Z","updated_at":"2016-06-14T15:02:56.966Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":685,"note":"Tenetur autem nihil rerum odit.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.989Z","updated_at":"2016-06-14T15:02:56.989Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":686,"note":"Quia maiores et odio sed.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:57.012Z","updated_at":"2016-06-14T15:02:57.012Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":26,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":26,"sha":"0b4bc9a49b562e85de7cc9e834518ea6828729b9","relative_order":0,"message":"Feature added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:26:01.000+01:00","committed_date":"2014-02-27T09:26:01.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":26,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":26,"created_at":"2016-06-14T15:02:36.421Z","updated_at":"2016-06-14T15:02:36.474Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"1"},"events":[{"id":222,"target_type":"MergeRequest","target_id":26,"project_id":36,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1},{"id":186,"target_type":"MergeRequest","target_id":26,"project_id":5,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1}],"merge_request_assignees":[],"merge_request_reviewers":[],"approvals":[]} {"id":15,"target_branch":"test-7","source_branch":"test-1","source_project_id":5,"author_id":22,"assignee_id":16,"title":"Qui accusantium et inventore facilis doloribus occaecati officiis.","created_at":"2016-06-14T15:02:25.168Z","updated_at":"2016-06-14T15:02:59.521Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":7,"description":"Et commodi deserunt aspernatur vero rerum. Ut non dolorum alias in odit est libero. Voluptatibus eos in et vitae repudiandae facilis ex mollitia.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":777,"note":"Pariatur voluptas placeat aspernatur culpa suscipit soluta.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.348Z","updated_at":"2016-06-14T15:02:59.348Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":778,"note":"Alias et iure mollitia suscipit molestiae voluptatum nostrum asperiores.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.372Z","updated_at":"2016-06-14T15:02:59.372Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":779,"note":"Laudantium qui eum qui sunt.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.395Z","updated_at":"2016-06-14T15:02:59.395Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":780,"note":"Quas rem est iusto ut delectus fugiat recusandae mollitia.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.418Z","updated_at":"2016-06-14T15:02:59.418Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":781,"note":"Repellendus ab et qui nesciunt.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.444Z","updated_at":"2016-06-14T15:02:59.444Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":782,"note":"Non possimus voluptatum odio qui ut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.469Z","updated_at":"2016-06-14T15:02:59.469Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":783,"note":"Dolores repellendus eum ducimus quam ab dolorem quia.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.494Z","updated_at":"2016-06-14T15:02:59.494Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":784,"note":"Facilis dolorem aut corrupti id ratione occaecati.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.520Z","updated_at":"2016-06-14T15:02:59.520Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":15,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":15,"relative_order":0,"sha":"94b8d581c48d894b86661718582fecbc5e3ed2eb","message":"fixes #10\n","authored_date":"2016-01-19T13:22:56.000+01:00","committed_date":"2016-01-19T13:22:56.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}}],"merge_request_diff_files":[{"merge_request_diff_id":15,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":15,"created_at":"2016-06-14T15:02:25.171Z","updated_at":"2016-06-14T15:02:25.230Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":223,"target_type":"MergeRequest","target_id":15,"project_id":36,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":1},{"id":175,"target_type":"MergeRequest","target_id":15,"project_id":5,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":22}]} {"id":14,"target_branch":"fix","source_branch":"test-3","source_project_id":5,"author_id":20,"assignee_id":20,"title":"In voluptas aut sequi voluptatem ullam vel corporis illum consequatur.","created_at":"2016-06-14T15:02:24.760Z","updated_at":"2016-06-14T15:02:59.749Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":6,"description":"Dicta magnam non voluptates nam dignissimos nostrum deserunt. Dolorum et suscipit iure quae doloremque. Necessitatibus saepe aut labore sed.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":785,"note":"Atque cupiditate necessitatibus deserunt minus natus odit.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.559Z","updated_at":"2016-06-14T15:02:59.559Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":786,"note":"Non dolorem provident mollitia nesciunt optio ex eveniet.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.587Z","updated_at":"2016-06-14T15:02:59.587Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":787,"note":"Similique officia nemo quasi commodi accusantium quae qui.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.621Z","updated_at":"2016-06-14T15:02:59.621Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":788,"note":"Et est et alias ad dolor qui.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.650Z","updated_at":"2016-06-14T15:02:59.650Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":789,"note":"Numquam temporibus ratione voluptatibus aliquid.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.675Z","updated_at":"2016-06-14T15:02:59.675Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":790,"note":"Ut ex aliquam consectetur perferendis est hic aut quia.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.703Z","updated_at":"2016-06-14T15:02:59.703Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":791,"note":"Esse eos quam quaerat aut ut asperiores officiis.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.726Z","updated_at":"2016-06-14T15:02:59.726Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":792,"note":"Sint facilis accusantium iure blanditiis.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.748Z","updated_at":"2016-06-14T15:02:59.748Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":14,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":14,"relative_order":0,"sha":"ddd4ff416a931589c695eb4f5b23f844426f6928","message":"fixes #10\n","authored_date":"2016-01-19T14:14:43.000+01:00","committed_date":"2016-01-19T14:14:43.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":14,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","committed_date":"2015-12-07T12:52:12.000+01:00","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","committed_date":"2015-12-07T11:54:28.000+01:00","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":14,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","committed_date":"2015-11-13T16:27:12.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","committed_date":"2015-11-13T08:50:17.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","committed_date":"2015-11-13T08:39:43.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","committed_date":"2015-11-13T07:21:40.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","committed_date":"2015-11-13T06:01:27.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","committed_date":"2015-11-13T06:00:16.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","committed_date":"2015-11-13T05:23:14.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","committed_date":"2015-11-13T05:08:45.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","committed_date":"2015-11-13T05:08:04.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","committed_date":"2015-08-25T17:53:12.000+02:00","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":14,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","committed_date":"2015-01-10T22:23:29.000+01:00","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","committed_date":"2015-01-10T21:28:18.000+01:00","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}},{"merge_request_diff_id":14,"relative_order":15,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":16,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":17,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":18,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":19,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":14,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":14,"created_at":"2016-06-14T15:02:24.770Z","updated_at":"2016-06-14T15:02:25.007Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":224,"target_type":"MergeRequest","target_id":14,"project_id":36,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":1},{"id":174,"target_type":"MergeRequest","target_id":14,"project_id":5,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":20}]} diff --git a/spec/frontend/batch_comments/components/submit_dropdown_spec.js b/spec/frontend/batch_comments/components/submit_dropdown_spec.js index 4f5ff797230..dc7ecb8e44d 100644 --- a/spec/frontend/batch_comments/components/submit_dropdown_spec.js +++ b/spec/frontend/batch_comments/components/submit_dropdown_spec.js @@ -8,7 +8,7 @@ Vue.use(Vuex); let wrapper; let publishReview; -function factory() { +function factory({ canApprove = true } = {}) { publishReview = jest.fn(); const store = new Vuex.Store({ @@ -17,7 +17,11 @@ function factory() { markdownDocsPath: '/markdown/docs', quickActionsDocsPath: '/quickactions/docs', }), - getNoteableData: () => ({ id: 1, preview_note_path: '/preview' }), + getNoteableData: () => ({ + id: 1, + preview_note_path: '/preview', + current_user: { can_approve: canApprove }, + }), noteableType: () => 'merge_request', }, modules: { @@ -54,6 +58,8 @@ describe('Batch comments submit dropdown', () => { noteable_type: 'merge_request', noteable_id: 1, note: 'Hello world', + approve: false, + approval_password: '', }); }); @@ -66,4 +72,14 @@ describe('Batch comments submit dropdown', () => { expect(findSubmitButton().props('loading')).toBe(true); }); + + it.each` + canApprove | exists | existsText + ${true} | ${true} | ${'shows'} + ${false} | ${false} | ${'hides'} + `('it $existsText approve checkbox if can_approve is $canApprove', ({ canApprove, exists }) => { + factory({ canApprove }); + + expect(wrapper.findByTestId('approve_merge_request').exists()).toBe(exists); + }); }); diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js index 9f50b12bac2..6369ea9aa15 100644 --- a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js +++ b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js @@ -180,6 +180,7 @@ describe('Batch comments store actions', () => { }); it('calls service with notes data', () => { + mock.onAny().reply(200); jest.spyOn(axios, 'post'); return actions @@ -192,7 +193,7 @@ describe('Batch comments store actions', () => { it('dispatches error commits', () => { mock.onAny().reply(500); - return actions.publishReview({ dispatch, commit, getters, rootGetters }).then(() => { + return actions.publishReview({ dispatch, commit, getters, rootGetters }).catch(() => { expect(commit.mock.calls[0]).toEqual(['REQUEST_PUBLISH_REVIEW']); expect(commit.mock.calls[1]).toEqual(['RECEIVE_PUBLISH_REVIEW_ERROR']); }); diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js index 3eaf06e0656..34784a88ba9 100644 --- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js @@ -30,16 +30,10 @@ import * as Api from '~/pipelines/components/graph_shared/api'; import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue'; import * as parsingUtils from '~/pipelines/components/parsing_utils'; import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql'; -import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql'; import * as sentryUtils from '~/pipelines/utils'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import { mockRunningPipelineHeaderData } from '../mock_data'; -import { - mapCallouts, - mockCalloutsResponse, - mockPipelineResponse, - mockPerformanceInsightsResponse, -} from './mock_data'; +import { mapCallouts, mockCalloutsResponse, mockPipelineResponse } from './mock_data'; const defaultProvide = { graphqlResourceEtag: 'frog/amphibirama/etag/', @@ -95,15 +89,11 @@ describe('Pipeline graph wrapper', () => { const callouts = mapCallouts(calloutsList); const getUserCalloutsHandler = jest.fn().mockResolvedValue(mockCalloutsResponse(callouts)); const getPipelineHeaderDataHandler = jest.fn().mockResolvedValue(mockRunningPipelineHeaderData); - const getPerformanceInsightsHandler = jest - .fn() - .mockResolvedValue(mockPerformanceInsightsResponse); const requestHandlers = [ [getPipelineHeaderData, getPipelineHeaderDataHandler], [getPipelineDetails, getPipelineDetailsHandler], [getUserCallouts, getUserCalloutsHandler], - [getPerformanceInsights, getPerformanceInsightsHandler], ]; const apolloProvider = createMockApollo(requestHandlers); diff --git a/spec/frontend/pipelines/graph/graph_view_selector_spec.js b/spec/frontend/pipelines/graph/graph_view_selector_spec.js index 1397500bdc7..4e79c7e73cc 100644 --- a/spec/frontend/pipelines/graph/graph_view_selector_spec.js +++ b/spec/frontend/pipelines/graph/graph_view_selector_spec.js @@ -1,19 +1,10 @@ import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui'; import { mount, shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants'; import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql'; -import { mockPerformanceInsightsResponse } from './mock_data'; - -Vue.use(VueApollo); describe('the graph view selector component', () => { let wrapper; - let trackingSpy; const findDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]'); const findViewTypeSelector = () => wrapper.findComponent(GlButtonGroup); @@ -22,13 +13,11 @@ describe('the graph view selector component', () => { const findSwitcherLoader = () => wrapper.find('[data-testid="switcher-loading-state"]'); const findToggleLoader = () => findDependenciesToggle().find(GlLoadingIcon); const findHoverTip = () => wrapper.findComponent(GlAlert); - const findPipelineInsightsBtn = () => wrapper.find('[data-testid="pipeline-insights-btn"]'); const defaultProps = { showLinks: false, tipPreviouslyDismissed: false, type: STAGE_VIEW, - isPipelineComplete: true, }; const defaultData = { @@ -38,14 +27,6 @@ describe('the graph view selector component', () => { showLinksActive: false, }; - const getPerformanceInsightsHandler = jest - .fn() - .mockResolvedValue(mockPerformanceInsightsResponse); - - const requestHandlers = [[getPerformanceInsights, getPerformanceInsightsHandler]]; - - const apolloProvider = createMockApollo(requestHandlers); - const createComponent = ({ data = {}, mountFn = shallowMount, props = {} } = {}) => { wrapper = mountFn(GraphViewSelector, { propsData: { @@ -58,7 +39,6 @@ describe('the graph view selector component', () => { ...data, }; }, - apolloProvider, }); }; @@ -222,44 +202,5 @@ describe('the graph view selector component', () => { expect(findHoverTip().exists()).toBe(false); }); }); - - describe('pipeline insights', () => { - it.each` - isPipelineComplete | shouldShow - ${true} | ${true} - ${false} | ${false} - `( - 'button should display $shouldShow if isPipelineComplete is $isPipelineComplete ', - ({ isPipelineComplete, shouldShow }) => { - createComponent({ - props: { - isPipelineComplete, - }, - }); - - expect(findPipelineInsightsBtn().exists()).toBe(shouldShow); - }, - ); - }); - - describe('tracking', () => { - beforeEach(() => { - createComponent(); - - trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); - }); - - afterEach(() => { - unmockTracking(); - }); - - it('tracks performance insights button click', () => { - findPipelineInsightsBtn().vm.$emit('click'); - - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_insights_button', { - label: 'performance_insights', - }); - }); - }); }); }); diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js index 959bbcefc98..6124d67af09 100644 --- a/spec/frontend/pipelines/graph/mock_data.js +++ b/spec/frontend/pipelines/graph/mock_data.js @@ -1038,245 +1038,3 @@ export const triggerJob = { action: null, }, }; - -export const mockPerformanceInsightsResponse = { - data: { - project: { - __typename: 'Project', - id: 'gid://gitlab/Project/20', - pipeline: { - __typename: 'Pipeline', - id: 'gid://gitlab/Ci::Pipeline/97', - jobs: { - __typename: 'CiJobConnection', - pageInfo: { - __typename: 'PageInfo', - hasNextPage: false, - }, - nodes: [ - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Bridge/2502', - duration: null, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2502-2502', - detailsPath: '/root/lots-of-jobs-project/-/pipelines/98', - }, - name: 'trigger_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/303', - name: 'deploy', - }, - startedAt: null, - queuedDuration: 424850.376278, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2501', - duration: 10, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2501-2501', - detailsPath: '/root/ci-project/-/jobs/2501', - }, - name: 'artifact_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/303', - name: 'deploy', - }, - startedAt: '2022-07-01T16:31:41Z', - queuedDuration: 2.621553, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2500', - duration: 4, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2500-2500', - detailsPath: '/root/ci-project/-/jobs/2500', - }, - name: 'coverage_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/302', - name: 'test', - }, - startedAt: '2022-07-01T16:31:33Z', - queuedDuration: 14.388869, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2499', - duration: 4, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2499-2499', - detailsPath: '/root/ci-project/-/jobs/2499', - }, - name: 'test_job_two', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/302', - name: 'test', - }, - startedAt: '2022-07-01T16:31:28Z', - queuedDuration: 15.792664, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2498', - duration: 4, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2498-2498', - detailsPath: '/root/ci-project/-/jobs/2498', - }, - name: 'test_job_one', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/302', - name: 'test', - }, - startedAt: '2022-07-01T16:31:17Z', - queuedDuration: 8.317072, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2497', - duration: 5, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'failed-2497-2497', - detailsPath: '/root/ci-project/-/jobs/2497', - }, - name: 'allow_failure_test_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/302', - name: 'test', - }, - startedAt: '2022-07-01T16:31:22Z', - queuedDuration: 3.547553, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2496', - duration: null, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'manual-2496-2496', - detailsPath: '/root/ci-project/-/jobs/2496', - }, - name: 'test_manual_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/302', - name: 'test', - }, - startedAt: null, - queuedDuration: null, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2495', - duration: 5, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2495-2495', - detailsPath: '/root/ci-project/-/jobs/2495', - }, - name: 'large_log_output', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/301', - name: 'build', - }, - startedAt: '2022-07-01T16:31:11Z', - queuedDuration: 79.128625, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2494', - duration: 5, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2494-2494', - detailsPath: '/root/ci-project/-/jobs/2494', - }, - name: 'build_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/301', - name: 'build', - }, - startedAt: '2022-07-01T16:31:05Z', - queuedDuration: 73.286895, - }, - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Build/2493', - duration: 16, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2493-2493', - detailsPath: '/root/ci-project/-/jobs/2493', - }, - name: 'wait_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/301', - name: 'build', - }, - startedAt: '2022-07-01T16:30:48Z', - queuedDuration: 56.258856, - }, - ], - }, - }, - }, - }, -}; - -export const mockPerformanceInsightsNextPageResponse = { - data: { - project: { - __typename: 'Project', - id: 'gid://gitlab/Project/20', - pipeline: { - __typename: 'Pipeline', - id: 'gid://gitlab/Ci::Pipeline/97', - jobs: { - __typename: 'CiJobConnection', - pageInfo: { - __typename: 'PageInfo', - hasNextPage: true, - }, - nodes: [ - { - __typename: 'CiJob', - id: 'gid://gitlab/Ci::Bridge/2502', - duration: null, - detailedStatus: { - __typename: 'DetailedStatus', - id: 'success-2502-2502', - detailsPath: '/root/lots-of-jobs-project/-/pipelines/98', - }, - name: 'trigger_job', - stage: { - __typename: 'CiStage', - id: 'gid://gitlab/Ci::Stage/303', - name: 'deploy', - }, - startedAt: null, - queuedDuration: 424850.376278, - }, - ], - }, - }, - }, - }, -}; diff --git a/spec/frontend/pipelines/performance_insights_modal_spec.js b/spec/frontend/pipelines/performance_insights_modal_spec.js deleted file mode 100644 index 8c802be7718..00000000000 --- a/spec/frontend/pipelines/performance_insights_modal_spec.js +++ /dev/null @@ -1,131 +0,0 @@ -import { GlAlert, GlLink, GlModal } from '@gitlab/ui'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import waitForPromises from 'helpers/wait_for_promises'; -import PerformanceInsightsModal from '~/pipelines/components/performance_insights_modal.vue'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { trimText } from 'helpers/text_helper'; -import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql'; -import { - mockPerformanceInsightsResponse, - mockPerformanceInsightsNextPageResponse, -} from './graph/mock_data'; - -Vue.use(VueApollo); - -describe('Performance insights modal', () => { - let wrapper; - - const findModal = () => wrapper.findComponent(GlModal); - const findAlert = () => wrapper.findComponent(GlAlert); - const findLink = () => wrapper.findComponent(GlLink); - const findLimitText = () => wrapper.findByTestId('limit-alert-text'); - const findQueuedCardData = () => wrapper.findByTestId('insights-queued-card-data'); - const findQueuedCardLink = () => wrapper.findByTestId('insights-queued-card-link'); - const findExecutedCardData = () => wrapper.findByTestId('insights-executed-card-data'); - const findExecutedCardLink = () => wrapper.findByTestId('insights-executed-card-link'); - const findSlowJobsStage = (index) => wrapper.findAllByTestId('insights-slow-job-stage').at(index); - const findSlowJobsLink = (index) => wrapper.findAllByTestId('insights-slow-job-link').at(index); - - const getPerformanceInsightsHandler = jest - .fn() - .mockResolvedValue(mockPerformanceInsightsResponse); - - const getPerformanceInsightsNextPageHandler = jest - .fn() - .mockResolvedValue(mockPerformanceInsightsNextPageResponse); - - const requestHandlers = [[getPerformanceInsights, getPerformanceInsightsHandler]]; - - const createComponent = (handlers = requestHandlers) => { - wrapper = shallowMountExtended(PerformanceInsightsModal, { - provide: { - pipelineIid: '1', - pipelineProjectPath: 'root/ci-project', - }, - apolloProvider: createMockApollo(handlers), - }); - }; - - afterEach(() => { - wrapper.destroy(); - }); - - describe('without next page', () => { - beforeEach(async () => { - createComponent(); - - await waitForPromises(); - }); - - it('displays modal', () => { - expect(findModal().exists()).toBe(true); - }); - - it('displays alert', () => { - expect(findAlert().exists()).toBe(true); - }); - - it('displays feedback issue link', () => { - expect(findLink().text()).toBe('Feedback issue'); - expect(findLink().attributes('href')).toBe( - 'https://gitlab.com/gitlab-org/gitlab/-/issues/365902', - ); - }); - - it('does not display limit text', () => { - expect(findLimitText().exists()).toBe(false); - }); - - describe('queued duration card', () => { - it('displays card data', () => { - expect(trimText(findQueuedCardData().text())).toBe('4.9 days'); - }); - it('displays card link', () => { - expect(findQueuedCardLink().attributes('href')).toBe( - '/root/lots-of-jobs-project/-/pipelines/98', - ); - }); - }); - - describe('executed duration card', () => { - it('displays card data', () => { - expect(trimText(findExecutedCardData().text())).toBe('trigger_job'); - }); - it('displays card link', () => { - expect(findExecutedCardLink().attributes('href')).toBe( - '/root/lots-of-jobs-project/-/pipelines/98', - ); - }); - }); - - describe('slow jobs', () => { - it.each` - index | expectedStage | expectedName | expectedLink - ${0} | ${'build'} | ${'wait_job'} | ${'/root/ci-project/-/jobs/2493'} - ${1} | ${'deploy'} | ${'artifact_job'} | ${'/root/ci-project/-/jobs/2501'} - ${2} | ${'test'} | ${'allow_failure_test_job'} | ${'/root/ci-project/-/jobs/2497'} - ${3} | ${'build'} | ${'large_log_output'} | ${'/root/ci-project/-/jobs/2495'} - ${4} | ${'build'} | ${'build_job'} | ${'/root/ci-project/-/jobs/2494'} - `( - 'should display slow job correctly', - ({ index, expectedStage, expectedName, expectedLink }) => { - expect(findSlowJobsStage(index).text()).toBe(expectedStage); - expect(findSlowJobsLink(index).text()).toBe(expectedName); - expect(findSlowJobsLink(index).attributes('href')).toBe(expectedLink); - }, - ); - }); - }); - - describe('with next page', () => { - it('displays limit text when there is a next page', async () => { - createComponent([[getPerformanceInsights, getPerformanceInsightsNextPageHandler]]); - - await waitForPromises(); - - expect(findLimitText().exists()).toBe(true); - }); - }); -}); diff --git a/spec/frontend/pipelines/utils_spec.js b/spec/frontend/pipelines/utils_spec.js index a82390fae22..1c23a7e4fcf 100644 --- a/spec/frontend/pipelines/utils_spec.js +++ b/spec/frontend/pipelines/utils_spec.js @@ -8,14 +8,10 @@ import { removeOrphanNodes, getMaxNodes, } from '~/pipelines/components/parsing_utils'; -import { createNodeDict, calculateJobStats, calculateSlowestFiveJobs } from '~/pipelines/utils'; +import { createNodeDict } from '~/pipelines/utils'; import { mockParsedGraphQLNodes, missingJob } from './components/dag/mock_data'; -import { - generateResponse, - mockPipelineResponse, - mockPerformanceInsightsResponse, -} from './graph/mock_data'; +import { generateResponse, mockPipelineResponse } from './graph/mock_data'; describe('DAG visualization parsing utilities', () => { const nodeDict = createNodeDict(mockParsedGraphQLNodes); @@ -162,40 +158,4 @@ describe('DAG visualization parsing utilities', () => { expect(columns).toMatchSnapshot(); }); }); - - describe('performance insights', () => { - const { - data: { - project: { - pipeline: { jobs }, - }, - }, - } = mockPerformanceInsightsResponse; - - describe('calculateJobStats', () => { - const expectedJob = jobs.nodes[0]; - - it('returns the job that spent this longest time queued', () => { - expect(calculateJobStats(jobs, 'queuedDuration')).toEqual(expectedJob); - }); - - it('returns the job that was executed last', () => { - expect(calculateJobStats(jobs, 'startedAt')).toEqual(expectedJob); - }); - }); - - describe('calculateSlowestFiveJobs', () => { - it('returns the slowest five jobs of the pipeline', () => { - const expectedJobs = [ - jobs.nodes[9], - jobs.nodes[1], - jobs.nodes[5], - jobs.nodes[7], - jobs.nodes[8], - ]; - - expect(calculateSlowestFiveJobs(jobs)).toEqual(expectedJobs); - }); - }); - }); }); diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb index bc9e64282bc..b3dee082d1f 100644 --- a/spec/graphql/types/ci/job_type_spec.rb +++ b/spec/graphql/types/ci/job_type_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Types::Ci::JobType do + include GraphqlHelpers + specify { expect(described_class.graphql_name).to eq('CiJob') } specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::Job) } @@ -45,8 +47,21 @@ RSpec.describe Types::Ci::JobType do tags triggered userPermissions + webPath ] expect(described_class).to have_graphql_fields(*expected_fields) end + + describe '#web_path' do + subject { resolve_field(:web_path, build, current_user: user, object_type: described_class) } + + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:build) { create(:ci_build, project: project, user: user) } + + it 'returns the web path of the job' do + is_expected.to eq("/#{project.full_path}/-/jobs/#{build.id}") + end + end end diff --git a/spec/graphql/types/deployment_details_type_spec.rb b/spec/graphql/types/deployment_details_type_spec.rb index 58756798ffb..4cbc4165f70 100644 --- a/spec/graphql/types/deployment_details_type_spec.rb +++ b/spec/graphql/types/deployment_details_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['DeploymentDetails'] do it 'has the expected fields' do expected_fields = %w[ - id iid ref tag sha created_at updated_at finished_at status + id iid ref tag sha created_at updated_at finished_at status commit job triggerer ] expect(described_class).to have_graphql_fields(*expected_fields) diff --git a/spec/graphql/types/deployment_type_spec.rb b/spec/graphql/types/deployment_type_spec.rb index 21e445c24b2..bf4be0523c6 100644 --- a/spec/graphql/types/deployment_type_spec.rb +++ b/spec/graphql/types/deployment_type_spec.rb @@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['Deployment'] do it 'has the expected fields' do expected_fields = %w[ - id iid ref tag sha created_at updated_at finished_at status + id iid ref tag sha created_at updated_at finished_at status commit job triggerer ] expect(described_class).to have_graphql_fields(*expected_fields) diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb index 0304aac18ae..ba6caf7cf17 100644 --- a/spec/helpers/application_settings_helper_spec.rb +++ b/spec/helpers/application_settings_helper_spec.rb @@ -46,6 +46,10 @@ RSpec.describe ApplicationSettingsHelper do expect(helper.visible_attributes).to include(:deactivate_dormant_users) end + it 'contains :deactivate_dormant_users_period' do + expect(helper.visible_attributes).to include(:deactivate_dormant_users_period) + end + it 'contains rate limit parameters' do expect(helper.visible_attributes).to include(*%i( issues_create_limit notes_create_limit project_export_limit @@ -63,6 +67,10 @@ RSpec.describe ApplicationSettingsHelper do it 'does not contain :deactivate_dormant_users' do expect(helper.visible_attributes).not_to include(:deactivate_dormant_users) end + + it 'does not contain :deactivate_dormant_users_period' do + expect(helper.visible_attributes).not_to include(:deactivate_dormant_users_period) + end end end diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb index b0c67cdafe1..690396d4dbc 100644 --- a/spec/lib/gitlab/encoding_helper_spec.rb +++ b/spec/lib/gitlab/encoding_helper_spec.rb @@ -98,6 +98,36 @@ RSpec.describe Gitlab::EncodingHelper do end end + describe '#encode_utf8_with_escaping!' do + where(:input, :expected) do + "abcd" | "abcd" + "DzDzDz" | "DzDzDz" + "\xC7\xB2\xC7DzDzDz" | "Dz%C7DzDzDz" + "🐤🐤🐤🐤\xF0\x9F\x90" | "🐤🐤🐤🐤%F0%9F%90" + "\xD0\x9F\xD1\x80 \x90" | "Пр %90" + "\x41" | "A" + end + + with_them do + it 'escapes invalid UTF-8' do + expect(ext_class.encode_utf8_with_escaping!(input.dup.force_encoding(Encoding::ASCII_8BIT))).to eq(expected) + expect(ext_class.encode_utf8_with_escaping!(input)).to eq(expected) + end + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(escape_gitaly_refs: false) + end + + it 'uses #encode! method' do + expect(ext_class).to receive(:encode!).with('String') + + ext_class.encode_utf8_with_escaping!('String') + end + end + end + describe '#encode_utf8' do [ ["nil", nil, nil], diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index a1fb8b70bd7..b35d1ea019b 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -673,6 +673,61 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do end end + describe '#search_files_by_name' do + let(:ref) { 'master' } + + subject(:result) { mutable_repository.search_files_by_name(query, ref) } + + context 'when sending a valid name' do + let(:query) { 'files/ruby/popen.rb' } + + it 'returns matched files' do + expect(result).to contain_exactly('files/ruby/popen.rb') + end + end + + context 'when sending a name with space' do + let(:query) { 'file with space.md' } + + before do + mutable_repository.multi_action( + user, + actions: [{ action: :create, file_path: "file with space.md", content: "Test content" }], + branch_name: ref, message: "Test" + ) + end + + it 'returns matched files' do + expect(result).to contain_exactly('file with space.md') + end + end + + context 'when sending a name with special ASCII characters' do + let(:file_name) { 'Hello !@#$%^&*()' } + let(:query) { file_name } + + before do + mutable_repository.multi_action( + user, + actions: [{ action: :create, file_path: file_name, content: "Test content" }], + branch_name: ref, message: "Test" + ) + end + + it 'returns matched files' do + expect(result).to contain_exactly(file_name) + end + end + + context 'when sending a non-existing name' do + let(:query) { 'please do not exist.md' } + + it 'raises error' do + expect(result).to eql([]) + end + end + end + describe '#find_remote_root_ref' do it 'gets the remote root ref from GitalyClient' do expect_any_instance_of(Gitlab::GitalyClient::RemoteService) diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb index f359679a930..0f6ef55b4b1 100644 --- a/spec/lib/gitlab/git_spec.rb +++ b/spec/lib/gitlab/git_spec.rb @@ -7,10 +7,18 @@ RSpec.describe Gitlab::Git do let(:committer_name) { 'John Doe' } describe '.ref_name' do - it 'ensure ref is a valid UTF-8 string' do - utf8_invalid_ref = Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5" + let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5" } - expect(described_class.ref_name(utf8_invalid_ref)).to eq("an_invalid_ref_å") + it 'ensure ref is a valid UTF-8 string' do + expect(described_class.ref_name(ref)).to eq("an_invalid_ref_%E5") + end + + context 'when ref contains characters \x80 - \xFF' do + let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "\x90" } + + it 'correctly converts it' do + expect(described_class.ref_name(ref)).to eq("%90") + end end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index c0be5b01cef..835199e0e3d 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -819,3 +819,14 @@ service_desk_setting: approvals: - user - merge_request +resource_milestone_events: + - user + - issue + - merge_request + - milestone +resource_state_events: + - user + - issue + - merge_request + - source_merge_request + - epic diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb index 47d7555c8f4..299e107c881 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -192,10 +192,26 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do expect(Issue.find_by(title: 'Voluptatem').resource_label_events).not_to be_empty end + it 'restores issue resource milestone events' do + expect(Issue.find_by(title: 'Voluptatem').resource_milestone_events).not_to be_empty + end + + it 'restores issue resource state events' do + expect(Issue.find_by(title: 'Voluptatem').resource_state_events).not_to be_empty + end + it 'restores merge requests resource label events' do expect(MergeRequest.find_by(title: 'MR1').resource_label_events).not_to be_empty end + it 'restores merge request resource milestone events' do + expect(MergeRequest.find_by(title: 'MR1').resource_milestone_events).not_to be_empty + end + + it 'restores merge request resource state events' do + expect(MergeRequest.find_by(title: 'MR1').resource_state_events).not_to be_empty + end + it 'restores suggestion' do note = Note.find_by("note LIKE 'Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum%'") diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 6cfc24a8996..ac3997fd3ec 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -917,3 +917,15 @@ Approval: - user_id - created_at - updated_at +ResourceMilestoneEvent: + - user_id + - action + - state + - created_at +ResourceStateEvent: + - user_id + - created_at + - state + - source_commit + - close_after_error_tracking_resolve + - close_auto_resolve_prometheus_alert diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 4ac8720780c..8517e583ec7 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -20,6 +20,7 @@ RSpec.describe Ci::Trigger do trigger = create(:ci_trigger_without_token, project: project) expect(trigger.token).not_to be_nil + expect(trigger.token).to start_with(Ci::Trigger::TRIGGER_TOKEN_PREFIX) end it 'does not set a random token if one provided' do @@ -30,12 +31,22 @@ RSpec.describe Ci::Trigger do end describe '#short_token' do - let(:trigger) { create(:ci_trigger, token: '12345678') } + let(:trigger) { create(:ci_trigger) } subject { trigger.short_token } - it 'returns shortened token' do - is_expected.to eq('1234') + it 'returns shortened token without prefix' do + is_expected.not_to start_with(Ci::Trigger::TRIGGER_TOKEN_PREFIX) + end + + context 'token does not have a prefix' do + before do + trigger.token = '12345678' + end + + it 'returns shortened token' do + is_expected.to eq('1234') + end end end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index c5ce18739aa..2fdca7114eb 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -897,6 +897,22 @@ RSpec.describe Deployment do end end + describe '#build' do + let!(:deployment) { create(:deployment) } + + subject { deployment.build } + + it 'retrieves build for the deployment' do + is_expected.to eq(deployment.deployable) + end + + it 'returns nil when the associated build is not found' do + deployment.update!(deployable_id: nil, deployable_type: nil) + + is_expected.to be_nil + end + end + describe '#previous_deployment' do using RSpec::Parameterized::TableSyntax diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 537e680102a..8921b6d208a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3810,8 +3810,8 @@ RSpec.describe User do describe '#can_be_deactivated?' do let(:activity) { {} } let(:user) { create(:user, name: 'John Smith', **activity) } - let(:day_within_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.pred.days.ago } - let(:day_outside_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.next.days.ago } + let(:day_within_minium_inactive_days_threshold) { Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago } + let(:day_outside_minium_inactive_days_threshold) { Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago } shared_examples 'not eligible for deactivation' do it 'returns false' do @@ -7193,8 +7193,8 @@ RSpec.describe User do describe '.dormant' do it 'returns dormant users' do freeze_time do - not_that_long_ago = (described_class::MINIMUM_INACTIVE_DAYS - 1).days.ago.to_date - too_long_ago = described_class::MINIMUM_INACTIVE_DAYS.days.ago.to_date + not_that_long_ago = (Gitlab::CurrentSettings.deactivate_dormant_users_period - 1).days.ago.to_date + too_long_ago = Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date create(:user, :deactivated, last_activity_on: too_long_ago) @@ -7214,8 +7214,8 @@ RSpec.describe User do describe '.with_no_activity' do it 'returns users with no activity' do freeze_time do - active_not_that_long_ago = (described_class::MINIMUM_INACTIVE_DAYS - 1).days.ago.to_date - active_too_long_ago = described_class::MINIMUM_INACTIVE_DAYS.days.ago.to_date + active_not_that_long_ago = (Gitlab::CurrentSettings.deactivate_dormant_users_period - 1).days.ago.to_date + active_too_long_ago = Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date created_recently = (described_class::MINIMUM_DAYS_CREATED - 1).days.ago.to_date created_not_recently = described_class::MINIMUM_DAYS_CREATED.days.ago.to_date diff --git a/spec/requests/api/graphql/environments/deployments_query_spec.rb b/spec/requests/api/graphql/environments/deployments_query_spec.rb index fbfd9c5d0ac..95cc83e9463 100644 --- a/spec/requests/api/graphql/environments/deployments_query_spec.rb +++ b/spec/requests/api/graphql/environments/deployments_query_spec.rb @@ -295,6 +295,147 @@ RSpec.describe 'Environments Deployments query' do end end + shared_examples_for 'avoids N+1 database queries' do + it 'does not increase the query count' do + create_deployments + + baseline = ActiveRecord::QueryRecorder.new do + run_with_clean_state(query, context: { current_user: user }) + end + + create_deployments + + multi = ActiveRecord::QueryRecorder.new do + run_with_clean_state(query, context: { current_user: user }) + end + + expect(multi).not_to exceed_query_limit(baseline) + end + + def create_deployments + create_list(:deployment, 3, environment: environment, project: project).each do |deployment| + deployment.user = create(:user).tap { |u| project.add_developer(u) } + deployment.deployable = + create(:ci_build, project: project, environment: environment.name, deployment: deployment, + user: deployment.user) + + deployment.save! + end + end + end + + context 'when requesting commits of deployments' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environment(name: "#{environment.name}") { + deployments { + nodes { + iid + commit { + author { + avatarUrl + name + webPath + } + fullTitle + webPath + sha + } + } + } + } + } + } + ) + end + + it_behaves_like 'avoids N+1 database queries' + + it 'returns commits of deployments' do + deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes') + + deployments.each do |deployment| + deployment_in_record = project.deployments.find_by_iid(deployment['iid']) + + expect(deployment_in_record.sha).to eq(deployment['commit']['sha']) + end + end + end + + context 'when requesting triggerers of deployments' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environment(name: "#{environment.name}") { + deployments { + nodes { + iid + triggerer { + id + avatarUrl + name + webPath + } + } + } + } + } + } + ) + end + + it_behaves_like 'avoids N+1 database queries' + + it 'returns triggerers of deployments' do + deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes') + + deployments.each do |deployment| + deployment_in_record = project.deployments.find_by_iid(deployment['iid']) + + expect(deployment_in_record.deployed_by.name).to eq(deployment['triggerer']['name']) + end + end + end + + context 'when requesting jobs of deployments' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environment(name: "#{environment.name}") { + deployments { + nodes { + iid + job { + id + status + name + webPath + } + } + } + } + } + } + ) + end + + it_behaves_like 'avoids N+1 database queries' + + it 'returns jobs of deployments' do + deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes') + + deployments.each do |deployment| + deployment_in_record = project.deployments.find_by_iid(deployment['iid']) + + expect(deployment_in_record.build.to_global_id.to_s).to eq(deployment['job']['id']) + end + end + end + describe 'sorting and pagination' do let(:data_path) { [:project, :environment, :deployments] } let(:current_user) { user } diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 26238a87209..331d8261dcf 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -3238,7 +3238,7 @@ RSpec.describe API::Users do let(:user) { create(:user, **activity) } context 'with no recent activity' do - let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.next.days.ago } } + let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago } } it 'deactivates an active user' do deactivate @@ -3249,13 +3249,13 @@ RSpec.describe API::Users do end context 'with recent activity' do - let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.pred.days.ago } } + let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago } } it 'does not deactivate an active user' do deactivate expect(response).to have_gitlab_http_status(:forbidden) - expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated") + expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated") expect(user.reload.state).to eq('active') end end diff --git a/spec/workers/users/deactivate_dormant_users_worker_spec.rb b/spec/workers/users/deactivate_dormant_users_worker_spec.rb index 263ca31e0a0..a8318de669b 100644 --- a/spec/workers/users/deactivate_dormant_users_worker_spec.rb +++ b/spec/workers/users/deactivate_dormant_users_worker_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do using RSpec::Parameterized::TableSyntax describe '#perform' do - let_it_be(:dormant) { create(:user, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date) } + let_it_be(:dormant) { create(:user, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date) } let_it_be(:inactive) { create(:user, last_activity_on: nil, created_at: User::MINIMUM_DAYS_CREATED.days.ago.to_date) } let_it_be(:inactive_recently_created) { create(:user, last_activity_on: nil, created_at: (User::MINIMUM_DAYS_CREATED - 1).days.ago.to_date) } @@ -14,7 +14,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do it 'does not run for GitLab.com' do expect(Gitlab).to receive(:com?).and_return(true) - expect(Gitlab::CurrentSettings).not_to receive(:current_application_settings) + # Now makes a call to current settings to determine period of dormancy worker.perform @@ -48,7 +48,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do end with_them do it 'deactivates certain user types' do - user = create(:user, user_type: user_type, state: :active, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date) + user = create(:user, user_type: user_type, state: :active, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date) worker.perform @@ -57,8 +57,8 @@ RSpec.describe Users::DeactivateDormantUsersWorker do end it 'does not deactivate non-active users' do - human_user = create(:user, user_type: :human, state: :blocked, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date) - service_user = create(:user, user_type: :service_user, state: :blocked, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date) + human_user = create(:user, user_type: :human, state: :blocked, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date) + service_user = create(:user, user_type: :service_user, state: :blocked, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date) worker.perform