diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-01 18:08:45 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-01 18:08:45 +0300 |
commit | ae1efa2e1d32dee59d8f509ba17b623b5ffe4ba6 (patch) | |
tree | a4cba8561c1671934751508ead7b2b1053e783ec /app | |
parent | a4c655515155710b3695699ea88d824f65af6446 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
32 files changed, 310 insertions, 271 deletions
diff --git a/app/assets/javascripts/alert_management/components/alert_management_list.vue b/app/assets/javascripts/alert_management/components/alert_management_list.vue index b7cd28de807..59c74d8cd27 100644 --- a/app/assets/javascripts/alert_management/components/alert_management_list.vue +++ b/app/assets/javascripts/alert_management/components/alert_management_list.vue @@ -74,9 +74,8 @@ export default { { key: 'title', label: s__('AlertManagement|Alert'), - thClass: `${thClass} gl-pointer-events-none`, + thClass: `gl-pointer-events-none`, tdClass, - sortable: false, }, { key: 'eventCount', @@ -88,7 +87,7 @@ export default { { key: 'assignees', label: s__('AlertManagement|Assignees'), - thClass: 'gl-w-eighth', + thClass: 'gl-w-eighth gl-pointer-events-none', tdClass, }, { diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue index acc31dfabaf..f43a058b5f8 100644 --- a/app/assets/javascripts/jobs/components/job_app.vue +++ b/app/assets/javascripts/jobs/components/job_app.vue @@ -17,7 +17,7 @@ import UnmetPrerequisitesBlock from './unmet_prerequisites_block.vue'; import Sidebar from './sidebar.vue'; import { sprintf } from '~/locale'; import delayedJobMixin from '../mixins/delayed_job_mixin'; -import { isNewJobLogActive } from '../store/utils'; +import Log from './log/log.vue'; export default { name: 'JobPageApp', @@ -28,7 +28,7 @@ export default { EnvironmentsBlock, ErasedBlock, Icon, - Log: () => (isNewJobLogActive() ? import('./log/log.vue') : import('./job_log.vue')), + Log, LogTopBar, StuckBlock, UnmetPrerequisitesBlock, diff --git a/app/assets/javascripts/jobs/components/job_log.vue b/app/assets/javascripts/jobs/components/job_log.vue deleted file mode 100644 index 20888c0af42..00000000000 --- a/app/assets/javascripts/jobs/components/job_log.vue +++ /dev/null @@ -1,59 +0,0 @@ -<script> -import { mapState, mapActions } from 'vuex'; - -export default { - name: 'JobLog', - props: { - trace: { - type: String, - required: true, - }, - isComplete: { - type: Boolean, - required: true, - }, - }, - computed: { - ...mapState(['isScrolledToBottomBeforeReceivingTrace']), - }, - updated() { - this.$nextTick(() => { - this.handleScrollDown(); - }); - }, - mounted() { - this.$nextTick(() => { - this.handleScrollDown(); - }); - }, - methods: { - ...mapActions(['scrollBottom']), - /** - * The job log is sent in HTML, which means we need to use `v-html` to render it - * Using the updated hook with $nextTick is not enough to wait for the DOM to be updated - * in this case because it runs before `v-html` has finished running, since there's no - * Vue binding. - * In order to scroll the page down after `v-html` has finished, we need to use setTimeout - */ - handleScrollDown() { - if (this.isScrolledToBottomBeforeReceivingTrace) { - setTimeout(() => { - this.scrollBottom(); - }, 0); - } - }, - }, -}; -</script> -<template> - <pre class="js-build-trace build-trace qa-build-trace"> - <code class="bash" v-html="trace"> - </code> - - <div v-if="!isComplete" class="js-log-animation build-loader-animation"> - <div class="dot"></div> - <div class="dot"></div> - <div class="dot"></div> - </div> - </pre> -</template> diff --git a/app/assets/javascripts/jobs/components/log/line.vue b/app/assets/javascripts/jobs/components/log/line.vue index 33ee84bd4ee..48f669ae8ed 100644 --- a/app/assets/javascripts/jobs/components/log/line.vue +++ b/app/assets/javascripts/jobs/components/log/line.vue @@ -2,9 +2,7 @@ import LineNumber from './line_number.vue'; export default { - components: { - LineNumber, - }, + functional: true, props: { line: { type: Object, @@ -15,18 +13,28 @@ export default { required: true, }, }, + render(h, { props }) { + const { line, path } = props; + + const chars = line.content.map(content => { + return h( + 'span', + { + class: ['ws-pre-wrap', content.style], + }, + content.text, + ); + }); + + return h('div', { class: 'js-line log-line' }, [ + h(LineNumber, { + props: { + lineNumber: line.lineNumber, + path, + }, + }), + ...chars, + ]); + }, }; </script> - -<template> - <div class="js-line log-line"> - <line-number :line-number="line.lineNumber" :path="path" /> - <span - v-for="(content, i) in line.content" - :key="i" - :class="content.style" - class="ws-pre-wrap" - >{{ content.text }}</span - > - </div> -</template> diff --git a/app/assets/javascripts/jobs/components/log/line_number.vue b/app/assets/javascripts/jobs/components/log/line_number.vue index ae96c32874b..7ca9154d2fe 100644 --- a/app/assets/javascripts/jobs/components/log/line_number.vue +++ b/app/assets/javascripts/jobs/components/log/line_number.vue @@ -1,10 +1,6 @@ <script> -import { GlLink } from '@gitlab/ui'; - export default { - components: { - GlLink, - }, + functional: true, props: { lineNumber: { type: Number, @@ -15,41 +11,24 @@ export default { required: true, }, }, - computed: { - /** - * Builds the url for each line number - * - * @returns {String} - */ - buildLineNumber() { - return `${this.path}#${this.lineNumberId}`; - }, - /** - * Array indexes start with 0, so we add 1 - * to create the line number - * - * @returns {Number} the line number - */ - parsedLineNumber() { - return this.lineNumber + 1; - }, + render(h, { props }) { + const { lineNumber, path } = props; - /** - * Creates the anchor for each link - * - * @returns {String} - */ - lineNumberId() { - return `L${this.parsedLineNumber}`; - }, + const parsedLineNumber = lineNumber + 1; + const lineId = `L${parsedLineNumber}`; + const lineHref = `${path}#${lineId}`; + + return h( + 'a', + { + class: 'gl-link d-inline-block text-right line-number flex-shrink-0', + attrs: { + id: lineId, + href: lineHref, + }, + }, + parsedLineNumber, + ); }, }; </script> -<template> - <gl-link - :id="lineNumberId" - class="d-inline-block text-right line-number flex-shrink-0" - :href="buildLineNumber" - >{{ parsedLineNumber }}</gl-link - > -</template> diff --git a/app/assets/javascripts/jobs/store/mutations.js b/app/assets/javascripts/jobs/store/mutations.js index 6193d8d34ab..924b811d0d6 100644 --- a/app/assets/javascripts/jobs/store/mutations.js +++ b/app/assets/javascripts/jobs/store/mutations.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import * as types from './mutation_types'; -import { logLinesParser, updateIncrementalTrace, isNewJobLogActive } from './utils'; +import { logLinesParser, updateIncrementalTrace } from './utils'; export default { [types.SET_JOB_ENDPOINT](state, endpoint) { @@ -25,22 +25,16 @@ export default { } if (log.append) { - if (isNewJobLogActive()) { - state.trace = log.lines ? updateIncrementalTrace(log.lines, state.trace) : state.trace; - } else { - state.trace += log.html; - } + state.trace = log.lines ? updateIncrementalTrace(log.lines, state.trace) : state.trace; + state.traceSize += log.size; } else { // When the job still does not have a trace // the trace response will not have a defined // html or size. We keep the old value otherwise these // will be set to `null` - if (isNewJobLogActive()) { - state.trace = log.lines ? logLinesParser(log.lines) : state.trace; - } else { - state.trace = log.html || state.trace; - } + state.trace = log.lines ? logLinesParser(log.lines) : state.trace; + state.traceSize = log.size || state.traceSize; } diff --git a/app/assets/javascripts/jobs/store/state.js b/app/assets/javascripts/jobs/store/state.js index d76828ad19b..2fe945b2985 100644 --- a/app/assets/javascripts/jobs/store/state.js +++ b/app/assets/javascripts/jobs/store/state.js @@ -1,5 +1,3 @@ -import { isNewJobLogActive } from './utils'; - export default () => ({ jobEndpoint: null, traceEndpoint: null, @@ -18,7 +16,7 @@ export default () => ({ // Used to check if we should keep the automatic scroll isScrolledToBottomBeforeReceivingTrace: true, - trace: isNewJobLogActive() ? [] : '', + trace: [], isTraceComplete: false, traceSize: 0, isTraceSizeVisible: false, diff --git a/app/assets/javascripts/jobs/store/utils.js b/app/assets/javascripts/jobs/store/utils.js index 0b28c52a78f..3b6b8a2c851 100644 --- a/app/assets/javascripts/jobs/store/utils.js +++ b/app/assets/javascripts/jobs/store/utils.js @@ -177,5 +177,3 @@ export const updateIncrementalTrace = (newLog = [], oldParsed = []) => { return logLinesParser(newLog, parsedLog); }; - -export const isNewJobLogActive = () => gon && gon.features && gon.features.jobLogJson; diff --git a/app/assets/stylesheets/pages/alert_management/list.scss b/app/assets/stylesheets/pages/alert_management/list.scss index ea5e7a1bdea..5d3fd0d7dcf 100644 --- a/app/assets/stylesheets/pages/alert_management/list.scss +++ b/app/assets/stylesheets/pages/alert_management/list.scss @@ -8,14 +8,9 @@ outline: none; } - > :not([aria-sort='none']).b-table-sort-icon-left:hover::before { - content: '' !important; - } - td, th { - // TODO: There is no gl-pl-9 utlity for this padding, to be done and then removed. - padding-left: 1.25rem; + @include gl-pl-9; @include gl-py-5; @include gl-outline-none; @include gl-relative; @@ -26,24 +21,8 @@ font-weight: $gl-font-weight-bold; color: $gl-gray-600; - &:hover::before { - left: 3%; - top: 34%; - @include gl-absolute; - content: url("data:image/svg+xml,%3Csvg \ - xmlns='http://www.w3.org/2000/svg' \ - width='14' height='14' viewBox='0 0 16 \ - 16'%3E%3Cpath fill='%23BABABA' fill-rule='evenodd' \ - d='M11.707085,11.7071 L7.999975,15.4142 L4.292875,11.7071 \ - C3.902375,11.3166 3.902375,10.6834 \ - 4.292875,10.2929 C4.683375,9.90237 \ - 5.316575,9.90237 5.707075,10.2929 \ - L6.999975,11.5858 L6.999975,2 C6.999975,1.44771 \ - 7.447695,1 7.999975,1 C8.552255,1 8.999975,1.44771 \ - 8.999975,2 L8.999975,11.5858 L10.292865,10.2929 \ - C10.683395,9.90237 11.316555,9.90237 11.707085,10.2929 \ - C12.097605,10.6834 12.097605,11.3166 11.707085,11.7071 \ - Z'/%3E%3C/svg%3E%0A"); + &[aria-sort='none']:hover { + background-image: url('data:image/svg+xml, %3csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="4 0 8 16"%3e %3cpath style="fill: %23BABABA;" fill-rule="evenodd" d="M11.707085,11.7071 L7.999975,15.4142 L4.292875,11.7071 C3.902375,11.3166 3.902375, 10.6834 4.292875,10.2929 C4.683375,9.90237 5.316575,9.90237 5.707075,10.2929 L6.999975, 11.5858 L6.999975,2 C6.999975,1.44771 7.447695,1 7.999975,1 C8.552255,1 8.999975,1.44771 8.999975,2 L8.999975,11.5858 L10.292865,10.2929 C10.683395 ,9.90237 11.316555,9.90237 11.707085,10.2929 C12.097605,10.6834 12.097605,11.3166 11.707085,11.7071 Z"/%3e %3c/svg%3e'); } } } diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index 67a7daf8445..deba71c9dd3 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -5,7 +5,8 @@ class Projects::ImportsController < Projects::ApplicationController include ImportUrlParams # Authorize - before_action :authorize_admin_project! + before_action :authorize_admin_project!, only: [:new, :create] + before_action :require_namespace_project_creation_permission, only: :show before_action :require_no_repo, only: [:new, :create] before_action :redirect_if_progress, only: [:new, :create] before_action :redirect_if_no_import, only: :show @@ -51,6 +52,10 @@ class Projects::ImportsController < Projects::ApplicationController end end + def require_namespace_project_creation_permission + render_404 unless current_user.can?(:admin_project, @project) || current_user.can?(:create_projects, @project.namespace) + end + def redirect_if_progress if @project.import_in_progress? redirect_to project_import_path(@project) diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index e1f6cbe3dca..3f7f8da3478 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -11,9 +11,6 @@ class Projects::JobsController < Projects::ApplicationController before_action :authorize_erase_build!, only: [:erase] before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize] before_action :verify_api_request!, only: :terminal_websocket_authorize - before_action only: [:show] do - push_frontend_feature_flag(:job_log_json, project, default_enabled: true) - end before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize before_action :verify_proxy_request!, only: :proxy_websocket_authorize @@ -55,15 +52,10 @@ class Projects::JobsController < Projects::ApplicationController format.json do build.trace.being_watched! - # TODO: when the feature flag is removed we should not pass - # content_format to serialize method. - content_format = Feature.enabled?(:job_log_json, @project, default_enabled: true) ? :json : :html - build_trace = Ci::BuildTrace.new( build: @build, stream: stream, - state: params[:state], - content_format: content_format) + state: params[:state]) render json: BuildTraceSerializer .new(project: @project, current_user: @current_user) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 2937c83cd27..20ef14e8546 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -109,8 +109,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # or from cache if already merged @commits = set_commits_for_rendering( - @merge_request.recent_commits.with_latest_pipeline(@merge_request.source_branch), - commits_count: @merge_request.commits_count + @merge_request.recent_commits.with_latest_pipeline(@merge_request.source_branch).with_markdown_cache, + commits_count: @merge_request.commits_count ) render json: { html: view_to_html_string('projects/merge_requests/_commits') } diff --git a/app/finders/projects/integrations/jira/issues_finder.rb b/app/finders/projects/integrations/jira/issues_finder.rb new file mode 100644 index 00000000000..280ed7954de --- /dev/null +++ b/app/finders/projects/integrations/jira/issues_finder.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Projects + module Integrations + module Jira + IntegrationError = Class.new(StandardError) + RequestError = Class.new(StandardError) + + class IssuesFinder + attr_reader :issues, :total_count + + def initialize(project, params = {}) + @project = project + @jira_service = project.jira_service + @page = params[:page].presence || 1 + @params = params + end + + def execute + return [] unless Feature.enabled?(:jira_integration, project) + + raise IntegrationError, _('Jira service not configured.') unless jira_service&.active? + + project_key = jira_service.project_key + raise IntegrationError, _('Jira project key is not configured') if project_key.blank? + + fetch_issues(project_key) + end + + private + + attr_reader :project, :jira_service, :page, :params + + # rubocop: disable CodeReuse/ServiceClass + def fetch_issues(project_key) + jql = ::Jira::JqlBuilderService.new(project_key, params).execute + response = ::Jira::Requests::Issues::ListService.new(jira_service, { jql: jql, page: page }).execute + + if response.success? + @total_count = response.payload[:total_count] + @issues = response.payload[:issues] + else + raise RequestError, response.message + end + end + # rubocop: enable CodeReuse/ServiceClass + end + end + end +end diff --git a/app/graphql/resolvers/projects/jira_projects_resolver.rb b/app/graphql/resolvers/projects/jira_projects_resolver.rb index 2b5d04ef39e..3b6e5c4fd42 100644 --- a/app/graphql/resolvers/projects/jira_projects_resolver.rb +++ b/app/graphql/resolvers/projects/jira_projects_resolver.rb @@ -37,7 +37,7 @@ module Resolvers def jira_projects(name:) args = { query: name }.compact - return Jira::Requests::Projects.new(project.jira_service, args).execute + return Jira::Requests::Projects::ListService.new(project.jira_service, args).execute end end end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 8a9380f4771..e9ef7278d44 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -271,6 +271,20 @@ module GitlabRoutingHelper end end + def gitlab_raw_snippet_blob_url(snippet, path, ref = nil) + params = { + snippet_id: snippet, + ref: ref || snippet.repository.root_ref, + path: path + } + + if snippet.is_a?(ProjectSnippet) + project_snippet_blob_raw_url(snippet.project, params) + else + snippet_blob_raw_url(params) + end + end + def gitlab_snippet_notes_path(snippet, *args) new_args = snippet_query_params(snippet, *args) snippet_notes_path(snippet, *new_args) diff --git a/app/models/ci/build_trace.rb b/app/models/ci/build_trace.rb index b9db1559836..f70e1ed69ea 100644 --- a/app/models/ci/build_trace.rb +++ b/app/models/ci/build_trace.rb @@ -2,40 +2,22 @@ module Ci class BuildTrace - CONVERTERS = { - html: Gitlab::Ci::Ansi2html, - json: Gitlab::Ci::Ansi2json - }.freeze - attr_reader :trace, :build delegate :state, :append, :truncated, :offset, :size, :total, to: :trace, allow_nil: true delegate :id, :status, :complete?, to: :build, prefix: true - def initialize(build:, stream:, state:, content_format:) + def initialize(build:, stream:, state:) @build = build - @content_format = content_format if stream.valid? stream.limit - @trace = CONVERTERS.fetch(content_format).convert(stream.stream, state) + @trace = Gitlab::Ci::Ansi2json.convert(stream.stream, state) end end - def json? - @content_format == :json - end - - def html? - @content_format == :html - end - - def json_lines - @trace&.lines if json? - end - - def html_lines - @trace&.html if html? + def lines + @trace&.lines end end end diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb index 456d32bf403..b8653f47392 100644 --- a/app/models/commit_collection.rb +++ b/app/models/commit_collection.rb @@ -53,6 +53,17 @@ class CommitCollection self end + # Returns the collection with markdown fields preloaded. + # + # Get the markdown cache from redis using pipeline to prevent n+1 requests + # when rendering the markdown of an attribute (e.g. title, full_title, + # description). + def with_markdown_cache + Commit.preload_markdown_cache!(commits) + + self + end + def unenriched commits.reject(&:gitaly_commit?) end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 8c97547f416..7ba5f1d01f9 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -23,7 +23,7 @@ class JiraService < IssueTrackerService # TODO: we can probably just delegate as part of # https://gitlab.com/gitlab-org/gitlab/issues/29404 - data_field :username, :password, :url, :api_url, :jira_issue_transition_id + data_field :username, :password, :url, :api_url, :jira_issue_transition_id, :project_key before_update :reset_password diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 21a9325104d..5f45407c05e 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -334,7 +334,13 @@ class Snippet < ApplicationRecord def file_name_on_repo return if repository.empty? - repository.ls_files(repository.root_ref).first + list_files(repository.root_ref).first + end + + def list_files(ref = nil) + return [] if repository.empty? + + repository.ls_files(ref) end class << self diff --git a/app/serializers/build_trace_entity.rb b/app/serializers/build_trace_entity.rb index b5bac8a5d64..f4c3c7770b2 100644 --- a/app/serializers/build_trace_entity.rb +++ b/app/serializers/build_trace_entity.rb @@ -12,6 +12,5 @@ class BuildTraceEntity < Grape::Entity expose :size expose :total - expose :json_lines, as: :lines, if: ->(*) { object.json? } - expose :html_lines, as: :html, if: ->(*) { object.html? } + expose :lines end diff --git a/app/services/jira/jql_builder_service.rb b/app/services/jira/jql_builder_service.rb new file mode 100644 index 00000000000..cb8cee8e38a --- /dev/null +++ b/app/services/jira/jql_builder_service.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Jira + class JqlBuilderService + DEFAULT_SORT = "created" + DEFAULT_SORT_DIRECTION = "DESC" + + def initialize(jira_project_key, params = {}) + @jira_project_key = jira_project_key + @sort = params[:sort] || DEFAULT_SORT + @sort_direction = params[:sort_direction] || DEFAULT_SORT_DIRECTION + end + + def execute + [by_project, order_by].join(' ') + end + + private + + attr_reader :jira_project_key, :sort, :sort_direction + + def by_project + "project = #{jira_project_key}" + end + + def order_by + "order by #{sort} #{sort_direction}" + end + end +end diff --git a/app/services/jira/requests/base.rb b/app/services/jira/requests/base.rb index 0934730d10c..7c6db372257 100644 --- a/app/services/jira/requests/base.rb +++ b/app/services/jira/requests/base.rb @@ -5,12 +5,11 @@ module Jira class Base include ProjectServicesLoggable - attr_reader :jira_service, :project, :query + JIRA_API_VERSION = 2 - def initialize(jira_service, query: nil) + def initialize(jira_service, params = {}) @project = jira_service&.project @jira_service = jira_service - @query = query end def execute @@ -19,8 +18,19 @@ module Jira request end + def base_api_url + "/rest/api/#{api_version}" + end + private + attr_reader :jira_service, :project + + # override this method in the specific request class implementation if a differnt API version is required + def api_version + JIRA_API_VERSION + end + def client @client ||= jira_service.client end diff --git a/app/services/jira/requests/issues/list_service.rb b/app/services/jira/requests/issues/list_service.rb new file mode 100644 index 00000000000..44a3d3966a8 --- /dev/null +++ b/app/services/jira/requests/issues/list_service.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Jira + module Requests + module Issues + class ListService < Base + extend ::Gitlab::Utils::Override + + PER_PAGE = 100 + + def initialize(jira_service, params = {}) + super(jira_service, params) + + @jql = params[:jql].to_s + @page = params[:page].to_i || 1 + end + + private + + attr_reader :jql, :page + + override :url + def url + "#{base_api_url}/search?jql=#{CGI.escape(jql)}&startAt=#{start_at}&maxResults=#{PER_PAGE}&fields=*all" + end + + override :build_service_response + def build_service_response(response) + return ServiceResponse.success(payload: empty_payload) if response.blank? || response["issues"].blank? + + ServiceResponse.success(payload: { + issues: map_issues(response["issues"]), + is_last: last?(response), + total_count: response["total"].to_i + }) + end + + def map_issues(response) + response.map { |v| JIRA::Resource::Issue.build(client, v) } + end + + def empty_payload + { issues: [], is_last: true, total_count: 0 } + end + + def last?(response) + response["total"].to_i <= response["startAt"].to_i + response["issues"].size + end + + def start_at + (page - 1) * PER_PAGE + end + end + end + end +end diff --git a/app/services/jira/requests/projects.rb b/app/services/jira/requests/projects.rb deleted file mode 100644 index afb3b45fac1..00000000000 --- a/app/services/jira/requests/projects.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module Jira - module Requests - class Projects < Base - extend ::Gitlab::Utils::Override - - private - - override :url - def url - '/rest/api/2/project' - end - - override :build_service_response - def build_service_response(response) - return ServiceResponse.success(payload: empty_payload) unless response.present? - - ServiceResponse.success(payload: { projects: map_projects(response), is_last: true }) - end - - def map_projects(response) - 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 - { projects: [], is_last: true } - end - end - end -end diff --git a/app/services/jira/requests/projects/list_service.rb b/app/services/jira/requests/projects/list_service.rb new file mode 100644 index 00000000000..8ecfd358ffb --- /dev/null +++ b/app/services/jira/requests/projects/list_service.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Jira + module Requests + module Projects + class ListService < Base + extend ::Gitlab::Utils::Override + + def initialize(jira_service, params: {}) + super(jira_service, params) + + @query = params[:query] + end + + private + + attr_reader :query + + override :url + def url + "#{base_api_url}/project" + end + + override :build_service_response + def build_service_response(response) + return ServiceResponse.success(payload: empty_payload) unless response.present? + + ServiceResponse.success(payload: { projects: map_projects(response), is_last: true }) + end + + def map_projects(response) + response.map { |v| JIRA::Resource::Project.build(client, v) }.select(&method(:match_query?)) + end + + def match_query?(jira_project) + query = query.to_s.downcase + + jira_project&.key&.downcase&.include?(query) || jira_project&.name&.downcase&.include?(query) + end + + def empty_payload + { projects: [], is_last: true } + end + end + end + end +end diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index 126d8450568..115ebc94238 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -8,10 +8,10 @@ = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0) %div = f.label 'Two-Factor Authentication code', name: :otp_attempt - = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.' + = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.', data: { qa_selector: 'two_fa_code_field' } %p.form-text.text-muted.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes. .prepend-top-20 - = f.submit "Verify code", class: "btn btn-success" + = f.submit "Verify code", class: "btn btn-success", data: { qa_selector: 'verify_code_button' } - if @user.two_factor_u2f_enabled? = render "u2f/authenticate", params: params, resource: resource, resource_name: resource_name, render_remember_me: true, target_path: new_user_session_path diff --git a/app/views/profiles/two_factor_auths/_codes.html.haml b/app/views/profiles/two_factor_auths/_codes.html.haml index be0af977011..94fd40ed669 100644 --- a/app/views/profiles/two_factor_auths/_codes.html.haml +++ b/app/views/profiles/two_factor_auths/_codes.html.haml @@ -9,5 +9,5 @@ %span.monospace= code .d-flex - = link_to _('Proceed'), profile_account_path, class: 'btn btn-success append-right-10' + = link_to _('Proceed'), profile_account_path, class: 'btn btn-success append-right-10', data: { qa_selector: 'proceed_button' } = link_to _('Download codes'), "data:text/plain;charset=utf-8,#{CGI.escape(@codes.join("\n"))}", download: "gitlab-recovery-codes.txt", class: 'btn btn-default' diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 6e3de0447ac..b8c5d626d17 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -39,7 +39,7 @@ = _('To add the entry manually, provide the following details to the application on your phone.') %p.gl-mt-0.gl-mb-0 = _('Account: %{account}') % { account: @account_string } - %p.gl-mt-0.gl-mb-0 + %p.gl-mt-0.gl-mb-0{ data: { qa_selector: 'otp_secret_content' } } = _('Key: %{key}') %{ key: current_user.otp_secret.scan(/.{4}/).join(' ') } %p.two-factor-new-manual-content = _('Time based: Yes') @@ -49,9 +49,9 @@ = @error .form-group = label_tag :pin_code, _('Pin code'), class: "label-bold" - = text_field_tag :pin_code, nil, class: "form-control", required: true + = text_field_tag :pin_code, nil, class: "form-control", required: true, data: { qa_selector: 'pin_code_field' } .gl-mt-3 - = submit_tag _('Register with two-factor app'), class: 'btn btn-success' + = submit_tag _('Register with two-factor app'), class: 'btn btn-success', data: { qa_selector: 'register_2fa_app_button' } %hr diff --git a/app/workers/concerns/project_export_options.rb b/app/workers/concerns/project_export_options.rb deleted file mode 100644 index e9318c1ba43..00000000000 --- a/app/workers/concerns/project_export_options.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module ProjectExportOptions - extend ActiveSupport::Concern - - EXPORT_RETRY_COUNT = 3 - - included do - sidekiq_options retry: EXPORT_RETRY_COUNT, status_expiration: StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION - - # We mark the project export as failed once we have exhausted all retries - sidekiq_retries_exhausted do |job| - project = Project.find(job['args'][1]) - # rubocop: disable CodeReuse/ActiveRecord - job = project.export_jobs.find_by(jid: job["jid"]) - # rubocop: enable CodeReuse/ActiveRecord - - if job&.fail_op - Sidekiq.logger.info "Job #{job['jid']} for project #{project.id} has been set to failed state" - else - Sidekiq.logger.error "Failed to set Job #{job['jid']} for project #{project.id} to failed state" - end - end - end -end diff --git a/app/workers/group_export_worker.rb b/app/workers/group_export_worker.rb index 6fd977e43d8..e22b691d35e 100644 --- a/app/workers/group_export_worker.rb +++ b/app/workers/group_export_worker.rb @@ -6,6 +6,7 @@ class GroupExportWorker # rubocop:disable Scalability/IdempotentWorker feature_category :importers loggable_arguments 2 + sidekiq_options retry: false def perform(current_user_id, group_id, params = {}) current_user = User.find(current_user_id) diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb index d29348e85bc..6c8640138a1 100644 --- a/app/workers/project_export_worker.rb +++ b/app/workers/project_export_worker.rb @@ -3,12 +3,13 @@ class ProjectExportWorker # rubocop:disable Scalability/IdempotentWorker include ApplicationWorker include ExceptionBacktrace - include ProjectExportOptions feature_category :importers worker_resource_boundary :memory urgency :throttled loggable_arguments 2, 3 + sidekiq_options retry: false + sidekiq_options status_expiration: StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION def perform(current_user_id, project_id, after_export_strategy = {}, params = {}) current_user = User.find(current_user_id) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 30570a2227e..54052bda675 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -4,10 +4,11 @@ class RepositoryImportWorker # rubocop:disable Scalability/IdempotentWorker include ApplicationWorker include ExceptionBacktrace include ProjectStartImport - include ProjectImportOptions feature_category :importers worker_has_external_dependencies! + sidekiq_options retry: false + sidekiq_options status_expiration: Gitlab::Import::StuckImportJob::IMPORT_JOBS_EXPIRATION # technical debt: https://gitlab.com/gitlab-org/gitlab/issues/33991 sidekiq_options memory_killer_memory_growth_kb: ENV.fetch('MEMORY_KILLER_REPOSITORY_IMPORT_WORKER_MEMORY_GROWTH_KB', 50).to_i |