diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-05 21:08:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-05 21:08:19 +0300 |
commit | a8de96bff51846e160b76506dc0ca0fe6f767f64 (patch) | |
tree | 1036f1ca75aba492eaaa3439c84a3109b4684896 /app | |
parent | afe2b984524ae4b0c8a0636db7ec5b2c452f0734 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
26 files changed, 163 insertions, 41 deletions
diff --git a/app/assets/javascripts/blob/components/blob_edit_header.vue b/app/assets/javascripts/blob/components/blob_edit_header.vue new file mode 100644 index 00000000000..e9b5ceda479 --- /dev/null +++ b/app/assets/javascripts/blob/components/blob_edit_header.vue @@ -0,0 +1,35 @@ +<script> +import { GlFormInput } from '@gitlab/ui'; + +export default { + components: { + GlFormInput, + }, + props: { + value: { + type: String, + required: true, + }, + }, + data() { + return { + name: this.value, + }; + }, +}; +</script> +<template> + <div class="js-file-title file-title-flex-parent"> + <gl-form-input + id="snippet_file_name" + v-model="name" + :placeholder=" + s__('Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby') + " + name="snippet_file_name" + class="form-control js-snippet-file-name qa-snippet-file-name" + type="text" + @change="$emit('input', name)" + /> + </div> +</template> diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue index c7c5e0e20f1..012a4f4ad89 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -63,7 +63,9 @@ export default { methods: { toggleForm() { - this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen; + if (this.isEditable) { + this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen; + } }, updateLockedAttribute(locked) { this.mediator.service diff --git a/app/assets/javascripts/sidebar/queries/sidebarDetails.query.graphql b/app/assets/javascripts/sidebar/queries/sidebarDetails.query.graphql new file mode 100644 index 00000000000..8cc68f6ea9a --- /dev/null +++ b/app/assets/javascripts/sidebar/queries/sidebarDetails.query.graphql @@ -0,0 +1,7 @@ +query ($fullPath: ID!, $iid: String!) { + project (fullPath: $fullPath) { + issue (iid: $iid) { + iid + } + } +} diff --git a/app/assets/javascripts/sidebar/queries/sidebarDetailsForHealthStatusFeatureFlag.query.graphql b/app/assets/javascripts/sidebar/queries/sidebarDetailsForHealthStatusFeatureFlag.query.graphql new file mode 100644 index 00000000000..8cc68f6ea9a --- /dev/null +++ b/app/assets/javascripts/sidebar/queries/sidebarDetailsForHealthStatusFeatureFlag.query.graphql @@ -0,0 +1,7 @@ +query ($fullPath: ID!, $iid: String!) { + project (fullPath: $fullPath) { + issue (iid: $iid) { + iid + } + } +} diff --git a/app/assets/javascripts/sidebar/services/sidebar_service.js b/app/assets/javascripts/sidebar/services/sidebar_service.js index feb08e3acaf..59d4f6ed388 100644 --- a/app/assets/javascripts/sidebar/services/sidebar_service.js +++ b/app/assets/javascripts/sidebar/services/sidebar_service.js @@ -1,4 +1,14 @@ import axios from '~/lib/utils/axios_utils'; +import createGqClient, { fetchPolicies } from '~/lib/graphql'; +import sidebarDetailsQuery from 'ee_else_ce/sidebar/queries/sidebarDetails.query.graphql'; +import sidebarDetailsForHealthStatusFeatureFlagQuery from 'ee_else_ce/sidebar/queries/sidebarDetailsForHealthStatusFeatureFlag.query.graphql'; + +export const gqClient = createGqClient( + {}, + { + fetchPolicy: fetchPolicies.NO_CACHE, + }, +); export default class SidebarService { constructor(endpointMap) { @@ -7,6 +17,8 @@ export default class SidebarService { this.toggleSubscriptionEndpoint = endpointMap.toggleSubscriptionEndpoint; this.moveIssueEndpoint = endpointMap.moveIssueEndpoint; this.projectsAutocompleteEndpoint = endpointMap.projectsAutocompleteEndpoint; + this.fullPath = endpointMap.fullPath; + this.id = endpointMap.id; SidebarService.singleton = this; } @@ -15,7 +27,20 @@ export default class SidebarService { } get() { - return axios.get(this.endpoint); + const hasHealthStatusFeatureFlag = gon.features && gon.features.saveIssuableHealthStatus; + + return Promise.all([ + axios.get(this.endpoint), + gqClient.query({ + query: hasHealthStatusFeatureFlag + ? sidebarDetailsForHealthStatusFeatureFlagQuery + : sidebarDetailsQuery, + variables: { + fullPath: this.fullPath, + iid: this.id.toString(), + }, + }), + ]); } update(key, data) { diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js index ce869a625bf..eb1cf977725 100644 --- a/app/assets/javascripts/sidebar/sidebar_mediator.js +++ b/app/assets/javascripts/sidebar/sidebar_mediator.js @@ -19,6 +19,8 @@ export default class SidebarMediator { toggleSubscriptionEndpoint: options.toggleSubscriptionEndpoint, moveIssueEndpoint: options.moveIssueEndpoint, projectsAutocompleteEndpoint: options.projectsAutocompleteEndpoint, + fullPath: options.fullPath, + id: options.id, }); SidebarMediator.singleton = this; } @@ -45,8 +47,8 @@ export default class SidebarMediator { fetch() { return this.service .get() - .then(({ data }) => { - this.processFetchedData(data); + .then(([restResponse, graphQlResponse]) => { + this.processFetchedData(restResponse.data, graphQlResponse.data); }) .catch(() => new Flash(__('Error occurred when fetching sidebar data'))); } diff --git a/app/assets/javascripts/snippet/snippet_bundle.js b/app/assets/javascripts/snippet/snippet_bundle.js index 8e952fe9358..42da65a941d 100644 --- a/app/assets/javascripts/snippet/snippet_bundle.js +++ b/app/assets/javascripts/snippet/snippet_bundle.js @@ -17,7 +17,7 @@ const initAce = () => { const initMonaco = () => { const editorEl = document.getElementById('editor'); const contentEl = document.querySelector('.snippet-file-content'); - const fileNameEl = document.querySelector('.snippet-file-name'); + const fileNameEl = document.querySelector('.js-snippet-file-name'); const form = document.querySelector('.snippet-form-holder form'); editor = new Editor(); diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index 2a92b271ed0..882ce29c671 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -77,3 +77,5 @@ .gl-text-red-700 { @include gl-text-red-700; } .gl-text-orange-700 { @include gl-text-orange-700; } .gl-text-green-700 { @include gl-text-green-700; } + +.gl-align-items-center { @include gl-align-items-center; } diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index b14a1179d46..5ddc60707d5 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -44,6 +44,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:vue_issuable_sidebar, project.group) + push_frontend_feature_flag(:save_issuable_health_status, project.group) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] diff --git a/app/helpers/analytics_navbar_helper.rb b/app/helpers/analytics_navbar_helper.rb index 021b9bb10cd..eecb4090bcf 100644 --- a/app/helpers/analytics_navbar_helper.rb +++ b/app/helpers/analytics_navbar_helper.rb @@ -35,7 +35,7 @@ module AnalyticsNavbarHelper return unless project_nav_tab?(:cycle_analytics) navbar_sub_item( - title: _('Value Stream Analytics'), + title: _('Value Stream'), path: 'cycle_analytics#show', link: project_cycle_analytics_path(project), link_to_options: { class: 'shortcuts-project-cycle-analytics' } @@ -47,7 +47,7 @@ module AnalyticsNavbarHelper return if project.empty_repo? navbar_sub_item( - title: _('Repository Analytics'), + title: _('Repository'), path: 'graphs#charts', link: charts_project_graph_path(project, current_ref), link_to_options: { class: 'shortcuts-repository-charts' } @@ -60,7 +60,7 @@ module AnalyticsNavbarHelper return unless project.feature_available?(:builds, current_user) || !project.empty_repo? navbar_sub_item( - title: _('CI / CD Analytics'), + title: _('CI / CD'), path: 'pipelines#charts', link: charts_project_pipelines_path(project) ) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 8c75a4a13e8..aa05076c43e 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -463,6 +463,7 @@ module IssuablesHelper currentUser: issuable[:current_user], rootPath: root_path, fullPath: issuable[:project_full_path], + id: issuable[:id], timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours } end diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index b3d72ebdcf3..0a536a01f72 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -52,7 +52,9 @@ class BroadcastMessage < ApplicationRecord end def cache - Gitlab::JsonCache.new(cache_key_with_version: false) + ::Gitlab::SafeRequestStore.fetch(:broadcast_message_json_cache) do + Gitlab::JsonCache.new(cache_key_with_version: false) + end end def cache_expires_in @@ -68,9 +70,9 @@ class BroadcastMessage < ApplicationRecord now_or_future = messages.select(&:now_or_future?) - # If there are cached entries but none are to be displayed we'll purge the - # cache so we don't keep running this code all the time. - cache.expire(cache_key) if now_or_future.empty? + # If there are cached entries but they don't match the ones we are + # displaying we'll refresh the cache so we don't need to keep filtering. + cache.expire(cache_key) if now_or_future != messages now_or_future.select(&:now?).select { |message| message.matches_current_path(current_path) } end diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb index 3e8d0c6a778..b6882701e23 100644 --- a/app/models/internal_id.rb +++ b/app/models/internal_id.rb @@ -21,7 +21,7 @@ class InternalId < ApplicationRecord belongs_to :project belongs_to :namespace - enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5, operations_feature_flags: 6 } + enum usage: ::InternalIdEnums.usage_resources validates :usage, presence: true diff --git a/app/models/internal_id_enums.rb b/app/models/internal_id_enums.rb new file mode 100644 index 00000000000..2f7d7aeff2f --- /dev/null +++ b/app/models/internal_id_enums.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module InternalIdEnums + def self.usage_resources + # when adding new resource, make sure it doesn't conflict with EE usage_resources + { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5, operations_feature_flags: 6 } + end +end + +InternalIdEnums.prepend_if_ee('EE::InternalIdEnums') diff --git a/app/services/groups/import_export/export_service.rb b/app/services/groups/import_export/export_service.rb index aa484e7203c..0bf54844430 100644 --- a/app/services/groups/import_export/export_service.rb +++ b/app/services/groups/import_export/export_service.rb @@ -11,11 +11,7 @@ module Groups end def execute - unless @current_user.can?(:admin_group, @group) - raise ::Gitlab::ImportExport::Error.new( - "User with ID: %s does not have permission to Group %s with ID: %s." % - [@current_user.id, @group.name, @group.id]) - end + validate_user_permissions save! ensure @@ -26,6 +22,14 @@ module Groups attr_accessor :shared + def validate_user_permissions + unless @current_user.can?(:admin_group, @group) + @shared.error(::Gitlab::ImportExport::Error.permission_error(@current_user, @group)) + + notify_error! + end + end + def save! if savers.all?(&:save) notify_success diff --git a/app/services/groups/import_export/import_service.rb b/app/services/groups/import_export/import_service.rb index 57d2d9855d1..548a4a98dc1 100644 --- a/app/services/groups/import_export/import_service.rb +++ b/app/services/groups/import_export/import_service.rb @@ -12,15 +12,14 @@ module Groups end def execute - validate_user_permissions + if valid_user_permissions? && import_file && restorer.restore + notify_success - if import_file && restorer.restore @group else - raise StandardError.new(@shared.errors.to_sentence) + notify_error! end - rescue => e - raise StandardError.new(e.message) + ensure remove_import_file end @@ -49,13 +48,37 @@ module Groups upload.save! end - def validate_user_permissions - unless current_user.can?(:admin_group, group) - raise ::Gitlab::ImportExport::Error.new( - "User with ID: %s does not have permission to Group %s with ID: %s." % - [current_user.id, group.name, group.id]) + def valid_user_permissions? + if current_user.can?(:admin_group, group) + true + else + @shared.error(::Gitlab::ImportExport::Error.permission_error(current_user, group)) + + false end end + + def notify_success + @shared.logger.info( + group_id: @group.id, + group_name: @group.name, + message: 'Group Import/Export: Import succeeded' + ) + end + + def notify_error + @shared.logger.error( + group_id: @group.id, + group_name: @group.name, + message: "Group Import/Export: Errors occurred, see '#{Gitlab::ErrorTracking::Logger.file_name}' for details" + ) + end + + def notify_error! + notify_error + + raise Gitlab::ImportExport::Error.new(@shared.errors.to_sentence) + end end end end diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb index 37b5805ae7e..1876b1096fe 100644 --- a/app/services/merge_requests/merge_to_ref_service.rb +++ b/app/services/merge_requests/merge_to_ref_service.rb @@ -60,7 +60,7 @@ module MergeRequests def commit repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message, first_parent_ref) - rescue Gitlab::Git::PreReceiveError => error + rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError => error raise MergeError, error.message end end diff --git a/app/services/metrics/dashboard/clone_dashboard_service.rb b/app/services/metrics/dashboard/clone_dashboard_service.rb index 990dc462432..2720bfdafcd 100644 --- a/app/services/metrics/dashboard/clone_dashboard_service.rb +++ b/app/services/metrics/dashboard/clone_dashboard_service.rb @@ -24,7 +24,7 @@ module Metrics def execute catch(:error) do - throw(:error, error(_(%q(You can't commit to this project)), :forbidden)) unless push_authorized? + throw(:error, error(_(%q(You are not allowed to push into this branch. Create another branch or open a merge request.)), :forbidden)) unless push_authorized? result = ::Files::CreateService.new(project, current_user, dashboard_attrs).execute throw(:error, wrap_error(result)) unless result[:status] == :success diff --git a/app/services/metrics/dashboard/update_dashboard_service.rb b/app/services/metrics/dashboard/update_dashboard_service.rb index 316563ecbe7..e29fc47678f 100644 --- a/app/services/metrics/dashboard/update_dashboard_service.rb +++ b/app/services/metrics/dashboard/update_dashboard_service.rb @@ -9,7 +9,7 @@ module Metrics def execute catch(:error) do - throw(:error, error(_(%q(You can't commit to this project)), :forbidden)) unless push_authorized? + throw(:error, error(_(%q(You are not allowed to push into this branch. Create another branch or open a merge request.)), :forbidden)) unless push_authorized? result = ::Files::UpdateService.new(project, current_user, dashboard_attrs).execute throw(:error, result.merge(http_status: :bad_request)) unless result[:status] == :success diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb index 77fddc44085..5a3eb4c2156 100644 --- a/app/services/projects/import_export/export_service.rb +++ b/app/services/projects/import_export/export_service.rb @@ -5,9 +5,7 @@ module Projects class ExportService < BaseService def execute(after_export_strategy = nil, options = {}) unless project.template_source? || can?(current_user, :admin_project, project) - raise ::Gitlab::ImportExport::Error.new( - "User with ID: %s does not have permission to Project %s with ID: %s." % - [current_user.id, project.name, project.id]) + raise ::Gitlab::ImportExport::Error.permission_error(current_user, project) end @shared = project.import_export_shared diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index c00c48b623c..b2fa647b23e 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -48,9 +48,9 @@ - unless should_display_analytics_pages_in_sidebar - if group_sidebar_link?(:contribution_analytics) = nav_link(path: 'contribution_analytics#show') do - = link_to group_contribution_analytics_path(@group), title: _('Contribution Analytics'), data: { placement: 'right', qa_selector: 'contribution_analytics_link' } do + = link_to group_contribution_analytics_path(@group), title: _('Contribution'), data: { placement: 'right', qa_selector: 'contribution_analytics_link' } do %span - = _('Contribution Analytics') + = _('Contribution') = render_if_exists 'layouts/nav/group_insights_link' diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 160e2a7c952..b8b5eacfab1 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -42,8 +42,8 @@ - unless should_display_analytics_pages_in_sidebar - if can?(current_user, :read_cycle_analytics, @project) = nav_link(path: 'cycle_analytics#show') do - = link_to project_cycle_analytics_path(@project), title: _('Value Stream Analytics'), class: 'shortcuts-project-cycle-analytics' do - %span= _('Value Stream Analytics') + = link_to project_cycle_analytics_path(@project), title: _('Value Stream'), class: 'shortcuts-project-cycle-analytics' do + %span= _('Value Stream') = render_if_exists 'layouts/nav/project_insights_link' diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 2a853de12a4..1a1da6b3801 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -129,6 +129,9 @@ = render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar + - if Feature.enabled?(:save_issuable_health_status, @project.group) && issuable_sidebar[:type] == "issue" + .js-sidebar-status-entry-point + - if issuable_sidebar.has_key?(:confidential) -# haml-lint:disable InlineJavaScript %script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: issuable_sidebar[:confidential], is_editable: can_edit_issuable }.to_json.html_safe diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index b07992c6890..828015d29f5 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -26,7 +26,7 @@ = f.label :file_name, s_('Snippets|File') .file-holder.snippet .js-file-title.file-title-flex-parent - = f.text_field :file_name, placeholder: s_("Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby"), class: 'form-control snippet-file-name qa-snippet-file-name' + = f.text_field :file_name, placeholder: s_("Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby"), class: 'form-control js-snippet-file-name qa-snippet-file-name' .file-content.code %pre#editor{ data: { 'editor-loading': true } }= @snippet.content = f.hidden_field :content, class: 'snippet-file-content' diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 61e9f50c2dd..890f38aa26b 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -865,7 +865,7 @@ :weight: 2 :idempotent: - :name: create_evidence - :feature_category: :release_governance + :feature_category: :release_evidence :has_external_dependencies: :urgency: :default :resource_boundary: :unknown diff --git a/app/workers/create_evidence_worker.rb b/app/workers/create_evidence_worker.rb index 46f211cf3e1..c2faba84cfc 100644 --- a/app/workers/create_evidence_worker.rb +++ b/app/workers/create_evidence_worker.rb @@ -3,7 +3,7 @@ class CreateEvidenceWorker # rubocop:disable Scalability/IdempotentWorker include ApplicationWorker - feature_category :release_governance + feature_category :release_evidence weight 2 def perform(release_id) |