diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-19 18:07:55 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-19 18:07:55 +0300 |
commit | 3c55affa6684311ca73bc4e3d3bfb17b7541f63b (patch) | |
tree | b15020de36fa6b7c0b10bce78165eea79dc1455e /app | |
parent | 881435f2a3eeca1b5b544ad7c7510481b1773d1b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/lib/graphql.js | 14 | ||||
-rw-r--r-- | app/assets/javascripts/pages/users/index.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/users/profile/components/report_abuse_button.vue | 58 | ||||
-rw-r--r-- | app/assets/javascripts/users/profile/index.js | 23 | ||||
-rw-r--r-- | app/controllers/search_controller.rb | 11 | ||||
-rw-r--r-- | app/finders/ci/runners_finder.rb | 6 | ||||
-rw-r--r-- | app/graphql/resolvers/ci/runners_resolver.rb | 6 | ||||
-rw-r--r-- | app/models/activity_pub.rb | 7 | ||||
-rw-r--r-- | app/models/activity_pub/releases_subscription.rb | 22 | ||||
-rw-r--r-- | app/models/ci/runner.rb | 2 | ||||
-rw-r--r-- | app/models/note.rb | 6 | ||||
-rw-r--r-- | app/services/import/validate_remote_git_endpoint_service.rb | 81 | ||||
-rw-r--r-- | app/validators/json_schemas/activity_pub_follow_payload.json | 53 | ||||
-rw-r--r-- | app/views/users/_cover_controls.html.haml | 2 | ||||
-rw-r--r-- | app/views/users/_profile_basic_info.html.haml | 4 | ||||
-rw-r--r-- | app/views/users/show.html.haml | 36 |
16 files changed, 146 insertions, 187 deletions
diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js index 6ab530576fc..5285fa363a5 100644 --- a/app/assets/javascripts/lib/graphql.js +++ b/app/assets/javascripts/lib/graphql.js @@ -1,5 +1,4 @@ import { ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client/core'; -import { BatchHttpLink } from '@apollo/client/link/batch-http'; import { createUploadLink } from 'apollo-upload-client'; import { persistCache } from 'apollo3-cache-persist'; import ActionCableLink from '~/actioncable_link'; @@ -116,18 +115,14 @@ Object.defineProperty(window, 'pendingApolloRequests', { function createApolloClient(resolvers = {}, config = {}) { const { baseUrl, - batchMax = 10, cacheConfig = { typePolicies: {}, possibleTypes: {} }, fetchPolicy = fetchPolicies.CACHE_FIRST, typeDefs, httpHeaders = {}, fetchCredentials = 'same-origin', path = '/api/graphql', - useGet = false, } = config; - const shouldUnbatch = gon.features?.unbatchGraphqlQueries; - let ac = null; let uri = `${gon.relative_url_root || ''}${path}`; @@ -146,7 +141,6 @@ function createApolloClient(resolvers = {}, config = {}) { // We set to `same-origin` which is default value in modern browsers. // See https://github.com/whatwg/fetch/pull/585 for more information. credentials: fetchCredentials, - batchMax, }; /* @@ -165,14 +159,10 @@ function createApolloClient(resolvers = {}, config = {}) { return fetch(stripWhitespaceFromQuery(url, uri), options); }; - const requestLink = ApolloLink.split( - () => useGet || shouldUnbatch, - new HttpLink({ ...httpOptions, fetch: fetchIntervention }), - new BatchHttpLink(httpOptions), - ); + const requestLink = new HttpLink({ ...httpOptions, fetch: fetchIntervention }); const uploadsLink = ApolloLink.split( - (operation) => operation.getContext().hasUpload || operation.getContext().isSingleRequest, + (operation) => operation.getContext().hasUpload, createUploadLink(httpOptions), ); diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js index af55a5dc01a..4215cfbf409 100644 --- a/app/assets/javascripts/pages/users/index.js +++ b/app/assets/javascripts/pages/users/index.js @@ -1,7 +1,6 @@ import $ from 'jquery'; import { setCookie } from '~/lib/utils/common_utils'; import UserCallout from '~/user_callout'; -import { initReportAbuse } from '~/users/profile'; import { initProfileTabs } from '~/profile'; import UserTabs from './user_tabs'; @@ -25,4 +24,3 @@ const page = $('body').attr('data-page'); const action = page.split(':')[1]; initUserProfile(action); new UserCallout(); // eslint-disable-line no-new -initReportAbuse(); diff --git a/app/assets/javascripts/users/profile/components/report_abuse_button.vue b/app/assets/javascripts/users/profile/components/report_abuse_button.vue deleted file mode 100644 index 0e41a214888..00000000000 --- a/app/assets/javascripts/users/profile/components/report_abuse_button.vue +++ /dev/null @@ -1,58 +0,0 @@ -<script> -import { GlButton, GlTooltipDirective } from '@gitlab/ui'; -import { s__ } from '~/locale'; -import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; - -import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; - -export default { - name: 'ReportAbuseButton', - components: { - GlButton, - AbuseCategorySelector, - }, - directives: { - GlTooltip: GlTooltipDirective, - }, - inject: ['reportedUserId', 'reportedFromUrl'], - i18n: { - reportAbuse: s__('ReportAbuse|Report abuse to administrator'), - }, - data() { - return { - open: false, - }; - }, - computed: { - buttonTooltipText() { - return this.$options.i18n.reportAbuse; - }, - }, - methods: { - toggleDrawer(open) { - this.open = open; - }, - hideTooltips() { - this.$root.$emit(BV_HIDE_TOOLTIP); - }, - }, -}; -</script> -<template> - <span> - <gl-button - v-gl-tooltip="buttonTooltipText" - category="primary" - :aria-label="buttonTooltipText" - icon="error" - @click="toggleDrawer(true)" - @mouseout="hideTooltips" - /> - <abuse-category-selector - :reported-user-id="reportedUserId" - :reported-from-url="reportedFromUrl" - :show-drawer="open" - @close-drawer="toggleDrawer(false)" - /> - </span> -</template> diff --git a/app/assets/javascripts/users/profile/index.js b/app/assets/javascripts/users/profile/index.js deleted file mode 100644 index 3ae3cc2de98..00000000000 --- a/app/assets/javascripts/users/profile/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import Vue from 'vue'; -import ReportAbuseButton from './components/report_abuse_button.vue'; - -export const initReportAbuse = () => { - const el = document.getElementById('js-report-abuse'); - - if (!el) return false; - - const { reportAbusePath, reportedUserId, reportedFromUrl } = el.dataset; - - return new Vue({ - el, - name: 'ReportAbuseButtonRoot', - provide: { - reportAbusePath, - reportedUserId: reportedUserId ? parseInt(reportedUserId, 10) : null, - reportedFromUrl, - }, - render(createElement) { - return createElement(ReportAbuseButton); - }, - }); -}; diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 7fff31c767f..8db302530ea 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -4,7 +4,6 @@ class SearchController < ApplicationController include ControllerWithCrossProjectAccessCheck include SearchHelper include ProductAnalyticsTracking - include ProductAnalyticsTracking include SearchRateLimitable RESCUE_FROM_TIMEOUT_ACTIONS = [:count, :show, :autocomplete, :aggregations].freeze @@ -16,6 +15,12 @@ class SearchController < ApplicationController action: 'executed', destinations: [:redis_hll, :snowplow] + track_event :autocomplete, + name: 'i_search_total', + label: 'redis_hll_counters.search.search_total_unique_counts_monthly', + action: 'autocomplete', + destinations: [:redis_hll, :snowplow] + def self.search_rate_limited_endpoints %i[show count autocomplete] end @@ -40,10 +45,6 @@ class SearchController < ApplicationController end before_action only: :show do - push_frontend_feature_flag(:search_issues_hide_archived_projects, current_user) - end - - before_action only: :show do push_frontend_feature_flag(:search_merge_requests_hide_archived_projects, current_user) end diff --git a/app/finders/ci/runners_finder.rb b/app/finders/ci/runners_finder.rb index 331f732bff7..a716740ac08 100644 --- a/app/finders/ci/runners_finder.rb +++ b/app/finders/ci/runners_finder.rb @@ -20,6 +20,7 @@ module Ci filter_by_upgrade_status! filter_by_runner_type! filter_by_tag_list! + filter_by_creator_id! sort! request_tag_list! @@ -113,6 +114,11 @@ module Ci end end + def filter_by_creator_id! + creator_id = @params[:creator_id] + @runners = @runners.with_creator_id(creator_id) if creator_id.present? + end + def sort! @runners = @runners.order_by(sort_key) end diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb index 3289f1d0056..efc692f7bab 100644 --- a/app/graphql/resolvers/ci/runners_resolver.rb +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -41,6 +41,10 @@ module Resolvers required: false, description: 'Filter by upgrade status.' + argument :creator_id, ::Types::GlobalIDType[::User].as('UserID'), + required: false, + description: 'Filter runners by creator ID.' + def resolve_with_lookahead(**args) apply_lookahead( ::Ci::RunnersFinder @@ -68,6 +72,8 @@ module Resolvers upgrade_status: params[:upgrade_status], search: params[:search], sort: params[:sort]&.to_s, + creator_id: + params[:creator_id] ? ::GitlabSchema.parse_gid(params[:creator_id], expected_type: ::User).model_id : nil, preload: false # we'll handle preloading ourselves }.compact .merge(parent_param) diff --git a/app/models/activity_pub.rb b/app/models/activity_pub.rb new file mode 100644 index 00000000000..9131d8be776 --- /dev/null +++ b/app/models/activity_pub.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module ActivityPub + def self.table_name_prefix + "activity_pub_" + end +end diff --git a/app/models/activity_pub/releases_subscription.rb b/app/models/activity_pub/releases_subscription.rb new file mode 100644 index 00000000000..a6304f1fc35 --- /dev/null +++ b/app/models/activity_pub/releases_subscription.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module ActivityPub + class ReleasesSubscription < ApplicationRecord + belongs_to :project, optional: false + + enum :status, [:requested, :accepted], default: :requested + + attribute :payload, Gitlab::Database::Type::JsonPgSafe.new + + validates :payload, json_schema: { filename: 'activity_pub_follow_payload' }, allow_blank: true + validates :subscriber_url, presence: true, uniqueness: { case_sensitive: false, scope: :project_id }, + public_url: true + validates :subscriber_inbox_url, uniqueness: { case_sensitive: false, scope: :project_id }, + public_url: { allow_nil: true } + validates :shared_inbox_url, public_url: { allow_nil: true } + + def self.find_by_subscriber_url(subscriber_url) + find_by('LOWER(subscriber_url) = ?', subscriber_url.downcase) + end + end +end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 91c919dc662..8a9e51ef133 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -123,6 +123,8 @@ module Ci joins(:runner_namespaces).where(ci_runner_namespaces: { namespace_id: group_id }) } + scope :with_creator_id, -> (value) { where(creator_id: value) } + scope :belonging_to_group_or_project_descendants, -> (group_id) { group_ids = Ci::NamespaceMirror.by_group_and_descendants(group_id).select(:namespace_id) project_ids = Ci::ProjectMirror.by_namespace_id(group_ids).select(:project_id) diff --git a/app/models/note.rb b/app/models/note.rb index eae7a40fb4e..6f4a56dd3cc 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -383,7 +383,11 @@ class Note < ApplicationRecord end def for_project_noteable? - !(for_personal_snippet? || for_abuse_report?) + !(for_personal_snippet? || for_abuse_report? || group_level_issue?) + end + + def group_level_issue? + (for_issue? || for_work_item?) && noteable&.project_id.blank? end def for_design? diff --git a/app/services/import/validate_remote_git_endpoint_service.rb b/app/services/import/validate_remote_git_endpoint_service.rb index 2177238fddf..a994072c4aa 100644 --- a/app/services/import/validate_remote_git_endpoint_service.rb +++ b/app/services/import/validate_remote_git_endpoint_service.rb @@ -13,8 +13,6 @@ module Import GIT_PROTOCOL_PKT_LEN = 4 GIT_MINIMUM_RESPONSE_LENGTH = GIT_PROTOCOL_PKT_LEN + GIT_EXPECTED_FIRST_PACKET_LINE.length EXPECTED_CONTENT_TYPE = "application/x-#{GIT_SERVICE_NAME}-advertisement" - INVALID_BODY_MESSAGE = 'Not a git repository: Invalid response body' - INVALID_CONTENT_TYPE_MESSAGE = 'Not a git repository: Invalid content-type' def initialize(params) @params = params @@ -32,35 +30,32 @@ module Import uri.fragment = nil url = Gitlab::Utils.append_path(uri.to_s, "/info/refs?service=#{GIT_SERVICE_NAME}") - response, response_body = http_get_and_extract_first_chunks(url) - - validate(uri, response, response_body) - rescue *Gitlab::HTTP::HTTP_ERRORS => err - error_result("HTTP #{err.class.name.underscore} error: #{err.message}") - rescue StandardError => err - ServiceResponse.error( - message: "Internal #{err.class.name.underscore} error: #{err.message}", - reason: 500 - ) - end - - private - - def http_get_and_extract_first_chunks(url) - # We are interested only in the first chunks of the response - # So we're using stream_body: true and breaking when receive enough body - response = nil response_body = '' - - Gitlab::HTTP.get(url, stream_body: true, follow_redirects: false, basic_auth: auth) do |response_chunk| - response = response_chunk - response_body += response_chunk - break if GIT_MINIMUM_RESPONSE_LENGTH <= response_body.length + result = nil + Gitlab::HTTP.try_get(url, stream_body: true, follow_redirects: false, basic_auth: auth) do |fragment| + response_body += fragment + next if response_body.length < GIT_MINIMUM_RESPONSE_LENGTH + + result = if status_code_is_valid(fragment) && content_type_is_valid(fragment) && response_body_is_valid(response_body) + :success + else + :error + end + + # We are interested only in the first chunks of the response + # So we're using stream_body: true and breaking when receive enough body + break end - [response, response_body] + if result == :success + ServiceResponse.success + else + ServiceResponse.error(message: "#{uri} is not a valid HTTP Git repository") + end end + private + def auth unless @params[:user].to_s.blank? { @@ -70,38 +65,16 @@ module Import end end - def validate(uri, response, response_body) - return status_code_error(uri, response) unless status_code_is_valid?(response) - return error_result(INVALID_CONTENT_TYPE_MESSAGE) unless content_type_is_valid?(response) - return error_result(INVALID_BODY_MESSAGE) unless response_body_is_valid?(response_body) - - ServiceResponse.success - end - - def status_code_error(uri, response) - http_code = response.http_response.code.to_i - message = response.http_response.message || Rack::Utils::HTTP_STATUS_CODES[http_code] - - error_result( - "#{uri} endpoint error: #{http_code}#{message.presence&.prepend(' ')}", - http_code - ) - end - - def error_result(message, reason = nil) - ServiceResponse.error(message: message, reason: reason) - end - - def status_code_is_valid?(response) - response.http_response.code == '200' + def status_code_is_valid(fragment) + fragment.http_response.code == '200' end - def content_type_is_valid?(response) - response.http_response['content-type'] == EXPECTED_CONTENT_TYPE + def content_type_is_valid(fragment) + fragment.http_response['content-type'] == EXPECTED_CONTENT_TYPE end - def response_body_is_valid?(response_body) - response_body.length <= GIT_MINIMUM_RESPONSE_LENGTH && response_body.match?(GIT_BODY_MESSAGE_REGEXP) + def response_body_is_valid(response_body) + response_body.match?(GIT_BODY_MESSAGE_REGEXP) end end end diff --git a/app/validators/json_schemas/activity_pub_follow_payload.json b/app/validators/json_schemas/activity_pub_follow_payload.json new file mode 100644 index 00000000000..1f453ce840f --- /dev/null +++ b/app/validators/json_schemas/activity_pub_follow_payload.json @@ -0,0 +1,53 @@ +{ + "description": "ActivityPub Follow activity payload", + "type": "object", + "required": [ + "@context", + "id", + "type", + "actor", + "object" + ], + "properties": { + "@context": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array" + } + ] + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "actor": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "required": [ + "id" + ], + "id": { + "type": "string" + }, + "inbox": { + "type": "string" + }, + "additionalProperties": true + } + ] + }, + "object": { + "type": "string" + }, + "additionalProperties": true + } +} diff --git a/app/views/users/_cover_controls.html.haml b/app/views/users/_cover_controls.html.haml deleted file mode 100644 index 899a08c8a17..00000000000 --- a/app/views/users/_cover_controls.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -.cover-controls.gl-display-flex.gl-gap-3.gl-pb-4 - = yield diff --git a/app/views/users/_profile_basic_info.html.haml b/app/views/users/_profile_basic_info.html.haml index 6de9e80008e..7dd131dbe2c 100644 --- a/app/views/users/_profile_basic_info.html.haml +++ b/app/views/users/_profile_basic_info.html.haml @@ -2,9 +2,5 @@ = render 'middle_dot_divider', stacking: true do @#{@user.username} - if can?(current_user, :read_user_profile, @user) - - unless Feature.enabled?(:user_profile_overflow_menu_vue) - = render 'middle_dot_divider', stacking: true do - = s_('UserProfile|User ID: %{id}') % { id: @user.id } - = clipboard_button(title: s_('UserProfile|Copy user ID'), text: @user.id) = render 'middle_dot_divider', stacking: true do = s_('Member since %{date}') % { date: l(@user.created_at.to_date, format: :long) } diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 0881c5bba54..034e7f18bbe 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -17,32 +17,16 @@ .user-profile .cover-block.user-cover-block.gl-border-t.gl-border-b.gl-mt-n1 %div{ class: container_class } - - if Feature.enabled?(:user_profile_overflow_menu_vue) - .cover-controls.gl-display-flex.gl-gap-3.gl-pb-4 - = render 'users/follow_user' - -# The following edit button is mutually exclusive to the follow user button, they won't be shown together - - if @user == current_user - = render Pajamas::ButtonComponent.new(href: profile_path, - button_options: { class: 'gl-flex-grow-1', title: s_('UserProfile|Edit profile') }) do - = s_("UserProfile|Edit profile") - = render 'users/view_gpg_keys' - = render 'users/view_user_in_admin_area' - .js-user-profile-actions{ data: user_profile_actions_data(@user) } - - else - = render layout: 'users/cover_controls' do - - if @user == current_user - = render Pajamas::ButtonComponent.new(href: profile_path, - icon: 'pencil', - button_options: { class: 'gl-flex-grow-1 has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' }}) - - elsif current_user - #js-report-abuse{ data: { report_abuse_path: add_category_abuse_reports_path, reported_user_id: @user.id, reported_from_url: user_url(@user) } } - = render 'users/view_gpg_keys' - - if can?(current_user, :read_user_profile, @user) - = render Pajamas::ButtonComponent.new(href: user_path(@user, rss_url_options), - icon: 'rss', - button_options: { class: 'gl-flex-grow-1 has-tooltip', title: s_('UserProfile|Subscribe'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' }}) - = render 'users/view_user_in_admin_area' - = render 'users/follow_user' + .cover-controls.gl-display-flex.gl-gap-3.gl-pb-4 + = render 'users/follow_user' + -# The following edit button is mutually exclusive to the follow user button, they won't be shown together + - if @user == current_user + = render Pajamas::ButtonComponent.new(href: profile_path, + button_options: { class: 'gl-flex-grow-1', title: s_('UserProfile|Edit profile') }) do + = s_("UserProfile|Edit profile") + = render 'users/view_gpg_keys' + = render 'users/view_user_in_admin_area' + .js-user-profile-actions{ data: user_profile_actions_data(@user) } .profile-header{ class: [('with-no-profile-tabs' if profile_tabs.empty?), ('gl-mb-4!' if show_super_sidebar?)] } .gl-display-inline-block.gl-mx-8.gl-vertical-align-top |