From 6046a605fdbb6d180861c978d17fe3516b2e7507 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 22 Jun 2020 12:08:47 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../diffs/components/diff_file_header.vue | 4 - app/assets/javascripts/editor/editor_lite.js | 5 + .../fragments/project.fragment.graphql | 4 +- .../snippets/queries/snippet.query.graphql | 2 +- app/controllers/concerns/service_params.rb | 2 - .../concerns/snippets/blobs_actions.rb | 53 +++ .../concerns/snippets/send_blob.rb | 22 ++ app/controllers/concerns/snippets_actions.rb | 18 +- .../projects/snippets/blobs_controller.rb | 5 + .../projects/snippets_controller.rb | 4 +- app/controllers/snippets/blobs_controller.rb | 7 + .../projects/jira_projects_resolver.rb | 42 +-- .../projects/services/jira_service_type.rb | 2 +- app/models/ci/build_metadata.rb | 3 +- .../project_services/bugzilla_service.rb | 4 +- .../custom_issue_tracker_service.rb | 6 +- .../gitlab_issue_tracker_service.rb | 4 +- .../project_services/issue_tracker_service.rb | 32 -- app/models/project_services/jira_service.rb | 6 +- .../project_services/redmine_service.rb | 4 +- .../project_services/youtrack_service.rb | 5 +- app/models/service.rb | 3 + app/services/gpg_keys/destroy_service.rb | 9 + app/services/jira/requests/base.rb | 12 +- app/services/jira/requests/projects.rb | 15 +- app/views/admin/appearances/show.html.haml | 2 +- app/views/admin/applications/edit.html.haml | 4 +- app/views/admin/applications/index.html.haml | 2 +- app/views/admin/applications/new.html.haml | 4 +- app/views/admin/applications/show.html.haml | 2 +- .../admin/background_jobs/show.html.haml | 2 +- .../admin/broadcast_messages/edit.html.haml | 4 +- .../admin/broadcast_messages/index.html.haml | 4 +- app/views/admin/dashboard/index.html.haml | 3 +- app/views/admin/deploy_keys/new.html.haml | 2 +- .../admin/gitaly_servers/index.html.haml | 1 + app/views/admin/hook_logs/show.html.haml | 2 +- app/views/admin/jobs/index.html.haml | 3 +- app/views/admin/keys/show.html.haml | 2 +- app/views/admin/projects/show.html.haml | 4 +- .../admin/requests_profiles/index.html.haml | 2 +- app/views/admin/runners/index.html.haml | 1 + app/views/admin/runners/show.html.haml | 1 + app/views/admin/services/edit.html.haml | 4 +- app/views/admin/services/index.html.haml | 2 +- app/views/admin/spam_logs/index.html.haml | 2 +- app/views/admin/users/edit.html.haml | 2 +- app/views/admin/users/index.html.haml | 2 +- app/views/admin/users/keys.html.haml | 4 +- app/views/admin/users/new.html.haml | 2 +- app/views/admin/users/projects.html.haml | 4 +- app/views/admin/users/show.html.haml | 4 +- app/views/dashboard/activity.html.haml | 4 +- app/views/dashboard/groups/index.html.haml | 4 +- .../dashboard/milestones/index.html.haml | 4 +- app/views/dashboard/projects/index.html.haml | 4 +- app/views/dashboard/snippets/index.html.haml | 4 +- app/views/dashboard/todos/index.html.haml | 4 +- app/views/devise/registrations/new.html.haml | 2 +- app/views/devise/sessions/new.html.haml | 2 +- app/views/explore/snippets/index.html.haml | 4 +- app/views/groups/activity.html.haml | 2 +- app/views/groups/edit.html.haml | 1 + app/views/groups/issues.html.haml | 2 +- app/views/groups/labels/edit.html.haml | 2 +- app/views/groups/labels/index.html.haml | 2 +- app/views/groups/merge_requests.html.haml | 2 +- app/views/groups/milestones/index.html.haml | 2 +- app/views/groups/projects.html.haml | 3 +- .../groups/settings/ci_cd/show.html.haml | 4 +- app/views/groups/show.html.haml | 1 + .../help/instance_configuration.html.haml | 2 +- app/views/help/ui.html.haml | 2 +- app/views/ide/_show.html.haml | 2 +- .../import/bitbucket_server/new.html.haml | 2 +- .../import/bitbucket_server/status.html.haml | 4 +- app/views/import/manifest/new.html.haml | 4 +- app/views/import/manifest/status.html.haml | 4 +- .../cohorts/index.html.haml | 1 + app/views/layouts/header/_new_dropdown.haml | 1 + app/views/layouts/snippets.html.haml | 1 + app/views/profiles/show.html.haml | 1 + app/views/projects/artifacts/browse.html.haml | 2 +- app/views/projects/artifacts/file.html.haml | 2 +- app/views/projects/blame/show.html.haml | 2 +- app/views/projects/blob/edit.html.haml | 4 +- app/views/projects/blob/new.html.haml | 4 +- app/views/projects/branches/new.html.haml | 2 +- app/views/projects/compare/index.html.haml | 4 +- .../projects/cycle_analytics/show.html.haml | 2 +- app/views/projects/deploy_keys/edit.html.haml | 2 +- app/views/projects/empty.html.haml | 1 + app/views/projects/find_file/show.html.haml | 2 +- app/views/projects/imports/new.html.haml | 2 +- app/views/projects/issues/edit.html.haml | 2 +- app/views/projects/issues/index.html.haml | 2 +- app/views/projects/jobs/index.html.haml | 2 +- app/views/projects/jobs/terminal.html.haml | 6 +- app/views/projects/labels/edit.html.haml | 6 +- app/views/projects/labels/index.html.haml | 2 +- app/views/projects/labels/new.html.haml | 6 +- .../merge_requests/conflicts/show.html.haml | 2 +- .../merge_requests/creations/new.html.haml | 6 +- .../projects/merge_requests/edit.html.haml | 2 +- .../projects/merge_requests/index.html.haml | 2 +- .../projects/merge_requests/invalid.html.haml | 2 +- .../projects/merge_requests/show.html.haml | 4 +- app/views/projects/network/show.html.haml | 4 +- app/views/projects/no_repo.html.haml | 1 + app/views/projects/pages/show.html.haml | 2 +- .../protected_branches/show.html.haml | 2 +- .../projects/protected_tags/show.html.haml | 2 +- .../serverless/functions/index.html.haml | 4 +- app/views/projects/show.html.haml | 1 + .../projects/tags/releases/edit.html.haml | 4 +- app/views/projects/triggers/edit.html.haml | 2 +- app/views/shared/runners/show.html.haml | 2 +- ...ignore-title-description-from-services.yml | 5 + .../220232-get-all-jira-projects.yml | 5 + .../dmishunov-editor-lite-extensions.yml | 5 + changelogs/unreleased/services-usage-7.yml | 5 + .../unreleased/vij-raw-snippet-blobs.yml | 5 + config/routes/project.rb | 6 + config/routes/snippets.rb | 9 + doc/administration/high_availability/nfs.md | 8 + .../graphql/reference/gitlab_schema.graphql | 57 ++- doc/api/graphql/reference/gitlab_schema.json | 166 +++++++- doc/api/graphql/reference/index.md | 25 +- doc/api/issues.md | 23 +- doc/development/sidekiq_style_guide.md | 94 ++--- doc/user/project/integrations/bugzilla.md | 1 - .../integrations/custom_issue_tracker.md | 2 - doc/user/project/integrations/redmine.md | 1 - doc/user/project/integrations/youtrack.md | 1 - lib/api/users.rb | 7 +- .../import_export/project/import_export.yml | 2 + lib/gitlab/suggestions/file_suggestion.rb | 53 +-- lib/gitlab/suggestions/suggestion_set.rb | 22 +- locale/gitlab.pot | 78 ++++ package.json | 2 +- .../projects/issues_controller_spec.rb | 2 +- .../snippets/blobs_controller_spec.rb | 85 +++++ .../snippets/blobs_controller_spec.rb | 61 +++ spec/db/schema_spec.rb | 2 +- .../corrupted_project_export.tar.gz | Bin 4352 -> 3973 bytes .../lightweight_project_export.tar.gz | Bin 3837 -> 3778 bytes .../gitlab/import_export/complex/project.json | 19 - .../complex/tree/project/services.ndjson | 38 +- .../gitlab/import_export/light/project.json | 2 - .../light/tree/project/services.ndjson | 4 +- spec/frontend/editor/editor_lite_spec.js | 70 ++++ spec/frontend/fixtures/services.rb | 2 +- .../projects/jira_projects_resolver_spec.rb | 2 +- .../suggestions/file_suggestion_spec.rb | 355 ++++++++++++++---- .../gitlab/suggestions/suggestion_set_spec.rb | 5 +- spec/models/ci/build_metadata_spec.rb | 29 ++ .../project_services/bugzilla_service_spec.rb | 45 --- .../custom_issue_tracker_service_spec.rb | 61 --- .../gitlab_issue_tracker_service_spec.rb | 45 --- .../project_services/jira_service_spec.rb | 90 +---- .../project_services/redmine_service_spec.rb | 45 --- .../project_services/youtrack_service_spec.rb | 45 --- spec/models/service_spec.rb | 15 +- .../api/graphql/project/jira_projects_spec.rb | 28 -- spec/routing/project_routing_spec.rb | 8 + spec/routing/routing_spec.rb | 7 + .../services/gpg_keys/destroy_service_spec.rb | 15 + spec/services/jira/requests/projects_spec.rb | 20 +- spec/support/helpers/jira_service_helper.rb | 3 +- spec/support/helpers/snippet_helpers.rb | 7 + .../jira_import/jira_projects_context.rb | 68 ++++ .../snippet_blob_shared_examples.rb | 49 +++ .../models/services_fields_shared_examples.rb | 31 -- yarn.lock | 8 +- 174 files changed, 1470 insertions(+), 896 deletions(-) create mode 100644 app/controllers/concerns/snippets/blobs_actions.rb create mode 100644 app/controllers/concerns/snippets/send_blob.rb create mode 100644 app/controllers/projects/snippets/blobs_controller.rb create mode 100644 app/controllers/snippets/blobs_controller.rb create mode 100644 app/services/gpg_keys/destroy_service.rb create mode 100644 changelogs/unreleased/217587-ignore-title-description-from-services.yml create mode 100644 changelogs/unreleased/220232-get-all-jira-projects.yml create mode 100644 changelogs/unreleased/dmishunov-editor-lite-extensions.yml create mode 100644 changelogs/unreleased/services-usage-7.yml create mode 100644 changelogs/unreleased/vij-raw-snippet-blobs.yml create mode 100644 spec/controllers/projects/snippets/blobs_controller_spec.rb create mode 100644 spec/controllers/snippets/blobs_controller_spec.rb create mode 100644 spec/services/gpg_keys/destroy_service_spec.rb create mode 100644 spec/support/helpers/snippet_helpers.rb create mode 100644 spec/support/shared_examples/controllers/snippet_blob_shared_examples.rb delete mode 100644 spec/support/shared_examples/models/services_fields_shared_examples.rb diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index 61bbf13aa53..d2110483c1c 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -2,7 +2,6 @@ import { escape } from 'lodash'; import { mapActions, mapGetters } from 'vuex'; import { GlDeprecatedButton, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui'; -import { polyfillSticky } from '~/lib/utils/sticky'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import Icon from '~/vue_shared/components/icon.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue'; @@ -124,9 +123,6 @@ export default { return s__('MRDiff|Show full file'); }, }, - mounted() { - polyfillSticky(this.$refs.header); - }, methods: { ...mapActions('diffs', [ 'toggleFileDiscussions', diff --git a/app/assets/javascripts/editor/editor_lite.js b/app/assets/javascripts/editor/editor_lite.js index 6db24a1b7ef..551ffbabaef 100644 --- a/app/assets/javascripts/editor/editor_lite.js +++ b/app/assets/javascripts/editor/editor_lite.js @@ -88,4 +88,9 @@ export default class Editor { updateOptions(options = {}) { this.instance.updateOptions(options); } + + use(exts = []) { + const extensions = Array.isArray(exts) ? exts : [exts]; + Object.assign(this, ...extensions); + } } diff --git a/app/assets/javascripts/snippets/fragments/project.fragment.graphql b/app/assets/javascripts/snippets/fragments/project.fragment.graphql index 7d65789c67b..64bb2315c1b 100644 --- a/app/assets/javascripts/snippets/fragments/project.fragment.graphql +++ b/app/assets/javascripts/snippets/fragments/project.fragment.graphql @@ -1,6 +1,6 @@ -fragment Project on Snippet { +fragment SnippetProject on Snippet { project { fullPath webUrl } -} \ No newline at end of file +} diff --git a/app/assets/javascripts/snippets/queries/snippet.query.graphql b/app/assets/javascripts/snippets/queries/snippet.query.graphql index c58a5168ba3..b23ab862439 100644 --- a/app/assets/javascripts/snippets/queries/snippet.query.graphql +++ b/app/assets/javascripts/snippets/queries/snippet.query.graphql @@ -7,7 +7,7 @@ query GetSnippetQuery($ids: [ID!]) { edges { node { ...SnippetBase - ...Project + ...SnippetProject author { ...Author } diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb index e78fa8f8250..a3e986572ef 100644 --- a/app/controllers/concerns/service_params.rb +++ b/app/controllers/concerns/service_params.rb @@ -23,7 +23,6 @@ module ServiceParams :comment_detail, :confidential_issues_events, :default_irc_uri, - :description, :device, :disable_diffs, :drone_url, @@ -61,7 +60,6 @@ module ServiceParams :sound, :subdomain, :teamcity_url, - :title, :token, :type, :url, diff --git a/app/controllers/concerns/snippets/blobs_actions.rb b/app/controllers/concerns/snippets/blobs_actions.rb new file mode 100644 index 00000000000..db56ce8f193 --- /dev/null +++ b/app/controllers/concerns/snippets/blobs_actions.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Snippets::BlobsActions + extend ActiveSupport::Concern + + include Gitlab::Utils::StrongMemoize + include ExtractsRef + include Snippets::SendBlob + + included do + before_action :authorize_read_snippet!, only: [:raw] + before_action :ensure_repository + before_action :ensure_blob + end + + def raw + send_snippet_blob(snippet, blob) + end + + private + + def repository_container + snippet + end + + # rubocop:disable Gitlab/ModuleWithInstanceVariables + def blob + strong_memoize(:blob) do + assign_ref_vars + + next unless @commit + + @repo.blob_at(@commit.id, @path) + end + end + # rubocop:enable Gitlab/ModuleWithInstanceVariables + + def ensure_blob + render_404 unless blob + end + + def ensure_repository + unless snippet.repo_exists? + Gitlab::AppLogger.error(message: "Snippet raw blob attempt with no repo", snippet: snippet.id) + + respond_422 + end + end + + def snippet_id + params[:snippet_id] + end +end diff --git a/app/controllers/concerns/snippets/send_blob.rb b/app/controllers/concerns/snippets/send_blob.rb new file mode 100644 index 00000000000..4f432430aaa --- /dev/null +++ b/app/controllers/concerns/snippets/send_blob.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Snippets::SendBlob + include SendsBlob + + def send_snippet_blob(snippet, blob) + workhorse_set_content_type! + + send_blob( + snippet.repository, + blob, + inline: content_disposition == 'inline', + allow_caching: snippet.public? + ) + end + + private + + def content_disposition + @disposition ||= params[:inline] == 'false' ? 'attachment' : 'inline' + end +end diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb index 51fc12398d9..3b8f9722913 100644 --- a/app/controllers/concerns/snippets_actions.rb +++ b/app/controllers/concerns/snippets_actions.rb @@ -2,11 +2,12 @@ module SnippetsActions extend ActiveSupport::Concern - include SendsBlob + include RendersNotes include RendersBlob include PaginatedCollection include Gitlab::NoteableMetadata + include Snippets::SendBlob included do skip_before_action :verify_authenticity_token, @@ -25,6 +26,10 @@ module SnippetsActions render 'edit' end + # This endpoint is being replaced by Snippets::BlobController#raw + # Support for old raw links will be maintainted via this action but + # it will only return the first blob found, + # see: https://gitlab.com/gitlab-org/gitlab/-/issues/217775 def raw workhorse_set_content_type! @@ -39,12 +44,7 @@ module SnippetsActions filename: Snippet.sanitized_file_name(blob.name) ) else - send_blob( - snippet.repository, - blob, - inline: content_disposition == 'inline', - allow_caching: snippet.public? - ) + send_snippet_blob(snippet, blob) end end @@ -106,10 +106,6 @@ module SnippetsActions private - def content_disposition - @disposition ||= params[:inline] == 'false' ? 'attachment' : 'inline' - end - # rubocop:disable Gitlab/ModuleWithInstanceVariables def blob return unless snippet diff --git a/app/controllers/projects/snippets/blobs_controller.rb b/app/controllers/projects/snippets/blobs_controller.rb new file mode 100644 index 00000000000..148fc7c96f8 --- /dev/null +++ b/app/controllers/projects/snippets/blobs_controller.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class Projects::Snippets::BlobsController < Projects::Snippets::ApplicationController + include Snippets::BlobsActions +end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 5ee6abef804..1bc63fce5fc 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -15,7 +15,7 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController before_action :authorize_admin_snippet!, only: [:destroy] def index - @snippet_counts = Snippets::CountService + @snippet_counts = ::Snippets::CountService .new(current_user, project: @project) .execute @@ -35,7 +35,7 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController def create create_params = snippet_params.merge(spammable_params) - service_response = Snippets::CreateService.new(project, current_user, create_params).execute + service_response = ::Snippets::CreateService.new(project, current_user, create_params).execute @snippet = service_response.payload[:snippet] handle_repository_error(:new) diff --git a/app/controllers/snippets/blobs_controller.rb b/app/controllers/snippets/blobs_controller.rb new file mode 100644 index 00000000000..d7c4bbcf8f2 --- /dev/null +++ b/app/controllers/snippets/blobs_controller.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Snippets::BlobsController < Snippets::ApplicationController + include Snippets::BlobsActions + + skip_before_action :authenticate_user!, only: [:raw] +end diff --git a/app/graphql/resolvers/projects/jira_projects_resolver.rb b/app/graphql/resolvers/projects/jira_projects_resolver.rb index a8c3768df41..2b5d04ef39e 100644 --- a/app/graphql/resolvers/projects/jira_projects_resolver.rb +++ b/app/graphql/resolvers/projects/jira_projects_resolver.rb @@ -13,11 +13,10 @@ module Resolvers def resolve(name: nil, **args) authorize!(project) - response, start_cursor, end_cursor = jira_projects(name: name, **compute_pagination_params(args)) - end_cursor = nil if !!response.payload[:is_last] + response = jira_projects(name: name) if response.success? - Gitlab::Graphql::ExternallyPaginatedArray.new(start_cursor, end_cursor, *response.payload[:projects]) + response.payload[:projects] else raise Gitlab::Graphql::Errors::BaseError, response.message end @@ -35,41 +34,10 @@ module Resolvers jira_service&.project end - def compute_pagination_params(params) - after_cursor = Base64.decode64(params[:after].to_s) - before_cursor = Base64.decode64(params[:before].to_s) + def jira_projects(name:) + args = { query: name }.compact - # differentiate between 0 cursor and nil or invalid cursor that decodes into zero. - after_index = after_cursor.to_i == 0 && after_cursor != "0" ? nil : after_cursor.to_i - before_index = before_cursor.to_i == 0 && before_cursor != "0" ? nil : before_cursor.to_i - - if after_index.present? && before_index.present? - if after_index >= before_index - { start_at: 0, limit: 0 } - else - { start_at: after_index + 1, limit: before_index - after_index - 1 } - end - elsif after_index.present? - { start_at: after_index + 1, limit: nil } - elsif before_index.present? - { start_at: 0, limit: before_index - 1 } - else - { start_at: 0, limit: nil } - end - end - - def jira_projects(name:, start_at:, limit:) - args = { query: name, start_at: start_at, limit: limit }.compact - - response = Jira::Requests::Projects.new(project.jira_service, args).execute - - return [response, nil, nil] if response.error? - - projects = response.payload[:projects] - start_cursor = start_at == 0 ? nil : Base64.encode64((start_at - 1).to_s) - end_cursor = Base64.encode64((start_at + projects.size - 1).to_s) - - [response, start_cursor, end_cursor] + return Jira::Requests::Projects.new(project.jira_service, args).execute end end end diff --git a/app/graphql/types/projects/services/jira_service_type.rb b/app/graphql/types/projects/services/jira_service_type.rb index e81963f752d..8bf85a14cbf 100644 --- a/app/graphql/types/projects/services/jira_service_type.rb +++ b/app/graphql/types/projects/services/jira_service_type.rb @@ -15,7 +15,7 @@ module Types null: true, connection: false, extensions: [Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension], - description: 'List of Jira projects fetched through Jira REST API', + description: 'List of all Jira projects fetched through Jira REST API', resolver: Resolvers::Projects::JiraProjectsResolver end end diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb index c756a9b546a..4094bdb26dc 100644 --- a/app/models/ci/build_metadata.rb +++ b/app/models/ci/build_metadata.rb @@ -19,6 +19,7 @@ module Ci before_create :set_build_project validates :build, presence: true + validates :secrets, json_schema: { filename: 'build_metadata_secrets' } serialize :config_options, Serializers::JSON # rubocop:disable Cop/ActiveRecordSerialize serialize :config_variables, Serializers::JSON # rubocop:disable Cop/ActiveRecordSerialize @@ -83,5 +84,3 @@ module Ci end end end - -Ci::BuildMetadata.prepend_if_ee('EE::Ci::BuildMetadata') diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb index 0a498fde95a..4332db3e961 100644 --- a/app/models/project_services/bugzilla_service.rb +++ b/app/models/project_services/bugzilla_service.rb @@ -3,11 +3,11 @@ class BugzillaService < IssueTrackerService validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? - def default_title + def title 'Bugzilla' end - def default_description + def description s_('IssueTracker|Bugzilla issue tracker') end diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index dbc42b1b86d..fc58ba27c3d 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -3,11 +3,11 @@ class CustomIssueTrackerService < IssueTrackerService validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? - def default_title + def title 'Custom Issue Tracker' end - def default_description + def description s_('IssueTracker|Custom issue tracker') end @@ -17,8 +17,6 @@ class CustomIssueTrackerService < IssueTrackerService def fields [ - { type: 'text', name: 'title', placeholder: title }, - { type: 'text', name: 'description', placeholder: description }, { type: 'text', name: 'project_url', placeholder: 'Project url', required: true }, { type: 'text', name: 'issues_url', placeholder: 'Issue url', required: true }, { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url', required: true } diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index ec28602b5e6..b3f44e040bc 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -7,11 +7,11 @@ class GitlabIssueTrackerService < IssueTrackerService default_value_for :default, true - def default_title + def title 'GitLab' end - def default_description + def description s_('IssueTracker|GitLab issue tracker') end diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index f5d6ae10469..694374e9548 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -25,28 +25,6 @@ class IssueTrackerService < Service end end - # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - def title - if title_attribute = read_attribute(:title) - title_attribute - elsif self.properties && self.properties['title'].present? - self.properties['title'] - else - default_title - end - end - - # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - def description - if description_attribute = read_attribute(:description) - description_attribute - elsif self.properties && self.properties['description'].present? - self.properties['description'] - else - default_description - end - end - def handle_properties # this has been moved from initialize_properties and should be improved # as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 @@ -54,13 +32,6 @@ class IssueTrackerService < Service @legacy_properties_data = properties.dup data_values = properties.slice!('title', 'description') - properties.each do |key, _| - current_value = self.properties.delete(key) - value = attribute_changed?(key) ? attribute_change(key).last : current_value - - write_attribute(key, value) - end - data_values.reject! { |key| data_fields.changed.include?(key) } data_values.slice!(*data_fields.attributes.keys) data_fields.assign_attributes(data_values) if data_values.present? @@ -102,7 +73,6 @@ class IssueTrackerService < Service def fields [ - { type: 'text', name: 'description', placeholder: description }, { type: 'text', name: 'project_url', placeholder: 'Project url', required: true }, { type: 'text', name: 'issues_url', placeholder: 'Issue url', required: true }, { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url', required: true } @@ -117,8 +87,6 @@ class IssueTrackerService < Service def set_default_data return unless issues_tracker.present? - self.title ||= issues_tracker['title'] - # we don't want to override if we have set something return if project_url || issues_url || new_issue_url diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index bb4d35cad22..8c97547f416 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -64,8 +64,6 @@ class JiraService < IssueTrackerService def set_default_data return unless issues_tracker.present? - self.title ||= issues_tracker['title'] - return if url data_fields.url ||= issues_tracker['url'] @@ -103,11 +101,11 @@ class JiraService < IssueTrackerService [Jira service documentation](#{help_page_url('user/project/integrations/jira')})." end - def default_title + def title 'Jira' end - def default_description + def description s_('JiraService|Jira issue tracker') end diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb index a4ca0d20669..df78520d65f 100644 --- a/app/models/project_services/redmine_service.rb +++ b/app/models/project_services/redmine_service.rb @@ -3,11 +3,11 @@ class RedmineService < IssueTrackerService validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? - def default_title + def title 'Redmine' end - def default_description + def description s_('IssueTracker|Redmine issue tracker') end diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb index 40203ad692d..7fb3bde44a5 100644 --- a/app/models/project_services/youtrack_service.rb +++ b/app/models/project_services/youtrack_service.rb @@ -12,11 +12,11 @@ class YoutrackService < IssueTrackerService end end - def default_title + def title 'YouTrack' end - def default_description + def description s_('IssueTracker|YouTrack issue tracker') end @@ -26,7 +26,6 @@ class YoutrackService < IssueTrackerService def fields [ - { type: 'text', name: 'description', placeholder: description }, { type: 'text', name: 'project_url', title: 'Project URL', placeholder: 'Project URL', required: true }, { type: 'text', name: 'issues_url', title: 'Issue URL', placeholder: 'Issue URL', required: true } ] diff --git a/app/models/service.rb b/app/models/service.rb index 04b30930d54..e310ff9abd2 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -7,6 +7,9 @@ class Service < ApplicationRecord include Importable include ProjectServicesLoggable include DataFields + include IgnorableColumns + + ignore_columns %i[title description], remove_with: '13.4', remove_after: '2020-09-22' SERVICE_NAMES = %w[ alerts asana assembla bamboo bugzilla buildkite campfire custom_issue_tracker discord diff --git a/app/services/gpg_keys/destroy_service.rb b/app/services/gpg_keys/destroy_service.rb new file mode 100644 index 00000000000..cecbfe26611 --- /dev/null +++ b/app/services/gpg_keys/destroy_service.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module GpgKeys + class DestroyService < Keys::BaseService + def execute(key) + key.destroy + end + end +end diff --git a/app/services/jira/requests/base.rb b/app/services/jira/requests/base.rb index 7521c7610cb..0934730d10c 100644 --- a/app/services/jira/requests/base.rb +++ b/app/services/jira/requests/base.rb @@ -5,22 +5,16 @@ module Jira class Base include ProjectServicesLoggable - PER_PAGE = 50 + attr_reader :jira_service, :project, :query - attr_reader :jira_service, :project, :limit, :start_at, :query - - def initialize(jira_service, limit: PER_PAGE, start_at: 0, query: nil) + def initialize(jira_service, query: nil) @project = jira_service&.project @jira_service = jira_service - - @limit = limit - @start_at = start_at - @query = query + @query = query end def execute return ServiceResponse.error(message: _('Jira service not configured.')) unless jira_service&.active? - return ServiceResponse.success(payload: empty_payload) if limit.to_i <= 0 request end diff --git a/app/services/jira/requests/projects.rb b/app/services/jira/requests/projects.rb index da464503211..afb3b45fac1 100644 --- a/app/services/jira/requests/projects.rb +++ b/app/services/jira/requests/projects.rb @@ -9,19 +9,24 @@ module Jira override :url def url - '/rest/api/2/project/search?query=%{query}&maxResults=%{limit}&startAt=%{start_at}' % - { query: CGI.escape(query.to_s), limit: limit.to_i, start_at: start_at.to_i } + '/rest/api/2/project' end override :build_service_response def build_service_response(response) - return ServiceResponse.success(payload: empty_payload) unless response['values'].present? + return ServiceResponse.success(payload: empty_payload) unless response.present? - ServiceResponse.success(payload: { projects: map_projects(response), is_last: response['isLast'] }) + ServiceResponse.success(payload: { projects: map_projects(response), is_last: true }) end def map_projects(response) - response['values'].map { |v| JIRA::Resource::Project.build(client, v) } + response.map { |v| JIRA::Resource::Project.build(client, v) }.select(&method(:match_query?)) + end + + def match_query?(jira_project) + query = self.query.to_s.downcase + + jira_project&.key&.downcase&.include?(query) || jira_project&.name&.downcase&.include?(query) end def empty_payload diff --git a/app/views/admin/appearances/show.html.haml b/app/views/admin/appearances/show.html.haml index ccf6f960cf2..77a08913666 100644 --- a/app/views/admin/appearances/show.html.haml +++ b/app/views/admin/appearances/show.html.haml @@ -1,4 +1,4 @@ -- page_title "Appearance" +- page_title _("Appearance") - @content_class = "limit-container-width" unless fluid_layout = render 'form' diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml index 13c408914bb..4f737a14e12 100644 --- a/app/views/admin/applications/edit.html.haml +++ b/app/views/admin/applications/edit.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Applications", admin_applications_path +- add_to_breadcrumbs _("Applications"), admin_applications_path - breadcrumb_title @application.name -- page_title "Edit", @application.name, "Applications" +- page_title _("Edit"), @application.name, _("Applications") %h3.page-title Edit application - @url = admin_application_path(@application) diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index c3861f335b8..0119cabf1ad 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Applications" +- page_title _("Applications") %h3.page-title System OAuth applications %p.light diff --git a/app/views/admin/applications/new.html.haml b/app/views/admin/applications/new.html.haml index 346c58877d9..4d4b6b0c994 100644 --- a/app/views/admin/applications/new.html.haml +++ b/app/views/admin/applications/new.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title "Applications" -- page_title "New Application" +- breadcrumb_title _("Applications") +- page_title _("New Application") %h3.page-title New application - @url = admin_applications_path diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index 146674a2fac..b39a753d010 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -1,4 +1,4 @@ -- page_title @application.name, "Applications" +- page_title @application.name, _("Applications") %h3.page-title Application: #{@application.name} diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index 1001a69b787..bbb47e29bb9 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -1,4 +1,4 @@ -- page_title "Background Jobs" +- page_title _("Background Jobs") %h3.page-title Background Jobs %p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing diff --git a/app/views/admin/broadcast_messages/edit.html.haml b/app/views/admin/broadcast_messages/edit.html.haml index 8cbc4597e32..569aaa29cc4 100644 --- a/app/views/admin/broadcast_messages/edit.html.haml +++ b/app/views/admin/broadcast_messages/edit.html.haml @@ -1,4 +1,4 @@ -- breadcrumb_title "Messages" -- page_title "Broadcast Messages" +- breadcrumb_title _("Messages") +- page_title _("Broadcast Messages") = render 'form' diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index e7a7ee96508..bca74f71c5c 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title "Messages" -- page_title "Broadcast Messages" +- breadcrumb_title _("Messages") +- page_title _("Broadcast Messages") %h3.page-title Broadcast Messages diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 951e5364ad8..3afed99334b 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -1,4 +1,5 @@ -- breadcrumb_title "Dashboard" +- breadcrumb_title _("Dashboard") +- page_title _("Dashboard") - if show_license_breakdown? = render_if_exists 'admin/licenses/breakdown', license: @license diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml index 9a563a5bc78..f43c1447f09 100644 --- a/app/views/admin/deploy_keys/new.html.haml +++ b/app/views/admin/deploy_keys/new.html.haml @@ -1,4 +1,4 @@ -- page_title 'New Deploy Key' +- page_title _('New Deploy Key') %h3.page-title New public deploy key %hr diff --git a/app/views/admin/gitaly_servers/index.html.haml b/app/views/admin/gitaly_servers/index.html.haml index 9b24f411a75..0b06f145687 100644 --- a/app/views/admin/gitaly_servers/index.html.haml +++ b/app/views/admin/gitaly_servers/index.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _("Gitaly Servers") +- page_title _("Gitaly Servers") %h3.page-title= _("Gitaly Servers") %hr diff --git a/app/views/admin/hook_logs/show.html.haml b/app/views/admin/hook_logs/show.html.haml index 86729dbe7bc..e0256e66376 100644 --- a/app/views/admin/hook_logs/show.html.haml +++ b/app/views/admin/hook_logs/show.html.haml @@ -1,4 +1,4 @@ -- page_title 'Request details' +- page_title _('Request details') %h3.page-title Request details diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml index f1bdd52b399..32c0a801a1d 100644 --- a/app/views/admin/jobs/index.html.haml +++ b/app/views/admin/jobs/index.html.haml @@ -1,4 +1,5 @@ -- breadcrumb_title "Jobs" +- breadcrumb_title _("Jobs") +- page_title _("Jobs") .top-area.scrolling-tabs-container.inner-page-scroll-tabs - build_path_proc = ->(scope) { admin_jobs_path(scope: scope) } diff --git a/app/views/admin/keys/show.html.haml b/app/views/admin/keys/show.html.haml index 9ee77c77398..03cc0ae15be 100644 --- a/app/views/admin/keys/show.html.haml +++ b/app/views/admin/keys/show.html.haml @@ -1,2 +1,2 @@ -- page_title @key.title, "Keys" +- page_title @key.title, _("Keys") = render "profiles/keys/key_details", admin: true diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index f9d42d3f53b..1edfedb00f8 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Projects", admin_projects_path +- add_to_breadcrumbs _("Projects"), admin_projects_path - breadcrumb_title @project.full_name -- page_title @project.full_name, "Projects" +- page_title @project.full_name, _("Projects") - @content_class = "admin-projects" %h3.page-title diff --git a/app/views/admin/requests_profiles/index.html.haml b/app/views/admin/requests_profiles/index.html.haml index efc16bb4d3b..b7a4401d90e 100644 --- a/app/views/admin/requests_profiles/index.html.haml +++ b/app/views/admin/requests_profiles/index.html.haml @@ -1,4 +1,4 @@ -- page_title 'Requests Profiles' +- page_title _('Requests Profiles') %h3.page-title = page_title diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 59e28a3b244..08d65819476 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _('Runners') +- page_title _('Runners') .row .col-sm-6 diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 0120d4038b9..0c2b9bab357 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -9,6 +9,7 @@ %span.runner-state.runner-state-specific Specific +- page_title _("Runners") - add_to_breadcrumbs _("Runners"), admin_runners_path - breadcrumb_title "##{@runner.id}" diff --git a/app/views/admin/services/edit.html.haml b/app/views/admin/services/edit.html.haml index 558fe3ab15d..d13b5a34dac 100644 --- a/app/views/admin/services/edit.html.haml +++ b/app/views/admin/services/edit.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Service Templates", admin_application_settings_services_path +- add_to_breadcrumbs _("Service Templates"), admin_application_settings_services_path +- page_title @service.title, _("Service Templates") - breadcrumb_title @service.title -- page_title @service.title, "Service Templates" - @content_class = 'limit-container-width' unless fluid_layout = render 'form' diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml index b476c990462..ec343c38470 100644 --- a/app/views/admin/services/index.html.haml +++ b/app/views/admin/services/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Service Templates" +- page_title _("Service Templates") %h3.page-title Service templates %p.light= s_('AdminSettings|Service template allows you to set default values for integrations') diff --git a/app/views/admin/spam_logs/index.html.haml b/app/views/admin/spam_logs/index.html.haml index b45d3e4823b..40fbc559d72 100644 --- a/app/views/admin/spam_logs/index.html.haml +++ b/app/views/admin/spam_logs/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Spam Logs" +- page_title _("Spam Logs") %h3.page-title Spam Logs %hr - if @spam_logs.present? diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml index 3b6fd71500d..7d10e839cd6 100644 --- a/app/views/admin/users/edit.html.haml +++ b/app/views/admin/users/edit.html.haml @@ -1,4 +1,4 @@ -- page_title "Edit", @user.name, "Users" +- page_title _("Edit"), @user.name, _("Users") %h3.page-title Edit user: #{@user.name} %hr diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index ecbabab3e7f..69df8fa2198 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Users" +- page_title _("Users") .top-area.scrolling-tabs-container.inner-page-scroll-tabs .fade-left diff --git a/app/views/admin/users/keys.html.haml b/app/views/admin/users/keys.html.haml index 103bbb3b063..5f9d11af7c1 100644 --- a/app/views/admin/users/keys.html.haml +++ b/app/views/admin/users/keys.html.haml @@ -1,5 +1,5 @@ -- add_to_breadcrumbs "Users", admin_users_path +- add_to_breadcrumbs _("Users"), admin_users_path - breadcrumb_title @user.name -- page_title "SSH Keys", @user.name, "Users" +- page_title _("SSH Keys"), @user.name, _("Users") = render 'admin/users/head' = render 'profiles/keys/key_table', admin: true diff --git a/app/views/admin/users/new.html.haml b/app/views/admin/users/new.html.haml index bfc36ed7373..e5e6790b789 100644 --- a/app/views/admin/users/new.html.haml +++ b/app/views/admin/users/new.html.haml @@ -1,4 +1,4 @@ -- page_title "New User" +- page_title _("New User") %h3.page-title New user %hr diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml index e6da81831ab..5a11ee7e836 100644 --- a/app/views/admin/users/projects.html.haml +++ b/app/views/admin/users/projects.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Users", admin_users_path +- add_to_breadcrumbs _("Users"), admin_users_path - breadcrumb_title @user.name -- page_title "Groups and projects", @user.name, "Users" +- page_title _("Groups and projects"), @user.name, _("Users") = render 'admin/users/head' - if @user.groups.any? diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index c2ceb7e7cbb..2bc39a23b2d 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Users", admin_users_path +- add_to_breadcrumbs _("Users"), admin_users_path - breadcrumb_title @user.name -- page_title @user.name, "Users" +- page_title @user.name, _("Users") = render 'admin/users/head' .row diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml index d7306f5932d..1e93613e978 100644 --- a/app/views/dashboard/activity.html.haml +++ b/app/views/dashboard/activity.html.haml @@ -5,8 +5,8 @@ = render_dashboard_gold_trial(current_user) -- page_title "Activity" -- header_title "Activity", activity_dashboard_path +- page_title _("Activity") +- header_title _("Activity"), activity_dashboard_path = render "projects/last_push" = render 'dashboard/activity_head' diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index d1d8d970b59..9536ff940f5 100644 --- a/app/views/dashboard/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -1,6 +1,6 @@ - @hide_top_links = true -- page_title "Groups" -- header_title "Groups", dashboard_groups_path +- page_title _("Groups") +- header_title _("Groups"), dashboard_groups_path = render_dashboard_gold_trial(current_user) = render 'dashboard/groups_head' diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml index b9be6028b72..a0c1c314a85 100644 --- a/app/views/dashboard/milestones/index.html.haml +++ b/app/views/dashboard/milestones/index.html.haml @@ -1,6 +1,6 @@ - @hide_top_links = true -- page_title 'Milestones' -- header_title 'Milestones', dashboard_milestones_path +- page_title _('Milestones') +- header_title _('Milestones'), dashboard_milestones_path .page-title-holder.d-flex.align-items-center %h1.page-title= _('Milestones') diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index d2aa07bab22..2e7eab87af3 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -5,8 +5,8 @@ = render_dashboard_gold_trial(current_user) -- page_title "Projects" -- header_title "Projects", dashboard_projects_path +- page_title _("Projects") +- header_title _("Projects"), dashboard_projects_path = render "projects/last_push" - if show_projects?(@projects, params) diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index 2f0cc76f2e0..68457ab33f7 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -1,6 +1,6 @@ - @hide_top_links = true -- page_title "Snippets" -- header_title "Snippets", dashboard_snippets_path +- page_title _("Snippets") +- header_title _("Snippets"), dashboard_snippets_path - button_path = new_snippet_path if can?(current_user, :create_snippet) = render 'dashboard/snippets_head' diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index cfc637592d3..9fca580a870 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -1,6 +1,6 @@ - @hide_top_links = true -- page_title "To-Do List" -- header_title "To-Do List", dashboard_todos_path +- page_title _("To-Do List") +- header_title _("To-Do List"), dashboard_todos_path = render_dashboard_gold_trial(current_user) diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index 9fb5e27b692..fb00e1b4384 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -1,4 +1,4 @@ -- page_title "Sign up" +- page_title _("Sign up") - if experiment_enabled?(:signup_flow) .row .col-lg-7 diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index fd6d8f3f769..c466d2ce936 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,4 +1,4 @@ -- page_title "Sign in" +- page_title _("Sign in") #signin-container - if any_form_based_providers_enabled? diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml index d23c8301b10..bf861e30b3a 100644 --- a/app/views/explore/snippets/index.html.haml +++ b/app/views/explore/snippets/index.html.haml @@ -1,6 +1,6 @@ - @hide_top_links = true -- page_title "Snippets" -- header_title "Snippets", snippets_path +- page_title _("Snippets") +- header_title _("Snippets"), snippets_path - if current_user = render 'dashboard/snippets_head' diff --git a/app/views/groups/activity.html.haml b/app/views/groups/activity.html.haml index cb7dab26332..bc75fada937 100644 --- a/app/views/groups/activity.html.haml +++ b/app/views/groups/activity.html.haml @@ -1,7 +1,7 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity") -- page_title "Activity" +- page_title _("Activity") %section.activities = render 'activities' diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 2e58517fdc7..1e04b2761f6 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _("General Settings") +- page_title _("General Settings") - @content_class = "limit-container-width" unless fluid_layout - expanded = expanded_by_default? diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 1cb1cc45bdb..59432e5f015 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -1,6 +1,6 @@ - @can_bulk_update = can?(current_user, :admin_issue, @group) && @group.feature_available?(:group_bulk_edit) -- page_title "Issues" +- page_title _("Issues") = content_for :meta_tags do = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@group.name} issues") diff --git a/app/views/groups/labels/edit.html.haml b/app/views/groups/labels/edit.html.haml index 586b0f6ebfa..fbab4f8a250 100644 --- a/app/views/groups/labels/edit.html.haml +++ b/app/views/groups/labels/edit.html.haml @@ -1,6 +1,6 @@ - add_to_breadcrumbs _("Labels"), group_labels_path(@group) - breadcrumb_title _("Edit") -- page_title "Edit", @label.name, _("Labels") +- page_title _("Edit"), @label.name, _("Labels") %h3.page-title Edit Label diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 41c1d3e84b7..ec6694672ae 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -1,4 +1,4 @@ -- page_title 'Labels' +- page_title _('Labels') - can_admin_label = can?(current_user, :admin_label, @group) - search = params[:search] - subscribed = params[:subscribed] diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 0780fab513b..1828f850d35 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,6 +1,6 @@ - @can_bulk_update = can?(current_user, :admin_merge_request, @group) && @group.feature_available?(:group_bulk_edit) -- page_title "Merge Requests" +- page_title _("Merge Requests") - if group_merge_requests_count(state: 'all').zero? = render 'shared/empty_states/merge_requests', project_select_button: true diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index 03407adb57d..89ea0c6c7d9 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Milestones" +- page_title _("Milestones") .top-area = render 'shared/milestones_filter', counts: @milestone_states diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 8b01e54474a..c9cf8c5a76c 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -1,4 +1,5 @@ -- breadcrumb_title "Projects" +- breadcrumb_title _("Projects") +- page_title _("Projects") .card.prepend-top-default .card-header diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml index 8c9b859e127..366d7dd5afe 100644 --- a/app/views/groups/settings/ci_cd/show.html.haml +++ b/app/views/groups/settings/ci_cd/show.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title "CI / CD Settings" -- page_title "CI / CD" +- breadcrumb_title _("CI / CD Settings") +- page_title _("CI / CD") - expanded = expanded_by_default? - general_expanded = @group.errors.empty? ? expanded : true diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 7e5bf6ddde1..d896b28944e 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _("Details") +- page_title _("Groups") - @content_class = "limit-container-width" unless fluid_layout = content_for :meta_tags do diff --git a/app/views/help/instance_configuration.html.haml b/app/views/help/instance_configuration.html.haml index 99576d45f76..260566b1441 100644 --- a/app/views/help/instance_configuration.html.haml +++ b/app/views/help/instance_configuration.html.haml @@ -1,4 +1,4 @@ -- page_title 'Instance Configuration' +- page_title _('Instance Configuration') .documentation.md %h1 Instance Configuration diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index d71650ae50c..5c216ee1ec0 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -1,4 +1,4 @@ -- page_title "UI Development Kit", "Help" +- page_title _("UI Development Kit"), _("Help") - lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare." - link_classes = "flex-grow-1 mx-1 " diff --git a/app/views/ide/_show.html.haml b/app/views/ide/_show.html.haml index b871f0363f3..d0384fd50bc 100644 --- a/app/views/ide/_show.html.haml +++ b/app/views/ide/_show.html.haml @@ -1,5 +1,5 @@ - @body_class = 'ide-layout' -- page_title 'IDE' +- page_title _('IDE') - content_for :page_specific_javascripts do = stylesheet_link_tag 'page_bundles/ide' diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml index 2eac8d0c5a1..18ccad9ca30 100644 --- a/app/views/import/bitbucket_server/new.html.haml +++ b/app/views/import/bitbucket_server/new.html.haml @@ -1,7 +1,7 @@ - title = _('Bitbucket Server Import') - page_title title - breadcrumb_title title -- header_title "Projects", root_path +- header_title _("Projects"), root_path %h3.page-title = icon 'bitbucket-square', text: _('Import repositories from Bitbucket Server') diff --git a/app/views/import/bitbucket_server/status.html.haml b/app/views/import/bitbucket_server/status.html.haml index 7523b8f7b1c..c4b30a8c17e 100644 --- a/app/views/import/bitbucket_server/status.html.haml +++ b/app/views/import/bitbucket_server/status.html.haml @@ -1,5 +1,5 @@ -- page_title 'Bitbucket Server import' -- header_title 'Projects', root_path +- page_title _('Bitbucket Server import') +- header_title _('Projects'), root_path %h3.page-title %i.fa.fa-bitbucket-square diff --git a/app/views/import/manifest/new.html.haml b/app/views/import/manifest/new.html.haml index df00c4d2179..852f269f2ed 100644 --- a/app/views/import/manifest/new.html.haml +++ b/app/views/import/manifest/new.html.haml @@ -1,5 +1,5 @@ -- page_title "Manifest file import" -- header_title "Projects", root_path +- page_title _("Manifest file import") +- header_title _("Projects"), root_path %h3.page-title = _('Manifest file import') diff --git a/app/views/import/manifest/status.html.haml b/app/views/import/manifest/status.html.haml index 3d4abc32b88..e85162ad1b4 100644 --- a/app/views/import/manifest/status.html.haml +++ b/app/views/import/manifest/status.html.haml @@ -1,5 +1,5 @@ -- page_title "Manifest import" -- header_title "Projects", root_path +- page_title _("Manifest import") +- header_title _("Projects"), root_path - provider = 'manifest' %h3.page-title diff --git a/app/views/instance_statistics/cohorts/index.html.haml b/app/views/instance_statistics/cohorts/index.html.haml index 5333f8b7a1f..771e988e256 100644 --- a/app/views/instance_statistics/cohorts/index.html.haml +++ b/app/views/instance_statistics/cohorts/index.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _("Cohorts") +- page_title _("Cohorts") - if @cohorts = render 'cohorts_table' diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index 3cbfb24a868..4bfac76ec5b 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -15,6 +15,7 @@ %li= link_to _('New project'), new_project_path(namespace_id: @group.id) - if create_group_subgroup %li= link_to _('New subgroup'), new_group_path(parent_id: @group.id) + = render_if_exists 'layouts/header/create_epic_new_dropdown_item' %li.divider %li.dropdown-bold-header GitLab diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml index cde2b467392..6cc53ba3342 100644 --- a/app/views/layouts/snippets.html.haml +++ b/app/views/layouts/snippets.html.haml @@ -1,3 +1,4 @@ +- page_title _("Snippets") - header_title _("Snippets"), snippets_path - snippets_upload_path = snippets_upload_path(@snippet, current_user) diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 78fdcdef3c4..f5ea805cf85 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title s_("Profiles|Edit Profile") +- page_title s_("Profiles|Edit Profile") - @content_class = "limit-container-width" unless fluid_layout - gravatar_link = link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index 7abac2d14e4..ff56cb53720 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -1,5 +1,5 @@ - breadcrumb_title _('Artifacts') -- page_title @path.presence, 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs' +- page_title @path.presence, _('Artifacts'), "#{@build.name} (##{@build.id})", _('Jobs') = render "projects/jobs/header" diff --git a/app/views/projects/artifacts/file.html.haml b/app/views/projects/artifacts/file.html.haml index 808b4acc8f3..1ad70506be4 100644 --- a/app/views/projects/artifacts/file.html.haml +++ b/app/views/projects/artifacts/file.html.haml @@ -1,4 +1,4 @@ -- page_title @path, 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs' +- page_title @path, _('Artifacts'), "#{@build.name} (##{@build.id})", _('Jobs') = render "projects/jobs/header" diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 0591c3180ea..a2d6b2e18a9 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -1,4 +1,4 @@ -- page_title "Blame", @blob.path, @ref +- page_title _("Blame"), @blob.path, @ref - link_icon = icon("link") #blob-content-holder.tree-holder diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 3d84adbc49a..1319c58eb38 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title "Repository" -- page_title "Edit", @blob.path, @ref +- breadcrumb_title _("Repository") +- page_title _("Edit"), @blob.path, @ref - unless Feature.enabled?(:monaco_blobs) - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index f9abcffeeb4..2420c4a4bd5 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title "Repository" -- page_title "New File", @path.presence, @ref +- breadcrumb_title _("Repository") +- page_title _("New File"), @path.presence, @ref - unless Feature.enabled?(:monaco_blobs) - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index af8887b0c39..97e46aaa710 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -1,4 +1,4 @@ -- page_title "New Branch" +- page_title _("New Branch") - default_ref = params[:ref] || @project.default_branch - if @error diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 02f2b104ce3..93ee1bed809 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title "Compare Revisions" -- page_title "Compare" +- breadcrumb_title _("Compare Revisions") +- page_title _("Compare") %h3.page-title = _("Compare Git revisions") diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index b6c30c680e4..090fc602ebb 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -1,4 +1,4 @@ -- page_title "Value Stream Analytics" +- page_title _("Value Stream Analytics") #cycle-analytics{ "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } - if @cycle_analytics_no_data diff --git a/app/views/projects/deploy_keys/edit.html.haml b/app/views/projects/deploy_keys/edit.html.haml index 0ce93eef369..7fa7036245c 100644 --- a/app/views/projects/deploy_keys/edit.html.haml +++ b/app/views/projects/deploy_keys/edit.html.haml @@ -1,4 +1,4 @@ -- page_title 'Edit Deploy Key' +- page_title _('Edit Deploy Key') %h3.page-title= _('Edit Deploy Key') %hr diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 6b1455acd08..68c2af336b3 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,5 +1,6 @@ - @content_class = "limit-container-width" unless fluid_layout - breadcrumb_title _("Details") +- page_title _("Details") = render partial: 'flash_messages', locals: { project: @project } diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index 971107675ab..3b0555908f6 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -1,4 +1,4 @@ -- page_title "Find File", @ref +- page_title _("Find File"), @ref .file-finder-holder.tree-holder.clearfix.js-file-finder{ 'data-file-find-url': "#{escape_javascript(project_files_path(@project, @ref, format: :json))}", 'data-find-tree-url': escape_javascript(project_tree_path(@project, @ref)), 'data-blob-url-template': escape_javascript(project_blob_path(@project, @id || @commit.id)) } .nav-block diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index bd0ab2c19f2..58981ca1556 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -1,4 +1,4 @@ -- page_title "Import repository" +- page_title _("Import repository") %h3.page-title Import repository diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml index 1b7d878c38c..353ff9c1cc2 100644 --- a/app/views/projects/issues/edit.html.haml +++ b/app/views/projects/issues/edit.html.haml @@ -1,4 +1,4 @@ -- page_title "Edit", "#{@issue.title} (#{@issue.to_reference})", "Issues" +- page_title _("Edit"), "#{@issue.title} (#{@issue.to_reference})", _("Issues") %h3.page-title Edit Issue ##{@issue.iid} diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 826a62e39d3..cfc423da57a 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -1,6 +1,6 @@ - @can_bulk_update = can?(current_user, :admin_issue, @project) -- page_title "Issues" +- page_title _("Issues") - new_issue_email = @project.new_issuable_address(current_user, 'issue') = content_for :meta_tags do diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml index 5acb2af08e4..4f537ee8014 100644 --- a/app/views/projects/jobs/index.html.haml +++ b/app/views/projects/jobs/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Jobs" +- page_title _("Jobs") .top-area - build_path_proc = ->(scope) { project_jobs_path(@project, scope: scope) } diff --git a/app/views/projects/jobs/terminal.html.haml b/app/views/projects/jobs/terminal.html.haml index 5439a4b5d5c..01f40543926 100644 --- a/app/views/projects/jobs/terminal.html.haml +++ b/app/views/projects/jobs/terminal.html.haml @@ -1,7 +1,7 @@ -- add_to_breadcrumbs 'Jobs', project_jobs_path(@project) +- add_to_breadcrumbs _('Jobs'), project_jobs_path(@project) - add_to_breadcrumbs "##{@build.id}", project_job_path(@project, @build) -- breadcrumb_title 'Terminal' -- page_title 'Terminal', "#{@build.name} (##{@build.id})", 'Jobs' +- breadcrumb_title _('Terminal') +- page_title _('Terminal'), "#{@build.name} (##{@build.id})", _('Jobs') - content_for :page_specific_javascripts do = stylesheet_link_tag "xterm.css" diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index b7996f0dad1..343900359b4 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Labels", project_labels_path(@project) -- breadcrumb_title "Edit" -- page_title "Edit", @label.name, "Labels" +- add_to_breadcrumbs _("Labels"), project_labels_path(@project) +- breadcrumb_title _("Edit") +- page_title _("Edit"), @label.name, _("Labels") %h3.page-title Edit Label diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 760d81136c6..93ece26357e 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,4 +1,4 @@ -- page_title "Labels" +- page_title _("Labels") - can_admin_label = can?(current_user, :admin_label, @project) - search = params[:search] - subscribed = params[:subscribed] diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 96ce0eba2c6..38bd6102437 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Labels", project_labels_path(@project) -- breadcrumb_title "New" -- page_title "New Label" +- add_to_breadcrumbs _("Labels"), project_labels_path(@project) +- breadcrumb_title _("New") +- page_title _("New Label") %h3.page-title New Label diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml index d933675eac5..6c23661fb86 100644 --- a/app/views/projects/merge_requests/conflicts/show.html.haml +++ b/app/views/projects/merge_requests/conflicts/show.html.haml @@ -1,4 +1,4 @@ -- page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" +- page_title _("Merge Conflicts"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests") - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/merge_requests/creations/new.html.haml b/app/views/projects/merge_requests/creations/new.html.haml index 0f618826305..ad4980fa57f 100644 --- a/app/views/projects/merge_requests/creations/new.html.haml +++ b/app/views/projects/merge_requests/creations/new.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Merge Requests", project_merge_requests_path(@project) -- breadcrumb_title "New" -- page_title "New Merge Request" +- add_to_breadcrumbs _("Merge Requests"), project_merge_requests_path(@project) +- breadcrumb_title _("New") +- page_title _("New Merge Request") - if @merge_request.can_be_created && !params[:change_branches] = render 'new_submit' diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml index 318c9d809c1..a4bb790ce0b 100644 --- a/app/views/projects/merge_requests/edit.html.haml +++ b/app/views/projects/merge_requests/edit.html.haml @@ -1,4 +1,4 @@ -- page_title "Edit", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" +- page_title _("Edit"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests") %h3.page-title Edit Merge Request #{@merge_request.to_reference} diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 4e30f09b9a2..36b1cf0796f 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -2,7 +2,7 @@ - merge_project = merge_request_source_project_for_project(@project) - new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project -- page_title "Merge Requests" +- page_title _("Merge Requests") - new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request') = render 'projects/last_push' diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml index 749228a9664..7b831aa2d01 100644 --- a/app/views/projects/merge_requests/invalid.html.haml +++ b/app/views/projects/merge_requests/invalid.html.haml @@ -1,4 +1,4 @@ -- page_title "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" +- page_title "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests") .merge-request = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 90bc2504cb4..ba97bb81c56 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -1,8 +1,8 @@ - @gfm_form = true - @content_class = "limit-container-width" unless fluid_layout -- add_to_breadcrumbs "Merge Requests", project_merge_requests_path(@project) +- add_to_breadcrumbs _("Merge Requests"), project_merge_requests_path(@project) - breadcrumb_title @merge_request.to_reference -- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests" +- page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge Requests") - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes - suggest_changes_help_path = help_page_path('user/discussions/index.md', anchor: 'suggest-changes') diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 6821453cffa..b8b4507c80f 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,5 +1,5 @@ -- breadcrumb_title "Graph" -- page_title "Graph", @ref +- breadcrumb_title _("Graph") +- page_title _("Graph"), @ref = render "head" %div{ class: container_class } .project-network diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml index 08772a0188b..7748aadf44d 100644 --- a/app/views/projects/no_repo.html.haml +++ b/app/views/projects/no_repo.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _("Details") +- page_title _("Details") %h2 %i.fa.fa-warning diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 4b7810ea357..fc69b390bde 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,4 +1,4 @@ -- page_title 'Pages' +- page_title _('Pages') - if @project.pages_enabled? %h3.page-title.with-button diff --git a/app/views/projects/protected_branches/show.html.haml b/app/views/projects/protected_branches/show.html.haml index ffaf118a5e3..06578c94079 100644 --- a/app/views/projects/protected_branches/show.html.haml +++ b/app/views/projects/protected_branches/show.html.haml @@ -1,4 +1,4 @@ -- page_title @protected_ref.name, "Protected Branches" +- page_title @protected_ref.name, _("Protected Branches") .row.prepend-top-default.append-bottom-default .col-lg-3 diff --git a/app/views/projects/protected_tags/show.html.haml b/app/views/projects/protected_tags/show.html.haml index 6f4535a0b3f..227aac77583 100644 --- a/app/views/projects/protected_tags/show.html.haml +++ b/app/views/projects/protected_tags/show.html.haml @@ -1,4 +1,4 @@ -- page_title @protected_ref.name, "Protected Tags" +- page_title @protected_ref.name, _("Protected Tags") .row.prepend-top-default.append-bottom-default .col-lg-3 diff --git a/app/views/projects/serverless/functions/index.html.haml b/app/views/projects/serverless/functions/index.html.haml index 2f1da453c0a..b21965915a2 100644 --- a/app/views/projects/serverless/functions/index.html.haml +++ b/app/views/projects/serverless/functions/index.html.haml @@ -1,6 +1,6 @@ - @content_class = "limit-container-width" unless fluid_layout -- breadcrumb_title 'Serverless' -- page_title 'Serverless' +- breadcrumb_title _('Serverless') +- page_title _('Serverless') - status_path = project_serverless_functions_path(@project, format: :json) - clusters_path = project_clusters_path(@project) diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 17bc10af58a..732b0d6c9e1 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,4 +1,5 @@ - breadcrumb_title _("Details") +- page_title _("Projects") - @content_class = "limit-container-width" unless fluid_layout = content_for :meta_tags do diff --git a/app/views/projects/tags/releases/edit.html.haml b/app/views/projects/tags/releases/edit.html.haml index a3746808440..55564808e6e 100644 --- a/app/views/projects/tags/releases/edit.html.haml +++ b/app/views/projects/tags/releases/edit.html.haml @@ -1,6 +1,6 @@ -- add_to_breadcrumbs "Tags", project_tags_path(@project) +- add_to_breadcrumbs _("Tags"), project_tags_path(@project) - breadcrumb_title @tag.name -- page_title "Edit", @tag.name, "Tags" +- page_title _("Edit"), @tag.name, _("Tags") .sub-header-block.no-bottom-space .oneline diff --git a/app/views/projects/triggers/edit.html.haml b/app/views/projects/triggers/edit.html.haml index e287f05fe6a..b1bc9b0f900 100644 --- a/app/views/projects/triggers/edit.html.haml +++ b/app/views/projects/triggers/edit.html.haml @@ -1,4 +1,4 @@ -- page_title "Trigger" +- page_title _("Trigger") .row.prepend-top-default.append-bottom-default .col-lg-12 diff --git a/app/views/shared/runners/show.html.haml b/app/views/shared/runners/show.html.haml index f62eed694d2..8a78f12bdd8 100644 --- a/app/views/shared/runners/show.html.haml +++ b/app/views/shared/runners/show.html.haml @@ -1,4 +1,4 @@ -- page_title "#{@runner.description} ##{@runner.id}", "Runners" +- page_title "#{@runner.description} ##{@runner.id}", _("Runners") %h3.page-title Runner ##{@runner.id} diff --git a/changelogs/unreleased/217587-ignore-title-description-from-services.yml b/changelogs/unreleased/217587-ignore-title-description-from-services.yml new file mode 100644 index 00000000000..6602b19e979 --- /dev/null +++ b/changelogs/unreleased/217587-ignore-title-description-from-services.yml @@ -0,0 +1,5 @@ +--- +title: Remove the ability to customize the title and description of some integrations (Bugzilla, Custom Issue Tracker, Redmine, and YouTrack). +merge_request: 33298 +author: +type: removed diff --git a/changelogs/unreleased/220232-get-all-jira-projects.yml b/changelogs/unreleased/220232-get-all-jira-projects.yml new file mode 100644 index 00000000000..b0620305a02 --- /dev/null +++ b/changelogs/unreleased/220232-get-all-jira-projects.yml @@ -0,0 +1,5 @@ +--- +title: Expose all Jira projects endpoint through a GraphQL +merge_request: 33861 +author: +type: added diff --git a/changelogs/unreleased/dmishunov-editor-lite-extensions.yml b/changelogs/unreleased/dmishunov-editor-lite-extensions.yml new file mode 100644 index 00000000000..e8eb64bcffe --- /dev/null +++ b/changelogs/unreleased/dmishunov-editor-lite-extensions.yml @@ -0,0 +1,5 @@ +--- +title: Support extensibility for Editor Lite +merge_request: 35008 +author: +type: added diff --git a/changelogs/unreleased/services-usage-7.yml b/changelogs/unreleased/services-usage-7.yml new file mode 100644 index 00000000000..ca3900f36a3 --- /dev/null +++ b/changelogs/unreleased/services-usage-7.yml @@ -0,0 +1,5 @@ +--- +title: Add DestroyService for GPG keys and use for deleting GPG keys via API +merge_request: 34935 +author: Rajendra Kadam +type: fixed diff --git a/changelogs/unreleased/vij-raw-snippet-blobs.yml b/changelogs/unreleased/vij-raw-snippet-blobs.yml new file mode 100644 index 00000000000..0576de8bd33 --- /dev/null +++ b/changelogs/unreleased/vij-raw-snippet-blobs.yml @@ -0,0 +1,5 @@ +--- +title: Add new raw snippet blob endpoint +merge_request: 33938 +author: +type: added diff --git a/config/routes/project.rb b/config/routes/project.rb index 78dcc189d5b..d00e0ab1d04 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -313,6 +313,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end + get '/snippets/:snippet_id/raw/:ref/*path', + to: 'snippets/blobs#raw', + format: false, + as: :snippet_blob_raw, + constraints: { snippet_id: /\d+/ } + draw :issues draw :merge_requests draw :pipelines diff --git a/config/routes/snippets.rb b/config/routes/snippets.rb index ba6da3ac57e..259e88ba7b2 100644 --- a/config/routes/snippets.rb +++ b/config/routes/snippets.rb @@ -17,5 +17,14 @@ resources :snippets, concerns: :awardable do end end +# Use this /-/ scope for all new snippet routes. +scope path: '-' do + get '/snippets/:snippet_id/raw/:ref/*path', + to: 'snippets/blobs#raw', + as: :snippet_raw, + format: false, + constraints: { snippet_id: /\d+/ } +end + get '/s/:username', to: redirect('users/%{username}/snippets'), constraints: { username: /[a-zA-Z.0-9_\-]+(? Array | Identifiers of the vulnerability. | | `location` | VulnerabilityLocation | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability | +| `primaryIdentifier` | VulnerabilityIdentifier | Primary identifier of the vulnerability. | | `project` | Project | The project on which the vulnerability was found | | `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION) | +| `scanner` | VulnerabilityScanner | Scanner metadata for the vulnerability. | | `severity` | VulnerabilitySeverity | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) | | `state` | VulnerabilityState | State of the vulnerability (DETECTED, DISMISSED, RESOLVED, CONFIRMED) | | `title` | String | Title of the vulnerability | @@ -2024,6 +2027,17 @@ Represents a vulnerability. | `userPermissions` | VulnerabilityPermissions! | Permissions for the current user on the resource | | `vulnerabilityPath` | String | URL to the vulnerability's details page | +## VulnerabilityIdentifier + +Represents a vulnerability identifier. + +| Name | Type | Description | +| --- | ---- | ---------- | +| `externalId` | String | External ID of the vulnerability identifier | +| `externalType` | String | External type of the vulnerability identifier | +| `name` | String | Name of the vulnerability identifier | +| `url` | String | URL of the vulnerability identifier | + ## VulnerabilityIssueLink Represents an issue link of a vulnerability. @@ -2103,6 +2117,15 @@ Check permissions for the current user on a vulnerability | `readVulnerabilityFeedback` | Boolean! | Indicates the user can perform `read_vulnerability_feedback` on this resource | | `updateVulnerabilityFeedback` | Boolean! | Indicates the user can perform `update_vulnerability_feedback` on this resource | +## VulnerabilityScanner + +Represents a vulnerability scanner. + +| Name | Type | Description | +| --- | ---- | ---------- | +| `externalId` | String | External ID of the vulnerability scanner | +| `name` | String | Name of the vulnerability scanner | + ## VulnerabilitySeveritiesCount Represents vulnerability counts by severity diff --git a/doc/api/issues.md b/doc/api/issues.md index f640300e3ae..9d6bbf7f0e1 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -6,8 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Issues API -Every API call to issues must be authenticated. - If a user is not a member of a project and the project is private, a `GET` request on that project will result in a `404` status code. @@ -188,6 +186,9 @@ the `weight` parameter: Get a list of a group's issues. +If the group is private, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens](../user/profile/personal_access_tokens.md). + ```plaintext GET /groups/:id/issues GET /groups/:id/issues?state=opened @@ -343,6 +344,9 @@ the `weight` parameter: Get a list of a project's issues. +If the project is private, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens](../user/profile/personal_access_tokens.md). + ```plaintext GET /projects/:id/issues GET /projects/:id/issues?state=opened @@ -504,6 +508,9 @@ the `weight` parameter: Get a single project issue. +If the project is private or the issue is confidential, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens](../user/profile/personal_access_tokens.md). + ```plaintext GET /projects/:id/issues/:issue_iid ``` @@ -1413,6 +1420,9 @@ Example response: ## Get time tracking stats +If the project is private or the issue is confidential, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens](../user/profile/personal_access_tokens.md). + ```plaintext GET /projects/:id/issues/:issue_iid/time_stats ``` @@ -1441,6 +1451,9 @@ Example response: Get all the merge requests that are related to the issue. +If the project is private or the issue is confidential, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens](../user/profile/personal_access_tokens.md). + ```plaintext GET /projects/:id/issues/:issue_id/related_merge_requests ``` @@ -1597,6 +1610,9 @@ Example response: Get all the merge requests that will close issue when merged. +If the project is private or the issue is confidential, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens](../user/profile/personal_access_tokens.md). + ```plaintext GET /projects/:id/issues/:issue_iid/closed_by ``` @@ -1670,6 +1686,9 @@ Example response: ## Participants on issues +If the project is private or the issue is confidential, credentials will need to be provided for authorization. +The preferred way to do this, is by using [personal access tokens](../user/profile/personal_access_tokens.md). + ```plaintext GET /projects/:id/issues/:issue_iid/participants ``` diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md index 7ae3c9e9de2..4b3bf91030e 100644 --- a/doc/development/sidekiq_style_guide.md +++ b/doc/development/sidekiq_style_guide.md @@ -401,6 +401,8 @@ default weight, which is 1. ## Worker context +> - [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/9) in GitLab 12.8. + To have some more information about workers in the logs, we add [metadata to the jobs in the form of an `ApplicationContext`](logging.md#logging-context-metadata-through-rails-or-grape-requests). @@ -417,27 +419,27 @@ need to do anything. There are however some instances when there would be no context present when the job is scheduled, or the context that is present is -likely to be incorrect. For these instances we've added rubocop-rules +likely to be incorrect. For these instances, we've added Rubocop rules to draw attention and avoid incorrect metadata in our logs. As with most our cops, there are perfectly valid reasons for disabling them. In this case it could be that the context from the request is correct. Or maybe you've specified a context already in a way that -isn't picked up by the cops. In any case, please leave a code-comment +isn't picked up by the cops. In any case, leave a code comment pointing to which context will be used when disabling the cops. -When you do provide objects to the context, please make sure that the -route for namespaces and projects is pre-loaded. This can be done using +When you do provide objects to the context, make sure that the +route for namespaces and projects is pre-loaded. This can be done by using the `.with_route` scope defined on all `Routable`s. -### Cron-Workers +### Cron workers -The context is automatically cleared for workers in the cronjob-queue -(which `include CronjobQueue`), even when scheduling them from +The context is automatically cleared for workers in the Cronjob queue +(`include CronjobQueue`), even when scheduling them from requests. We do this to avoid incorrect metadata when other jobs are -scheduled from the cron-worker. +scheduled from the cron worker. -Cron-Workers themselves run instance wide, so they aren't scoped to +Cron workers themselves run instance wide, so they aren't scoped to users, namespaces, projects, or other resources that should be added to the context. @@ -449,46 +451,46 @@ somewhere within the worker: 1. Wrap the code that schedules jobs in the `with_context` helper: -```ruby - def perform - deletion_cutoff = Gitlab::CurrentSettings - .deletion_adjourned_period.days.ago.to_date - projects = Project.with_route.with_namespace - .aimed_for_deletion(deletion_cutoff) + ```ruby + def perform + deletion_cutoff = Gitlab::CurrentSettings + .deletion_adjourned_period.days.ago.to_date + projects = Project.with_route.with_namespace + .aimed_for_deletion(deletion_cutoff) - projects.find_each(batch_size: 100).with_index do |project, index| - delay = index * INTERVAL + projects.find_each(batch_size: 100).with_index do |project, index| + delay = index * INTERVAL - with_context(project: project) do - AdjournedProjectDeletionWorker.perform_in(delay, project.id) - end - end - end -``` + with_context(project: project) do + AdjournedProjectDeletionWorker.perform_in(delay, project.id) + end + end + end + ``` 1. Use the a batch scheduling method that provides context: -```ruby - def schedule_projects_in_batch(projects) - ProjectImportScheduleWorker.bulk_perform_async_with_contexts( - projects, - arguments_proc: -> (project) { project.id }, - context_proc: -> (project) { { project: project } } - ) - end -``` + ```ruby + def schedule_projects_in_batch(projects) + ProjectImportScheduleWorker.bulk_perform_async_with_contexts( + projects, + arguments_proc: -> (project) { project.id }, + context_proc: -> (project) { { project: project } } + ) + end + ``` -or when scheduling with delays: + Or, when scheduling with delays: -```ruby - diffs.each_batch(of: BATCH_SIZE) do |diffs, index| - DeleteDiffFilesWorker - .bulk_perform_in_with_contexts(index * 5.minutes, - diffs, - arguments_proc: -> (diff) { diff.id }, - context_proc: -> (diff) { { project: diff.merge_request.target_project } }) - end -``` + ```ruby + diffs.each_batch(of: BATCH_SIZE) do |diffs, index| + DeleteDiffFilesWorker + .bulk_perform_in_with_contexts(index * 5.minutes, + diffs, + arguments_proc: -> (diff) { diff.id }, + context_proc: -> (diff) { { project: diff.merge_request.target_project } }) + end + ``` ### Jobs scheduled in bulk @@ -512,11 +514,11 @@ For example: Each object from the enumerable in the first argument is yielded into 2 blocks: -The `arguments_proc` which needs to return the list of arguments the -job needs to be scheduled with. +- The `arguments_proc` which needs to return the list of arguments the + job needs to be scheduled with. -The `context_proc` which needs to return a hash with the context -information for the job. +- The `context_proc` which needs to return a hash with the context + information for the job. ## Arguments logging diff --git a/doc/user/project/integrations/bugzilla.md b/doc/user/project/integrations/bugzilla.md index de43c504b05..6d44c56743e 100644 --- a/doc/user/project/integrations/bugzilla.md +++ b/doc/user/project/integrations/bugzilla.md @@ -6,7 +6,6 @@ in the table below. | Field | Description | | ----- | ----------- | -| `description` | A name for the issue tracker (to differentiate between instances, for example) | | `project_url` | The URL to the project in Bugzilla which is being linked to this GitLab project. Note that the `project_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | | `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | | `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | diff --git a/doc/user/project/integrations/custom_issue_tracker.md b/doc/user/project/integrations/custom_issue_tracker.md index 4eaf3a0d4b4..7d15ae82b6f 100644 --- a/doc/user/project/integrations/custom_issue_tracker.md +++ b/doc/user/project/integrations/custom_issue_tracker.md @@ -11,8 +11,6 @@ To enable the Custom Issue Tracker integration in a project: | Field | Description | | --------------- | ----------- | - | **Title** | A title for the issue tracker (for example, to differentiate between instances). | - | **Description** | A name for the issue tracker (for example, to differentiate between instances). | | **Project URL** | The URL to the project in the custom issue tracker. | | **Issues URL** | The URL to the issue in the issue tracker project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. For example, `https://customissuetracker.com/project-name/:id`. | | **New issue URL** | Currently unused. Will be changed in a future release. | diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md index 9e1cdf0490c..c92ddf38ad2 100644 --- a/doc/user/project/integrations/redmine.md +++ b/doc/user/project/integrations/redmine.md @@ -7,7 +7,6 @@ | Field | Description | | ----- | ----------- | - | `description` | A name for the issue tracker (to differentiate between instances, for example) | | `project_url` | The URL to the project in Redmine which is being linked to this GitLab project | | `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | | `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** | diff --git a/doc/user/project/integrations/youtrack.md b/doc/user/project/integrations/youtrack.md index 119a53f5c35..e067ab6071e 100644 --- a/doc/user/project/integrations/youtrack.md +++ b/doc/user/project/integrations/youtrack.md @@ -13,7 +13,6 @@ To enable YouTrack integration in a project: | Field | Description | |:----------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| - | **Description** | Name for the issue tracker (to differentiate between instances, for example). | | **Project URL** | URL to the project in YouTrack which is being linked to this GitLab project. | | **Issues URL** | URL to the issue in YouTrack project that is linked to this GitLab project. Note that the **Issues URL** requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | diff --git a/lib/api/users.rb b/lib/api/users.rb index 85a33c608e5..192eeb691ff 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -374,9 +374,10 @@ module API key = user.gpg_keys.find_by(id: params[:key_id]) not_found!('GPG Key') unless key - key.destroy - - no_content! + destroy_conditionally!(key) do |key| + destroy_service = ::GpgKeys::DestroyService.new(current_user) + destroy_service.execute(key) + end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index f0b733d7e95..7d2a26b2d39 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -262,9 +262,11 @@ excluded_attributes: - :token - :token_encrypted services: + - :description - :inherit_from_id - :instance - :template + - :title error_tracking_setting: - :encrypted_token - :encrypted_token_iv diff --git a/lib/gitlab/suggestions/file_suggestion.rb b/lib/gitlab/suggestions/file_suggestion.rb index 73b9800f0b8..7805b27902d 100644 --- a/lib/gitlab/suggestions/file_suggestion.rb +++ b/lib/gitlab/suggestions/file_suggestion.rb @@ -7,17 +7,14 @@ module Gitlab SuggestionForDifferentFileError = Class.new(StandardError) - def initialize - @suggestions = [] - end + attr_reader :file_path + attr_reader :blob + attr_reader :suggestions - def add_suggestion(new_suggestion) - if for_different_file?(new_suggestion) - raise SuggestionForDifferentFileError, - 'Only add suggestions for the same file.' - end - - suggestions << new_suggestion + def initialize(file_path, suggestions) + @file_path = file_path + @suggestions = suggestions.sort_by(&:from_line_index) + @blob = suggestions.first&.diff_file&.new_blob end def line_conflict? @@ -30,18 +27,8 @@ module Gitlab @new_content ||= _new_content end - def file_path - @file_path ||= _file_path - end - private - attr_accessor :suggestions - - def blob - first_suggestion&.diff_file&.new_blob - end - def blob_data_lines blob.load_all_data! blob.data.lines @@ -53,31 +40,19 @@ module Gitlab def _new_content current_content.tap do |content| + # NOTE: We need to cater for line number changes when the range is more than one line. + offset = 0 + suggestions.each do |suggestion| - range = line_range(suggestion) + range = line_range(suggestion, offset) content[range] = suggestion.to_content + offset += range.count - 1 end end.join end - def line_range(suggestion) - suggestion.from_line_index..suggestion.to_line_index - end - - def for_different_file?(suggestion) - file_path && file_path != suggestion_file_path(suggestion) - end - - def suggestion_file_path(suggestion) - suggestion&.diff_file&.file_path - end - - def first_suggestion - suggestions.first - end - - def _file_path - suggestion_file_path(first_suggestion) + def line_range(suggestion, offset = 0) + (suggestion.from_line_index - offset)..(suggestion.to_line_index - offset) end def _line_conflict? diff --git a/lib/gitlab/suggestions/suggestion_set.rb b/lib/gitlab/suggestions/suggestion_set.rb index 22abef98bf0..abb05ba56a7 100644 --- a/lib/gitlab/suggestions/suggestion_set.rb +++ b/lib/gitlab/suggestions/suggestion_set.rb @@ -26,10 +26,10 @@ module Gitlab end def actions - @actions ||= suggestions_per_file.map do |file_path, file_suggestion| + @actions ||= suggestions_per_file.map do |file_suggestion| { action: 'update', - file_path: file_path, + file_path: file_suggestion.file_path, content: file_suggestion.new_content } end @@ -50,19 +50,9 @@ module Gitlab end def _suggestions_per_file - suggestions.each_with_object({}) do |suggestion, result| - file_path = suggestion.diff_file.file_path - file_suggestion = result[file_path] ||= FileSuggestion.new - file_suggestion.add_suggestion(suggestion) - end - end - - def file_suggestions - suggestions_per_file.values - end - - def first_file_suggestion - file_suggestions.first + suggestions + .group_by { |suggestion| suggestion.diff_file.file_path } + .map { |file_path, group| FileSuggestion.new(file_path, group) } end def _error_message @@ -72,7 +62,7 @@ module Gitlab return message if message end - has_line_conflict = file_suggestions.any? do |file_suggestion| + has_line_conflict = suggestions_per_file.any? do |file_suggestion| file_suggestion.line_conflict? end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b9e1e25a769..8e9ed753f09 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1526,6 +1526,9 @@ msgstr "" msgid "Admin Note" msgstr "" +msgid "Admin Notifications" +msgstr "" + msgid "Admin Overview" msgstr "" @@ -3064,6 +3067,9 @@ msgstr "" msgid "Audit Events is a way to keep track of important events that happened in GitLab." msgstr "" +msgid "Audit Log" +msgstr "" + msgid "AuditEvents|(removed)" msgstr "" @@ -3499,9 +3505,15 @@ msgstr "" msgid "Bitbucket Server Import" msgstr "" +msgid "Bitbucket Server import" +msgstr "" + msgid "Bitbucket import" msgstr "" +msgid "Blame" +msgstr "" + msgid "Blocked" msgstr "" @@ -3715,6 +3727,9 @@ msgstr "" msgid "Broadcast Message was successfully updated." msgstr "" +msgid "Broadcast Messages" +msgstr "" + msgid "Browse Directory" msgstr "" @@ -8159,6 +8174,9 @@ msgstr "" msgid "Edit Release" msgstr "" +msgid "Edit Slack integration" +msgstr "" + msgid "Edit Snippet" msgstr "" @@ -8246,6 +8264,9 @@ msgstr "" msgid "Email" msgstr "" +msgid "Email Notification" +msgstr "" + msgid "Email address" msgstr "" @@ -9965,6 +9986,9 @@ msgstr "" msgid "Filter..." msgstr "" +msgid "Find File" +msgstr "" + msgid "Find by path" msgstr "" @@ -11459,6 +11483,9 @@ msgstr "" msgid "Groups (%{groups})" msgstr "" +msgid "Groups and projects" +msgstr "" + msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}." msgstr "" @@ -11766,6 +11793,9 @@ msgstr "" msgid "ID:" msgstr "" +msgid "IDE" +msgstr "" + msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeSandbox Live Preview." msgstr "" @@ -12221,6 +12251,9 @@ msgid_plural "Instances" msgstr[0] "" msgstr[1] "" +msgid "Instance Configuration" +msgstr "" + msgid "Instance Statistics visibility" msgstr "" @@ -12821,6 +12854,9 @@ msgstr "" msgid "Keep divergent refs" msgstr "" +msgid "Kerberos access denied" +msgstr "" + msgid "Key" msgstr "" @@ -12836,6 +12872,9 @@ msgstr "" msgid "Keyboard shortcuts" msgstr "" +msgid "Keys" +msgstr "" + msgid "Ki" msgstr "" @@ -12878,6 +12917,9 @@ msgstr "" msgid "LDAP" msgstr "" +msgid "LDAP Synchronization" +msgstr "" + msgid "LDAP settings" msgstr "" @@ -13616,6 +13658,9 @@ msgstr "" msgid "Manifest file import" msgstr "" +msgid "Manifest import" +msgstr "" + msgid "Manual job" msgstr "" @@ -13859,6 +13904,9 @@ msgstr "" msgid "Merge (when the pipeline succeeds)" msgstr "" +msgid "Merge Conflicts" +msgstr "" + msgid "Merge Request" msgstr "" @@ -14778,9 +14826,18 @@ msgstr "" msgid "New Application" msgstr "" +msgid "New Branch" +msgstr "" + +msgid "New Deploy Key" +msgstr "" + msgid "New Environment" msgstr "" +msgid "New File" +msgstr "" + msgid "New Geo Node" msgstr "" @@ -14807,6 +14864,9 @@ msgstr "" msgid "New Label" msgstr "" +msgid "New Merge Request" +msgstr "" + msgid "New Milestone" msgstr "" @@ -14825,6 +14885,9 @@ msgstr "" msgid "New Snippet" msgstr "" +msgid "New User" +msgstr "" + msgid "New branch" msgstr "" @@ -18169,6 +18232,9 @@ msgstr "" msgid "Protected Branch" msgstr "" +msgid "Protected Branches" +msgstr "" + msgid "Protected Environment" msgstr "" @@ -18181,6 +18247,9 @@ msgstr "" msgid "Protected Tag" msgstr "" +msgid "Protected Tags" +msgstr "" + msgid "Protected branches" msgstr "" @@ -19084,6 +19153,9 @@ msgstr "" msgid "Request Headers" msgstr "" +msgid "Request details" +msgstr "" + msgid "Request parameter %{param} is missing." msgstr "" @@ -24101,6 +24173,9 @@ msgstr "" msgid "U2F only works with HTTPS-enabled websites. Contact your administrator for more details." msgstr "" +msgid "UI Development Kit" +msgstr "" + msgid "URL" msgstr "" @@ -24458,6 +24533,9 @@ msgstr "" msgid "Upload CSV file" msgstr "" +msgid "Upload License" +msgstr "" + msgid "Upload New File" msgstr "" diff --git a/package.json b/package.json index 02a67664a11..0cd75c89f42 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", "@gitlab/svgs": "1.140.0", - "@gitlab/ui": "17.1.0", + "@gitlab/ui": "17.2.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-1", "@sentry/browser": "^5.10.2", diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index bcd1a53bd47..72076fbecf9 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -237,7 +237,7 @@ RSpec.describe Projects::IssuesController do context 'external issue tracker' do let!(:service) do - create(:custom_issue_tracker_service, project: project, title: 'Custom Issue Tracker', new_issue_url: 'http://test.com') + create(:custom_issue_tracker_service, project: project, new_issue_url: 'http://test.com') end before do diff --git a/spec/controllers/projects/snippets/blobs_controller_spec.rb b/spec/controllers/projects/snippets/blobs_controller_spec.rb new file mode 100644 index 00000000000..ca656705e07 --- /dev/null +++ b/spec/controllers/projects/snippets/blobs_controller_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::Snippets::BlobsController do + using RSpec::Parameterized::TableSyntax + include SnippetHelpers + + let_it_be(:author) { create(:user) } + let_it_be(:developer) { create(:user) } + let_it_be(:other_user) { create(:user) } + + let(:visibility) { :public } + let(:project_visibility) { :public } + let(:project) { create(:project, project_visibility) } + let(:snippet) { create(:project_snippet, visibility, :repository, project: project, author: author) } + + before do + project.add_maintainer(author) + project.add_developer(developer) + end + + describe 'GET #raw' do + let(:filepath) { 'file1' } + let(:ref) { TestEnv::BRANCH_SHA['snippet/single-file'] } + let(:inline) { nil } + + subject do + get(:raw, + params: { + namespace_id: project.namespace, + project_id: project, + snippet_id: snippet, + path: filepath, + ref: ref, + inline: inline + }) + end + + context 'with a snippet without a repository' do + let(:snippet) { create(:project_snippet, visibility, project: project, author: author) } + + it_behaves_like 'raw snippet without repository', :not_found + end + + where(:project_visibility_level, :snippet_visibility_level, :user, :status) do + :public | :public | :author | :ok + :public | :public | :developer | :ok + :public | :public | :other_user | :ok + :public | :public | nil | :ok + + :public | :private | :author | :ok + :public | :private | :developer | :ok + :public | :private | :other_user | :not_found + :public | :private | nil | :not_found + + :private | :public | :author | :ok + :private | :public | :developer | :ok + :private | :public | :other_user | :not_found + :private | :public | nil | :redirect + + :private | :private | :author | :ok + :private | :private | :developer | :ok + :private | :private | :other_user | :not_found + :private | :private | nil | :redirect + end + + with_them do + let(:visibility) { snippet_visibility_level } + let(:project_visibility) { project_visibility_level } + + before do + sign_in_as(user) + + subject + end + + it 'responds with correct status' do + expect(response).to have_gitlab_http_status(status) + end + end + + it_behaves_like 'raw snippet blob' + end +end diff --git a/spec/controllers/snippets/blobs_controller_spec.rb b/spec/controllers/snippets/blobs_controller_spec.rb new file mode 100644 index 00000000000..b9f58587a58 --- /dev/null +++ b/spec/controllers/snippets/blobs_controller_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Snippets::BlobsController do + using RSpec::Parameterized::TableSyntax + include SnippetHelpers + + describe 'GET #raw' do + let_it_be(:author) { create(:user) } + let_it_be(:other_user) { create(:user) } + + let(:visibility) { :public } + let(:snippet) { create(:personal_snippet, visibility, :repository, author: author) } + let(:filepath) { 'file1' } + let(:ref) { TestEnv::BRANCH_SHA['snippet/single-file'] } + let(:inline) { nil } + + subject do + get(:raw, + params: { + snippet_id: snippet, + path: filepath, + ref: ref, + inline: inline + }) + end + + where(:snippet_visibility_level, :user, :status) do + :public | :author | :ok + :public | :other_user | :ok + :public | nil | :ok + + :private | :author | :ok + :private | :other_user | :not_found + :private | nil | :redirect + end + + with_them do + let(:visibility) { snippet_visibility_level } + + before do + sign_in_as(user) + + subject + end + + it 'responds with correct status' do + expect(response).to have_gitlab_http_status(status) + end + end + + it_behaves_like 'raw snippet blob' + + context 'with a snippet without a repository' do + let(:snippet) { create(:personal_snippet, visibility, author: author) } + + it_behaves_like 'raw snippet without repository', :redirect + end + end +end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index b78fc63b7dd..2322babcd53 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -174,7 +174,7 @@ RSpec.describe 'Database schema' do IGNORED_JSONB_COLUMNS = { "ApplicationSetting" => %w[repository_storages_weighted], "AlertManagement::Alert" => %w[payload], - "Ci::BuildMetadata" => %w[config_options config_variables secrets], # secrets has an EE-only validator + "Ci::BuildMetadata" => %w[config_options config_variables], "Geo::Event" => %w[payload], "GeoNodeStatus" => %w[status], "Operations::FeatureFlagScope" => %w[strategies], diff --git a/spec/fixtures/gitlab/import_export/corrupted_project_export.tar.gz b/spec/fixtures/gitlab/import_export/corrupted_project_export.tar.gz index cac16cf9cd8c51835ed960bccc68f8cddfdbdc66..e0830c290d11d7aa57cd207072b1c7e388c6242d 100644 GIT binary patch literal 3973 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 f^bWyYOKms1&T^4 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 diff --git a/spec/fixtures/gitlab/import_export/lightweight_project_export.tar.gz b/spec/fixtures/gitlab/import_export/lightweight_project_export.tar.gz index c01402954dd5af43b88906c9bedb19b970644a1e..0aa417347784044db90af46e5a5dd74377a5f943 100644 GIT binary patch literal 3778 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 literal 3837 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 { }); }); + describe('extensions', () => { + const foo1 = jest.fn(); + const foo2 = jest.fn(); + const bar = jest.fn(); + const MyExt1 = { + foo: foo1, + }; + const MyExt2 = { + bar, + }; + const MyExt3 = { + foo: foo2, + }; + beforeEach(() => { + editor.createInstance({ el: editorEl, blobPath, blobContent }); + }); + + afterEach(() => { + editor.model.dispose(); + }); + + it('is extensible with the extensions', () => { + expect(editor.foo).toBeUndefined(); + + editor.use(MyExt1); + expect(editor.foo).toEqual(foo1); + }); + + it('does not fail if no extensions supplied', () => { + const spy = jest.spyOn(global.console, 'error'); + editor.use(); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('is extensible with multiple extensions', () => { + expect(editor.foo).toBeUndefined(); + expect(editor.bar).toBeUndefined(); + + editor.use([MyExt1, MyExt2]); + + expect(editor.foo).toEqual(foo1); + expect(editor.bar).toEqual(bar); + }); + + it('uses the last definition of a method in case of an overlap', () => { + editor.use([MyExt1, MyExt2, MyExt3]); + expect(editor).toEqual( + expect.objectContaining({ + foo: foo2, + bar, + }), + ); + }); + + it('correctly resolves references withing extensions', () => { + const FunctionExt = { + inst() { + return this.instance; + }, + mod() { + return this.model; + }, + }; + editor.use(FunctionExt); + expect(editor.inst()).toEqual(editor.instance); + expect(editor.mod()).toEqual(editor.model); + }); + }); + describe('languages', () => { it('registers custom languages defined with Monaco', () => { expect(monacoLanguages.getLanguages()).toEqual( diff --git a/spec/frontend/fixtures/services.rb b/spec/frontend/fixtures/services.rb index 0877998cc9d..43230301296 100644 --- a/spec/frontend/fixtures/services.rb +++ b/spec/frontend/fixtures/services.rb @@ -8,7 +8,7 @@ RSpec.describe Projects::ServicesController, '(JavaScript fixtures)', type: :con let(:admin) { create(:admin) } let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} let(:project) { create(:project_empty_repo, namespace: namespace, path: 'services-project') } - let!(:service) { create(:custom_issue_tracker_service, project: project, title: 'Custom Issue Tracker') } + let!(:service) { create(:custom_issue_tracker_service, project: project) } render_views diff --git a/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb index 364e2aa6ca8..156e10e4270 100644 --- a/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb +++ b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb @@ -63,7 +63,7 @@ describe Resolvers::Projects::JiraProjectsResolver do context 'when Jira connection is not valid' do before do - WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/project/search?maxResults=50&query=&startAt=0') + WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/project') .to_raise(JIRA::HTTPError.new(double(message: 'Some failure.'))) end diff --git a/spec/lib/gitlab/suggestions/file_suggestion_spec.rb b/spec/lib/gitlab/suggestions/file_suggestion_spec.rb index 6fbbad017c5..15bb8ae5979 100644 --- a/spec/lib/gitlab/suggestions/file_suggestion_spec.rb +++ b/spec/lib/gitlab/suggestions/file_suggestion_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::Suggestions::FileSuggestion do - def create_suggestion(new_line, to_content) + def create_suggestion(new_line, to_content, lines_above = 0, lines_below = 0) position = Gitlab::Diff::Position.new(old_path: file_path, new_path: file_path, old_line: nil, @@ -18,6 +18,8 @@ describe Gitlab::Suggestions::FileSuggestion do create(:suggestion, :content_from_repo, note: diff_note, + lines_above: lines_above, + lines_below: lines_below, to_content: to_content) end @@ -39,27 +41,9 @@ describe Gitlab::Suggestions::FileSuggestion do create_suggestion(15, " *** SUGGESTION 2 ***\n") end - let(:file_suggestion) { described_class.new } + let(:suggestions) { [suggestion1, suggestion2] } - describe '#add_suggestion' do - it 'succeeds when adding a suggestion for the same file as the original' do - file_suggestion.add_suggestion(suggestion1) - - expect { file_suggestion.add_suggestion(suggestion2) }.not_to raise_error - end - - it 'raises an error when adding a suggestion for a different file' do - allow(suggestion2) - .to(receive_message_chain(:diff_file, :file_path) - .and_return('path/to/different/file')) - - file_suggestion.add_suggestion(suggestion1) - - expect { file_suggestion.add_suggestion(suggestion2) }.to( - raise_error(described_class::SuggestionForDifferentFileError) - ) - end - end + let(:file_suggestion) { described_class.new(file_path, suggestions) } describe '#line_conflict' do def stub_suggestions(line_index_spans) @@ -175,67 +159,296 @@ describe Gitlab::Suggestions::FileSuggestion do end describe '#new_content' do - it 'returns a blob with the suggestions applied to it' do - file_suggestion.add_suggestion(suggestion1) - file_suggestion.add_suggestion(suggestion2) + context 'with two suggestions' do + let(:suggestions) { [suggestion1, suggestion2] } - expected_content = <<-CONTENT.strip_heredoc - require 'fileutils' - require 'open3' + it 'returns a blob with the suggestions applied to it' do + expected_content = <<-CONTENT.strip_heredoc + require 'fileutils' + require 'open3' - module Popen - extend self + module Popen + extend self - def popen(cmd, path=nil) - unless cmd.is_a?(Array) - *** SUGGESTION 1 *** + def popen(cmd, path=nil) + unless cmd.is_a?(Array) + *** SUGGESTION 1 *** + end + + path ||= Dir.pwd + + vars = { + *** SUGGESTION 2 *** + } + + options = { + chdir: path + } + + unless File.directory?(path) + FileUtils.mkdir_p(path) + end + + @cmd_output = "" + @cmd_status = 0 + + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_output << stdout.read + @cmd_output << stderr.read + @cmd_status = wait_thr.value.exitstatus + end + + return @cmd_output, @cmd_status end - - path ||= Dir.pwd - - vars = { - *** SUGGESTION 2 *** - } - - options = { - chdir: path - } - - unless File.directory?(path) - FileUtils.mkdir_p(path) - end - - @cmd_output = "" - @cmd_status = 0 - - Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| - @cmd_output << stdout.read - @cmd_output << stderr.read - @cmd_status = wait_thr.value.exitstatus - end - - return @cmd_output, @cmd_status end + CONTENT + + expect(file_suggestion.new_content).to eq(expected_content) + end + end + + context 'when no suggestions have been added' do + let(:suggestions) { [] } + + it 'returns an empty string' do + expect(file_suggestion.new_content).to eq('') + end + end + + context 'with multiline suggestions' do + let(:suggestions) { [multi_suggestion1, multi_suggestion2, multi_suggestion3] } + + context 'when the previous suggestion increases the line count' do + let!(:multi_suggestion1) do + create_suggestion(9, " *** SUGGESTION 1 ***\n *** SECOND LINE ***\n *** THIRD LINE ***\n") end - CONTENT - expect(file_suggestion.new_content).to eq(expected_content) - end + let!(:multi_suggestion2) do + create_suggestion(15, " *** SUGGESTION 2 ***\n *** SECOND LINE ***\n") + end - it 'returns an empty string when no suggestions have been added' do - expect(file_suggestion.new_content).to eq('') - end - end + let!(:multi_suggestion3) do + create_suggestion(19, " chdir: *** SUGGESTION 3 ***\n") + end - describe '#file_path' do - it 'returns the path of the file associated with the suggestions' do - file_suggestion.add_suggestion(suggestion1) + it 'returns a blob with the suggestions applied to it' do + expected_content = <<-CONTENT.strip_heredoc + require 'fileutils' + require 'open3' - expect(file_suggestion.file_path).to eq(file_path) - end + module Popen + extend self - it 'returns nil if no suggestions have been added' do - expect(file_suggestion.file_path).to be(nil) + def popen(cmd, path=nil) + unless cmd.is_a?(Array) + *** SUGGESTION 1 *** + *** SECOND LINE *** + *** THIRD LINE *** + end + + path ||= Dir.pwd + + vars = { + *** SUGGESTION 2 *** + *** SECOND LINE *** + } + + options = { + chdir: *** SUGGESTION 3 *** + } + + unless File.directory?(path) + FileUtils.mkdir_p(path) + end + + @cmd_output = "" + @cmd_status = 0 + + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_output << stdout.read + @cmd_output << stderr.read + @cmd_status = wait_thr.value.exitstatus + end + + return @cmd_output, @cmd_status + end + end + CONTENT + + expect(file_suggestion.new_content).to eq(expected_content) + end + end + + context 'when the previous suggestion decreases and increases the line count' do + let!(:multi_suggestion1) do + create_suggestion(9, " *** SUGGESTION 1 ***\n", 1, 1) + end + + let!(:multi_suggestion2) do + create_suggestion(15, " *** SUGGESTION 2 ***\n *** SECOND LINE ***\n") + end + + let!(:multi_suggestion3) do + create_suggestion(19, " chdir: *** SUGGESTION 3 ***\n") + end + + it 'returns a blob with the suggestions applied to it' do + expected_content = <<-CONTENT.strip_heredoc + require 'fileutils' + require 'open3' + + module Popen + extend self + + def popen(cmd, path=nil) + *** SUGGESTION 1 *** + + path ||= Dir.pwd + + vars = { + *** SUGGESTION 2 *** + *** SECOND LINE *** + } + + options = { + chdir: *** SUGGESTION 3 *** + } + + unless File.directory?(path) + FileUtils.mkdir_p(path) + end + + @cmd_output = "" + @cmd_status = 0 + + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_output << stdout.read + @cmd_output << stderr.read + @cmd_status = wait_thr.value.exitstatus + end + + return @cmd_output, @cmd_status + end + end + CONTENT + + expect(file_suggestion.new_content).to eq(expected_content) + end + end + + context 'when the previous suggestion replaces with the same number of lines' do + let!(:multi_suggestion1) do + create_suggestion(9, " *** SUGGESTION 1 ***\n *** SECOND LINE ***\n *** THIRD LINE ***\n", 1, 1) + end + + let!(:multi_suggestion2) do + create_suggestion(15, " *** SUGGESTION 2 ***\n") + end + + let!(:multi_suggestion3) do + create_suggestion(19, " chdir: *** SUGGESTION 3 ***\n") + end + + it 'returns a blob with the suggestions applied to it' do + expected_content = <<-CONTENT.strip_heredoc + require 'fileutils' + require 'open3' + + module Popen + extend self + + def popen(cmd, path=nil) + *** SUGGESTION 1 *** + *** SECOND LINE *** + *** THIRD LINE *** + + path ||= Dir.pwd + + vars = { + *** SUGGESTION 2 *** + } + + options = { + chdir: *** SUGGESTION 3 *** + } + + unless File.directory?(path) + FileUtils.mkdir_p(path) + end + + @cmd_output = "" + @cmd_status = 0 + + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_output << stdout.read + @cmd_output << stderr.read + @cmd_status = wait_thr.value.exitstatus + end + + return @cmd_output, @cmd_status + end + end + CONTENT + + expect(file_suggestion.new_content).to eq(expected_content) + end + end + + context 'when the previous suggestion replaces multiple lines and the suggestions were applied out of order' do + let(:suggestions) { [multi_suggestion1, multi_suggestion3, multi_suggestion2] } + + let!(:multi_suggestion1) do + create_suggestion(9, " *** SUGGESTION 1 ***\n *** SECOND LINE ***\n *** THIRD LINE ***\n", 1, 1) + end + + let!(:multi_suggestion3) do + create_suggestion(19, " *** SUGGESTION 3 ***\n", 1, 1) + end + + let!(:multi_suggestion2) do + create_suggestion(15, " *** SUGGESTION 2 ***\n", 1, 1) + end + + it 'returns a blob with the suggestions applied to it' do + expected_content = <<-CONTENT.strip_heredoc + require 'fileutils' + require 'open3' + + module Popen + extend self + + def popen(cmd, path=nil) + *** SUGGESTION 1 *** + *** SECOND LINE *** + *** THIRD LINE *** + + path ||= Dir.pwd + + *** SUGGESTION 2 *** + + *** SUGGESTION 3 *** + + unless File.directory?(path) + FileUtils.mkdir_p(path) + end + + @cmd_output = "" + @cmd_status = 0 + + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_output << stdout.read + @cmd_output << stderr.read + @cmd_status = wait_thr.value.exitstatus + end + + return @cmd_output, @cmd_status + end + end + CONTENT + + expect(file_suggestion.new_content).to eq(expected_content) + end + end end end end diff --git a/spec/lib/gitlab/suggestions/suggestion_set_spec.rb b/spec/lib/gitlab/suggestions/suggestion_set_spec.rb index 8c61e6c42a6..b664bb73176 100644 --- a/spec/lib/gitlab/suggestions/suggestion_set_spec.rb +++ b/spec/lib/gitlab/suggestions/suggestion_set_spec.rb @@ -87,11 +87,10 @@ describe Gitlab::Suggestions::SuggestionSet do it 'returns an array of hashes with proper key/value pairs' do first_action = suggestion_set.actions.first - file_path, file_suggestion = suggestion_set - .send(:suggestions_per_file).first + file_suggestion = suggestion_set.send(:suggestions_per_file).first expect(first_action[:action]).to be('update') - expect(first_action[:file_path]).to eq(file_path) + expect(first_action[:file_path]).to eq(file_suggestion.file_path) expect(first_action[:content]).to eq(file_suggestion.new_content) end end diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index 588e5872cc8..0f7fa5f8e60 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -92,4 +92,33 @@ describe Ci::BuildMetadata do end end end + + describe 'validations' do + context 'when attributes are valid' do + it 'returns no errors' do + metadata.secrets = { + DATABASE_PASSWORD: { + vault: { + engine: { name: 'kv-v2', path: 'kv-v2' }, + path: 'production/db', + field: 'password' + } + } + } + + expect(metadata).to be_valid + end + end + + context 'when data is invalid' do + it 'returns errors' do + metadata.secrets = { DATABASE_PASSWORD: { vault: {} } } + + aggregate_failures do + expect(metadata).to be_invalid + expect(metadata.errors.full_messages).to eq(["Secrets must be a valid json schema"]) + end + end + end + end end diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb index ab939e0d2f8..6818db48fee 100644 --- a/spec/models/project_services/bugzilla_service_spec.rb +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -32,49 +32,4 @@ describe BugzillaService do it { is_expected.not_to validate_presence_of(:new_issue_url) } end end - - context 'overriding properties' do - let(:url) { 'http://bugzilla.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - context 'when data are stored in properties' do - let(:properties) { access_params.merge(title: title, description: description) } - let(:service) do - create(:bugzilla_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:bugzilla_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:bugzilla_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:bugzilla_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Bugzilla') - expect(service.description).to eq('Bugzilla issue tracker') - end - end - end end diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb index e749ea6eacc..f2232ae8e9a 100644 --- a/spec/models/project_services/custom_issue_tracker_service_spec.rb +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -31,66 +31,5 @@ describe CustomIssueTrackerService do it { is_expected.not_to validate_presence_of(:issues_url) } it { is_expected.not_to validate_presence_of(:new_issue_url) } end - - context 'title' do - let(:issue_tracker) { described_class.new(properties: {}) } - - it 'sets a default title' do - issue_tracker.title = nil - - expect(issue_tracker.title).to eq('Custom Issue Tracker') - end - - it 'sets the custom title' do - issue_tracker.title = 'test title' - - expect(issue_tracker.title).to eq('test title') - end - end - end - - context 'overriding properties' do - let(:url) { 'http://custom.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - context 'when data are stored in properties' do - let(:properties) { access_params.merge(title: title, description: description) } - let(:service) do - create(:custom_issue_tracker_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:custom_issue_tracker_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:custom_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:custom_issue_tracker_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Custom Issue Tracker') - expect(service.description).to eq('Custom issue tracker') - end - end end end diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index 7f1c6224b7d..ba861aefe3c 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -51,49 +51,4 @@ describe GitlabIssueTrackerService do end end end - - context 'overriding properties' do - let(:url) { 'http://gitlab.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - context 'when data are stored in properties' do - let(:properties) { access_params.merge(title: title, description: description) } - let(:service) do - create(:gitlab_issue_tracker_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:gitlab_issue_tracker_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:gitlab_issue_tracker_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:gitlab_issue_tracker_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('GitLab') - expect(service.description).to eq('GitLab issue tracker') - end - end - end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 20e85f0fd4b..5e2dd5f3fc9 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -5,8 +5,6 @@ require 'spec_helper' describe JiraService do include AssetsHelpers - let(:title) { 'custom title' } - let(:description) { 'custom description' } let(:url) { 'http://jira.example.com' } let(:api_url) { 'http://api-jira.example.com' } let(:username) { 'jira-username' } @@ -93,7 +91,6 @@ describe JiraService do let(:params) do { project: create(:project), - title: 'custom title', description: 'custom description', url: url, api_url: api_url, username: username, password: password, jira_issue_transition_id: transition_id @@ -106,19 +103,6 @@ describe JiraService do expect(subject.properties).to be_nil end - it 'sets title correctly' do - service = subject - - expect(service.title).to eq('custom title') - end - - it 'sets service data correctly' do - service = subject - - expect(service.title).to eq('custom title') - expect(service.description).to eq('custom description') - end - it 'stores data in data_fields correcty' do service = subject @@ -209,7 +193,6 @@ describe JiraService do end it 'does not reset password if url "changed" to the same url as before' do - service.title = 'aaaaaa' service.url = 'http://jira.example.com' service.save @@ -318,46 +301,32 @@ describe JiraService do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 context 'when data are stored in properties' do - let(:properties) { data_params.merge(title: title, description: description) } + let(:properties) { data_params } let!(:service) do create(:jira_service, :without_properties_callback, properties: properties.merge(additional: 'something')) end - it_behaves_like 'issue tracker fields' it_behaves_like 'handles jira fields' end context 'when data are stored in separated fields' do let(:service) do - create(:jira_service, data_params.merge(properties: {}, title: title, description: description)) + create(:jira_service, data_params.merge(properties: {})) end - it_behaves_like 'issue tracker fields' it_behaves_like 'handles jira fields' end context 'when data are stored in both properties and separated fields' do - let(:properties) { data_params.merge(title: title, description: description) } + let(:properties) { data_params } let(:service) do create(:jira_service, :without_properties_callback, active: false, properties: properties).tap do |service| create(:jira_tracker_data, data_params.merge(service: service)) end end - it_behaves_like 'issue tracker fields' it_behaves_like 'handles jira fields' end - - context 'when no title & description are set' do - let(:service) do - create(:jira_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Jira') - expect(service.description).to eq(s_('JiraService|Jira issue tracker')) - end - end end describe '#close_issue' do @@ -704,59 +673,6 @@ describe JiraService do end end - describe 'description and title' do - let(:title) { 'Jira One' } - let(:description) { 'Jira One issue tracker' } - let(:properties) do - { - url: 'http://jira.example.com/web', - username: 'mic', - password: 'password', - title: title, - description: description - } - end - - context 'when it is not set' do - it 'default values are returned' do - service = create(:jira_service) - - expect(service.title).to eq('Jira') - expect(service.description).to eq(s_('JiraService|Jira issue tracker')) - end - end - - context 'when it is set in properties' do - it 'values from properties are returned' do - service = create(:jira_service, :without_properties_callback, properties: properties) - - expect(service.title).to eq(title) - expect(service.description).to eq(description) - end - end - - context 'when it is in title & description fields' do - it 'values from title and description fields are returned' do - service = create(:jira_service, title: title, description: description) - - expect(service.title).to eq(title) - expect(service.description).to eq(description) - end - end - - context 'when it is in both properites & title & description fields' do - it 'values from title and description fields are returned' do - title2 = 'Jira 2' - description2 = 'Jira description 2' - - service = create(:jira_service, title: title2, description: description2, properties: properties) - - expect(service.title).to eq(title2) - expect(service.description).to eq(description2) - end - end - end - describe 'project and issue urls' do context 'when gitlab.yml was initialized' do it 'is prepopulated with the settings' do diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb index 6220d7b1fac..4024de38505 100644 --- a/spec/models/project_services/redmine_service_spec.rb +++ b/spec/models/project_services/redmine_service_spec.rb @@ -50,49 +50,4 @@ describe RedmineService do expect(described_class.reference_pattern.match('#123')[:issue]).to eq('123') end end - - context 'overriding properties' do - let(:url) { 'http://redmine.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - context 'when data are stored in properties' do - let(:properties) { access_params.merge(title: title, description: description) } - let(:service) do - create(:redmine_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:redmine_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:redmine_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:redmine_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('Redmine') - expect(service.description).to eq('Redmine issue tracker') - end - end - end end diff --git a/spec/models/project_services/youtrack_service_spec.rb b/spec/models/project_services/youtrack_service_spec.rb index b8fff635e99..c82a3cc1bcf 100644 --- a/spec/models/project_services/youtrack_service_spec.rb +++ b/spec/models/project_services/youtrack_service_spec.rb @@ -42,49 +42,4 @@ describe YoutrackService do expect(described_class.reference_pattern.match('yt-123')[:issue]).to eq('yt-123') end end - - context 'overriding properties' do - let(:url) { 'http://youtrack.example.com' } - let(:access_params) do - { project_url: url, issues_url: url, new_issue_url: url } - end - - # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - context 'when data are stored in properties' do - let(:properties) { access_params.merge(title: title, description: description) } - let(:service) do - create(:youtrack_service, :without_properties_callback, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in separated fields' do - let(:service) do - create(:youtrack_service, title: title, description: description, properties: access_params) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when data are stored in both properties and separated fields' do - let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') } - let(:service) do - create(:youtrack_service, :without_properties_callback, title: title, description: description, properties: properties) - end - - it_behaves_like 'issue tracker fields' - end - - context 'when no title & description are set' do - let(:service) do - create(:youtrack_service, properties: access_params) - end - - it 'returns default values' do - expect(service.title).to eq('YouTrack') - expect(service.description).to eq(s_('IssueTracker|YouTrack issue tracker')) - end - end - end end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index ca6101364aa..8c914c6b74c 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -304,8 +304,6 @@ RSpec.describe Service do end describe 'build issue tracker from an integration' do - let(:title) { 'custom title' } - let(:description) { 'custom description' } let(:url) { 'http://jira.example.com' } let(:api_url) { 'http://api-jira.example.com' } let(:username) { 'jira-username' } @@ -322,8 +320,6 @@ RSpec.describe Service do service = described_class.build_from_integration(project.id, integration) expect(service).to be_active - expect(service.title).to eq(title) - expect(service.description).to eq(description) expect(service.url).to eq(url) expect(service.api_url).to eq(api_url) expect(service.username).to eq(username) @@ -335,7 +331,7 @@ RSpec.describe Service do # this will be removed as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 context 'when data are stored in properties' do - let(:properties) { data_params.merge(title: title, description: description) } + let(:properties) { data_params } let!(:integration) do create(:jira_service, :without_properties_callback, template: true, properties: properties.merge(additional: 'something')) end @@ -345,14 +341,14 @@ RSpec.describe Service do context 'when data are stored in separated fields' do let(:integration) do - create(:jira_service, :template, data_params.merge(properties: {}, title: title, description: description)) + create(:jira_service, :template, data_params.merge(properties: {})) end it_behaves_like 'service creation from an integration' end context 'when data are stored in both properties and separated fields' do - let(:properties) { data_params.merge(title: title, description: description) } + let(:properties) { data_params } let(:integration) do create(:jira_service, :without_properties_callback, active: true, template: true, properties: properties).tap do |service| create(:jira_tracker_data, data_params.merge(service: service)) @@ -514,7 +510,6 @@ RSpec.describe Service do let(:service) do GitlabIssueTrackerService.create( project: create(:project), - title: 'random title', project_url: 'http://gitlab.example.com' ) end @@ -523,10 +518,6 @@ RSpec.describe Service do expect { service }.not_to raise_error end - it 'sets title correctly' do - expect(service.title).to eq('random title') - end - it 'sets data correctly' do expect(service.data_fields.project_url).to eq('http://gitlab.example.com') end diff --git a/spec/requests/api/graphql/project/jira_projects_spec.rb b/spec/requests/api/graphql/project/jira_projects_spec.rb index d67c89f18c9..4d44d55f2de 100644 --- a/spec/requests/api/graphql/project/jira_projects_spec.rb +++ b/spec/requests/api/graphql/project/jira_projects_spec.rb @@ -80,34 +80,6 @@ describe 'query Jira projects' do it_behaves_like 'fetches first project' end - - context 'with before cursor' do - let(:projects_query) { 'projects(before: "Mg==", first: 1)' } - - it_behaves_like 'fetches first project' - end - - context 'with after cursor' do - let(:projects_query) { 'projects(after: "MA==", first: 1)' } - - it_behaves_like 'fetches first project' - end - end - - context 'with valid but inexistent after cursor' do - let(:projects_query) { 'projects(after: "MTk==")' } - - it 'retuns empty list of jira projects' do - expect(jira_projects.size).to eq(0) - end - end - - context 'with invalid after cursor' do - let(:projects_query) { 'projects(after: "invalid==")' } - - it 'treats the invalid cursor as no cursor and returns list of jira projects' do - expect(jira_projects.size).to eq(2) - end end end end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 7ae382d8aae..966d6f7b106 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -878,4 +878,12 @@ describe 'project routing' do expect(get('/gitlab/gitlabhq/-/design_management/designs/1/c6f00aa50b80887ada30a6fe517670be9f8f9ece/resized_image/small')).to route_to('application#route_not_found', unmatched_route: 'gitlab/gitlabhq/-/design_management/designs/1/c6f00aa50b80887ada30a6fe517670be9f8f9ece/resized_image/small') end end + + describe Projects::Snippets::BlobsController, "routing" do + it "to #raw" do + expect(get('/gitlab/gitlabhq/-/snippets/1/raw/master/lib/version.rb')) + .to route_to('projects/snippets/blobs#raw', namespace_id: 'gitlab', + project_id: 'gitlabhq', snippet_id: '1', ref: 'master', path: 'lib/version.rb') + end + end end diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 9c3d17f7d8f..5424f67f445 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -368,3 +368,10 @@ describe AutocompleteController, 'routing' do expect(get("/autocomplete/award_emojis")).to route_to('autocomplete#award_emojis') end end + +describe Snippets::BlobsController, "routing" do + it "to #raw" do + expect(get('/-/snippets/1/raw/master/lib/version.rb')) + .to route_to('snippets/blobs#raw', snippet_id: '1', ref: 'master', path: 'lib/version.rb') + end +end diff --git a/spec/services/gpg_keys/destroy_service_spec.rb b/spec/services/gpg_keys/destroy_service_spec.rb new file mode 100644 index 00000000000..82c7ab7adaa --- /dev/null +++ b/spec/services/gpg_keys/destroy_service_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe GpgKeys::DestroyService do + let(:user) { create(:user) } + + subject { described_class.new(user) } + + it 'destroys the GPG key' do + gpg_key = create(:gpg_key) + + expect { subject.execute(gpg_key) }.to change(GpgKey, :count).by(-1) + end +end diff --git a/spec/services/jira/requests/projects_spec.rb b/spec/services/jira/requests/projects_spec.rb index f7b9aa7c00c..42d09da138b 100644 --- a/spec/services/jira/requests/projects_spec.rb +++ b/spec/services/jira/requests/projects_spec.rb @@ -32,14 +32,6 @@ describe Jira::Requests::Projects do end context 'with jira_service' do - context 'when limit is invalid' do - let(:params) { { limit: 0 } } - - it 'returns a paylod with no projects returned' do - expect(subject.payload[:projects]).to be_empty - end - end - context 'when validations and params are ok' do let(:client) { double(options: { site: 'https://jira.example.com' }) } @@ -60,7 +52,7 @@ describe Jira::Requests::Projects do context 'when the request does not return any values' do before do - expect(client).to receive(:get).and_return({ 'someKey' => 'value' }) + expect(client).to receive(:get).and_return([]) end it 'returns a paylod with no projects returned' do @@ -74,19 +66,15 @@ describe Jira::Requests::Projects do context 'when the request returns values' do before do - expect(client).to receive(:get).and_return( - { 'values' => %w(project1 project2), 'isLast' => false } - ) - expect(JIRA::Resource::Project).to receive(:build).with(client, 'project1').and_return('jira_project1') - expect(JIRA::Resource::Project).to receive(:build).with(client, 'project2').and_return('jira_project2') + expect(client).to receive(:get).and_return([{ "key" => 'project1' }, { "key" => 'project2' }]) end it 'returns a paylod with jira projets' do payload = subject.payload expect(subject.success?).to be_truthy - expect(payload[:projects]).to eq(%w(jira_project1 jira_project2)) - expect(payload[:is_last]).to be_falsey + expect(payload[:projects].map(&:key)).to eq(%w(project1 project2)) + expect(payload[:is_last]).to be_truthy end end end diff --git a/spec/support/helpers/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb index 198bedfe3bc..9072c41fe66 100644 --- a/spec/support/helpers/jira_service_helper.rb +++ b/spec/support/helpers/jira_service_helper.rb @@ -5,14 +5,13 @@ module JiraServiceHelper JIRA_API = JIRA_URL + "/rest/api/2" def jira_service_settings - title = "Jira tracker" url = JIRA_URL username = 'jira-user' password = 'my-secret-password' jira_issue_transition_id = '1' jira_tracker.update( - title: title, url: url, username: username, password: password, + url: url, username: username, password: password, jira_issue_transition_id: jira_issue_transition_id, active: true ) end diff --git a/spec/support/helpers/snippet_helpers.rb b/spec/support/helpers/snippet_helpers.rb new file mode 100644 index 00000000000..ead18792efb --- /dev/null +++ b/spec/support/helpers/snippet_helpers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module SnippetHelpers + def sign_in_as(user) + sign_in(public_send(user)) if user + end +end diff --git a/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb b/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb index f0722beb3ed..3125f8ba315 100644 --- a/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb +++ b/spec/support/shared_contexts/requests/api/graphql/jira_import/jira_projects_context.rb @@ -74,6 +74,48 @@ shared_context 'jira projects request context' do }' end + let_it_be(:all_jira_projects_json) do + '[{ + "expand": "description,lead,issueTypes,url,projectKeys,permissions,insight", + "self": "https://gitlab-jira.atlassian.net/rest/api/2/project/10000", + "id": "10000", + "key": "EX", + "name": "Example", + "avatarUrls": { + "48x48": "https://gitlab-jira.atlassian.net/secure/projectavatar?pid=10000&avatarId=10425", + "24x24": "https://gitlab-jira.atlassian.net/secure/projectavatar?size=small&s=small&pid=10000&avatarId=10425", + "16x16": "https://gitlab-jira.atlassian.net/secure/projectavatar?size=xsmall&s=xsmall&pid=10000&avatarId=10425", + "32x32": "https://gitlab-jira.atlassian.net/secure/projectavatar?size=medium&s=medium&pid=10000&avatarId=10425" + }, + "projectTypeKey": "software", + "simplified": false, + "style": "classic", + "isPrivate": false, + "properties": { + } + }, + { + "expand": "description,lead,issueTypes,url,projectKeys,permissions,insight", + "self": "https://gitlab-jira.atlassian.net/rest/api/2/project/10001", + "id": "10001", + "key": "ABC", + "name": "Alphabetical", + "avatarUrls": { + "48x48": "https://gitlab-jira.atlassian.net/secure/projectavatar?pid=10001&avatarId=10405", + "24x24": "https://gitlab-jira.atlassian.net/secure/projectavatar?size=small&s=small&pid=10001&avatarId=10405", + "16x16": "https://gitlab-jira.atlassian.net/secure/projectavatar?size=xsmall&s=xsmall&pid=10001&avatarId=10405", + "32x32": "https://gitlab-jira.atlassian.net/secure/projectavatar?size=medium&s=medium&pid=10001&avatarId=10405" + }, + "projectTypeKey": "software", + "simplified": true, + "style": "next-gen", + "isPrivate": false, + "properties": { + }, + "entityId": "14935009-f8aa-481e-94bc-f7251f320b0e", + "uuid": "14935009-f8aa-481e-94bc-f7251f320b0e" + }]' + end let_it_be(:empty_jira_projects_json) do '{ "self": "https://your-domain.atlassian.net/rest/api/2/project/search?startAt=0&maxResults=2", @@ -86,10 +128,32 @@ shared_context 'jira projects request context' do }' end + let(:server_info_json) do + '{ + "baseUrl": "https://gitlab-jira.atlassian.net", + "version": "1001.0.0-SNAPSHOT", + "versionNumbers": [ + 1001, + 0, + 0 + ], + "deploymentType": "Cloud", + "buildNumber": 100128, + "buildDate": "2020-06-03T01:58:44.000-0700", + "serverTime": "2020-06-04T06:15:13.686-0700", + "scmInfo": "e736ab140ddb281c7cf5dcf9062c9ce2c08b3c1c", + "serverTitle": "Jira", + "defaultLocale": { + "locale": "en_US" + } + }' + end + let(:test_url) { "#{url}/rest/api/2/project/search?maxResults=50&query=&startAt=0" } let(:start_at_20_url) { "#{url}/rest/api/2/project/search?maxResults=50&query=&startAt=20" } let(:start_at_1_url) { "#{url}/rest/api/2/project/search?maxResults=50&query=&startAt=1" } let(:max_results_1_url) { "#{url}/rest/api/2/project/search?maxResults=1&query=&startAt=0" } + let(:all_projects_url) { "#{url}/rest/api/2/project" } before do WebMock.stub_request(:get, test_url).with(basic_auth: [username, password]) @@ -100,5 +164,9 @@ shared_context 'jira projects request context' do .to_return(body: jira_projects_json, headers: { "Content-Type": "application/json" }) WebMock.stub_request(:get, max_results_1_url).with(basic_auth: [username, password]) .to_return(body: jira_projects_json, headers: { "Content-Type": "application/json" }) + WebMock.stub_request(:get, all_projects_url).with(basic_auth: [username, password]) + .to_return(body: all_jira_projects_json, headers: { "Content-Type": "application/json" }) + WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/serverInfo') + .to_return(status: 200, body: server_info_json, headers: {}) end end diff --git a/spec/support/shared_examples/controllers/snippet_blob_shared_examples.rb b/spec/support/shared_examples/controllers/snippet_blob_shared_examples.rb new file mode 100644 index 00000000000..c3e8f807afb --- /dev/null +++ b/spec/support/shared_examples/controllers/snippet_blob_shared_examples.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'raw snippet blob' do + context 'with valid params' do + before do + subject + end + + it 'delivers file with correct Workhorse headers' do + expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') + expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true' + expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:') + end + + it 'responds with status 200' do + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'with invalid file path' do + let(:filepath) { 'doesnotexist' } + + it_behaves_like 'returning response status', :not_found + end + + context 'with invalid ref' do + let(:ref) { 'doesnotexist' } + + it_behaves_like 'returning response status', :not_found + end + + it_behaves_like 'content disposition headers' +end + +RSpec.shared_examples 'raw snippet without repository' do |unauthorized_status| + context 'when authorized' do + it 'returns a 422' do + subject + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + end + + context 'when unauthorized' do + let(:visibility) { :private } + + it_behaves_like 'returning response status', unauthorized_status + end +end diff --git a/spec/support/shared_examples/models/services_fields_shared_examples.rb b/spec/support/shared_examples/models/services_fields_shared_examples.rb deleted file mode 100644 index cb36f74460d..00000000000 --- a/spec/support/shared_examples/models/services_fields_shared_examples.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples 'issue tracker fields' do - let(:title) { 'custom title' } - let(:description) { 'custom description' } - let(:url) { 'http://issue_tracker.example.com' } - - context 'when data are stored in the properties' do - describe '#update' do - before do - service.update(title: 'new_title', description: 'new description') - end - - it 'removes title and description from properties' do - expect(service.reload.properties).not_to include('title', 'description') - end - - it 'stores title & description in services table' do - expect(service.read_attribute(:title)).to eq('new_title') - expect(service.read_attribute(:description)).to eq('new description') - end - end - - describe 'reading fields' do - it 'returns correct values' do - expect(service.title).to eq(title) - expect(service.description).to eq(description) - end - end - end -end diff --git a/yarn.lock b/yarn.lock index 6b482aa33a2..c5a68e51a1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -840,10 +840,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.140.0.tgz#593f1f65b0df57c3399fcfb9f472f59aa64da074" integrity sha512-6gANJGi2QkpvOgFTMcY3SIwEqhO69i6R3jU4BSskkVziwDdAWxGonln22a4Iu//Iv0NrsFDpAA0jIVfnJzw0iA== -"@gitlab/ui@17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.1.0.tgz#522912caee7689a0fde1e58cd6d4e4163e39ca7f" - integrity sha512-KruPE0I4qU4LP+pPIzhCY0xbNDcB7gUHaXMO1we2ssK9lc+NJxeLt9gr/ctOX1/Rzgau93+i9QvekeNg0wvqGA== +"@gitlab/ui@17.2.0": + version "17.2.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.2.0.tgz#c4bca963c987f21131be0a650bca47b3708714f2" + integrity sha512-pCzHoA41ggaPjN7612I5MxXq370Utlml9joUuo92BAXQk5XslAJQOFkdmyF/E89qaT7vgWurVAfPylqnSc5LrA== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0"