Commit c6c74378 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent d80f3cd7
# frozen_string_literal: true
module InvisibleCaptcha
module InvisibleCaptchaOnSignup
extend ActiveSupport::Concern
included do
......
......@@ -3,7 +3,7 @@
class Projects::ReleasesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project, except: [:index]
before_action :release, only: %i[edit show update]
before_action :release, only: %i[edit show update downloads]
before_action :authorize_read_release!
before_action do
push_frontend_feature_flag(:release_issue_summary, project)
......@@ -40,6 +40,10 @@ class Projects::ReleasesController < Projects::ApplicationController
end
end
def downloads
redirect_to link.url
end
protected
def releases
......@@ -69,6 +73,14 @@ class Projects::ReleasesController < Projects::ApplicationController
@release ||= project.releases.find_by_tag!(sanitized_tag_name)
end
def link
release.links.find_by_filepath!(sanitized_filepath)
end
def sanitized_filepath
CGI.unescape(params[:filepath])
end
def sanitized_tag_name
CGI.unescape(params[:tag])
end
......
......@@ -4,7 +4,7 @@ class RegistrationsController < Devise::RegistrationsController
include Recaptcha::Verify
include AcceptsPendingInvitations
include RecaptchaExperimentHelper
include InvisibleCaptcha
include InvisibleCaptchaOnSignup
layout :choose_layout
......
......@@ -17,12 +17,12 @@ module Mutations
argument :due_date,
Types::TimeType,
required: true,
required: false,
description: copy_field_description(Types::IssueType, :due_date)
argument :confidential,
GraphQL::BOOLEAN_TYPE,
required: true,
required: false,
description: copy_field_description(Types::IssueType, :confidential)
def resolve(project_path:, iid:, **args)
......
......@@ -10,8 +10,9 @@ class MergeRequestDiffCommit < ApplicationRecord
sha_attribute :sha
alias_attribute :id, :sha
# Deprecated; use `bulk_insert!` from `BulkInsertSafe` mixin instead.
# cf. https://gitlab.com/gitlab-org/gitlab/issues/207989 for progress
def self.create_bulk(merge_request_diff_id, commits)
warn 'Deprecated; use `bulk_insert` from `BulkInsertSafe` mixin instead'
rows = commits.map.with_index do |commit, index|
# See #parent_ids.
commit_hash = commit.to_hash.except(:parent_ids)
......
......@@ -28,7 +28,7 @@ class PostReceiveService
response.add_alert_message(message)
end
broadcast_message = BroadcastMessage.current&.last&.message
broadcast_message = BroadcastMessage.current_banner_messages&.last&.message
response.add_alert_message(broadcast_message)
response.add_merge_request_urls(merge_request_urls)
......
......@@ -2,7 +2,6 @@
- if note.emoji_awardable?
.note-actions-item
= link_to '#', title: _('Add reaction'), class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip", data: { position: 'right' } do
= icon('spinner spin')
%span{ class: 'link-highlight award-control-icon-neutral' }= sprite_icon('slight-smile')
%span{ class: 'link-highlight award-control-icon-positive' }= sprite_icon('smiley')
%span{ class: 'link-highlight award-control-icon-super-positive' }= sprite_icon('smile')
......
---
title: Add trigram index on snippet description
merge_request: 26341
author:
type: performance
---
title: Add filepath redirect url
merge_request: 25541
author:
type: added
---
title: Remove .fa-spinner from app/views/snippets/notes
merge_request: 25036
author: nuwe1
type: other
---
title: Limit notification-type broadcast display to web interface
merge_request: 26236
author: Aleksandrs Ļedovskis
type: changed
---
title: Add grape custom validator for sha params
merge_request: 26220
author: Rajendra Kadam
type: added
......@@ -171,6 +171,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :releases, only: [:index, :show, :edit], param: :tag, constraints: { tag: %r{[^/]+} } do
member do
get :evidence
get :downloads, path: 'downloads/*filepath', format: false
end
end
......
# frozen_string_literal: true
class AddIndexOnSnippetDescription < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_snippets_on_description_trigram'
disable_ddl_transaction!
def up
add_concurrent_index :snippets, :description, name: INDEX_NAME, using: :gin, opclass: { description: :gin_trgm_ops }
end
def down
remove_concurrent_index_by_name :snippets, INDEX_NAME
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_02_27_165129) do
ActiveRecord::Schema.define(version: 2020_03_03_074328) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
......@@ -3964,6 +3964,7 @@ ActiveRecord::Schema.define(version: 2020_02_27_165129) do
t.index ["author_id"], name: "index_snippets_on_author_id"
t.index ["content"], name: "index_snippets_on_content_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["created_at"], name: "index_snippets_on_created_at"
t.index ["description"], name: "index_snippets_on_description_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["file_name"], name: "index_snippets_on_file_name_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["project_id", "visibility_level"], name: "index_snippets_on_project_id_and_visibility_level"
t.index ["title"], name: "index_snippets_on_title_trigram", opclass: :gin_trgm_ops, using: :gin
......
......@@ -107,6 +107,7 @@ recorded:
- User was deleted ([introduced](https://gitlab.com/gitlab-org/gitlab/issues/251) in GitLab 12.8)
- User was added ([introduced](https://gitlab.com/gitlab-org/gitlab/issues/251) in GitLab 12.8)
- User was blocked via Admin Area ([introduced](https://gitlab.com/gitlab-org/gitlab/issues/251) in GitLab 12.8)
- User was blocked via API ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25872) in GitLab 12.9)
It's possible to filter particular actions by choosing an audit data type from
the filter dropdown box. You can further filter by specific group, project, or user
......
......@@ -8011,7 +8011,7 @@ input UpdateIssueInput {
"""
Indicates the issue is confidential
"""
confidential: Boolean!
confidential: Boolean
"""
Description of the issue
......@@ -8021,7 +8021,7 @@ input UpdateIssueInput {
"""
Due date of the issue
"""
dueDate: Time!
dueDate: Time
"""
The desired health status
......
......@@ -21114,13 +21114,9 @@
"name": "dueDate",
"description": "Due date of the issue",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Time",
"ofType": null
}
"kind": "SCALAR",
"name": "Time",
"ofType": null
},
"defaultValue": null
},
......@@ -21128,13 +21124,9 @@
"name": "confidential",
"description": "Indicates the issue is confidential",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": null
},
......
......@@ -1168,8 +1168,11 @@ Parameters:
- `id` (required) - id of specified user
Will return `201 OK` on success, `404 User Not Found` is user cannot be found or
`403 Forbidden` when trying to block an already blocked user by LDAP synchronization.
Returns:
- `201 OK` on success.
- `404 User Not Found` if user cannot be found.
- `403 Forbidden` when trying to block an already blocked user by LDAP synchronization.
## Unblock user
......
......@@ -14,6 +14,17 @@ module API
end
end
class GitSha < Grape::Validations::Base
def validate_param!(attr_name, params)
sha = params[attr_name]
return if Commit::EXACT_COMMIT_SHA_PATTERN.match?(sha)
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
message: "should be a valid sha"
end
end
class Absence < Grape::Validations::Base
def validate_param!(attr_name, params)
return if params.respond_to?(:key?) && !params.key?(attr_name)
......@@ -50,6 +61,7 @@ module API
end
Grape::Validations.register_validator(:file_path, ::API::Helpers::CustomValidators::FilePath)
Grape::Validations.register_validator(:git_sha, ::API::Helpers::CustomValidators::GitSha)
Grape::Validations.register_validator(:absence, ::API::Helpers::CustomValidators::Absence)
Grape::Validations.register_validator(:integer_none_any, ::API::Helpers::CustomValidators::IntegerNoneAny)
Grape::Validations.register_validator(:array_none_any, ::API::Helpers::CustomValidators::ArrayNoneAny)
......@@ -528,11 +528,18 @@ module API
user = User.find_by(id: params[:id])
not_found!('User') unless user
if !user.ldap_blocked?
user.block
else
if user.ldap_blocked?
forbidden!('LDAP blocked users cannot be modified by the API')
end
break if user.blocked?
result = ::Users::BlockService.new(current_user).execute(user)
if result[:status] == :success
true
else
render_api_error!(result[:message], result[:http_status])
end
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -13,6 +13,8 @@ require_dependency 'declarative_policy/step'
require_dependency 'declarative_policy/base'
module DeclarativePolicy
extend PreferredScope
CLASS_CACHE_MUTEX = Mutex.new
CLASS_CACHE_IVAR = :@__DeclarativePolicy_CLASS_CACHE
......
# frozen_string_literal: true
module DeclarativePolicy
PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
module PreferredScope
PREFERRED_SCOPE_KEY = :"DeclarativePolicy.preferred_scope"
class << self
def with_preferred_scope(scope)
Thread.current[PREFERRED_SCOPE_KEY], old_scope = scope, Thread.current[PREFERRED_SCOPE_KEY]
yield
......
......@@ -4,95 +4,97 @@ module Gitlab
module BackgroundMigration
module UserMentions
module Models
# == IsolatedMentionable concern
#
# Shortcutted for isolation version of Mentionable to be used in mentions migrations
#
module IsolatedMentionable
extend ::ActiveSupport::Concern
class_methods do
# Indicate which attributes of the Mentionable to search for GFM references.
def attr_mentionable(attr, options = {})
attr = attr.to_s
mentionable_attrs << [attr, options]
module Concerns
# == IsolatedMentionable concern
#
# Shortcutted for isolation version of Mentionable to be used in mentions migrations
#
module IsolatedMentionable
extend ::ActiveSupport::Concern
class_methods do
# Indicate which attributes of the Mentionable to search for GFM references.
def attr_mentionable(attr, options = {})
attr = attr.to_s
mentionable_attrs << [attr, options]
end
end
end
included do
# Accessor for attributes marked mentionable.
cattr_accessor :mentionable_attrs, instance_accessor: false do
[]
end
included do
# Accessor for attributes marked mentionable.
cattr_accessor :mentionable_attrs, instance_accessor: false do
[]
end
if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) }
if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) }
end
end
end
def all_references(current_user = nil, extractor: nil)
# Use custom extractor if it's passed in the function parameters.
if extractor
extractors[current_user] = extractor
else
extractor = extractors[current_user] ||= ::Gitlab::ReferenceExtractor.new(project, current_user)
def all_references(current_user = nil, extractor: nil)
# Use custom extractor if it's passed in the function parameters.
if extractor
extractors[current_user] = extractor
else
extractor = extractors[current_user] ||= ::Gitlab::ReferenceExtractor.new(project, current_user)
extractor.reset_memoized_values
end
extractor.reset_memoized_values
end
self.class.mentionable_attrs.each do |attr, options|
text = __send__(attr) # rubocop:disable GitlabSecurity/PublicSend
options = options.merge(
cache_key: [self, attr],
author: author,
skip_project_check: skip_project_check?
).merge(mentionable_params)
self.class.mentionable_attrs.each do |attr, options|
text = __send__(attr) # rubocop:disable GitlabSecurity/PublicSend
options = options.merge(
cache_key: [self, attr],
author: author,
skip_project_check: skip_project_check?
).merge(mentionable_params)
cached_html = self.try(:updated_cached_html_for, attr.to_sym)
options[:rendered] = cached_html if cached_html
cached_html = self.try(:updated_cached_html_for, attr.to_sym)
options[:rendered] = cached_html if cached_html
extractor.analyze(text, options)
end
extractor.analyze(text, options)
end
extractor
end
extractor
end
def extractors
@extractors ||= {}
end
def extractors
@extractors ||= {}
end
def skip_project_check?
false
end
def skip_project_check?
false
end
def build_mention_values(resource_foreign_key)
refs = all_references(author)
def build_mention_values(resource_foreign_key)
refs = all_references(author)
mentioned_users_ids = array_to_sql(refs.mentioned_users.pluck(:id))
mentioned_projects_ids = array_to_sql(refs.mentioned_projects.pluck(:id))
mentioned_groups_ids = array_to_sql(refs.mentioned_groups.pluck(:id))
mentioned_users_ids = array_to_sql(refs.mentioned_users.pluck(:id))
mentioned_projects_ids = array_to_sql(refs.mentioned_projects.pluck(:id))
mentioned_groups_ids = array_to_sql(refs.mentioned_groups.pluck(:id))
return if mentioned_users_ids.blank? && mentioned_projects_ids.blank? && mentioned_groups_ids.blank?
return if mentioned_users_ids.blank? && mentioned_projects_ids.blank? && mentioned_groups_ids.blank?
{
"#{resource_foreign_key}": user_mention_resource_id,
note_id: user_mention_note_id,
mentioned_users_ids: mentioned_users_ids,
mentioned_projects_ids: mentioned_projects_ids,
mentioned_groups_ids: mentioned_groups_ids
}
end
{
"#{resource_foreign_key}": user_mention_resource_id,
note_id: user_mention_note_id,
mentioned_users_ids: mentioned_users_ids,
mentioned_projects_ids: mentioned_projects_ids,
mentioned_groups_ids: mentioned_groups_ids
}
end
def array_to_sql(ids_array)
return unless ids_array.present?
def array_to_sql(ids_array)
return unless ids_array.present?
'{' + ids_array.join(", ") + '}'
end
'{' + ids_array.join(", ") + '}'
end
private
private
def mentionable_params
{}
def mentionable_params
{}
end
end
end
end
......
......@@ -4,17 +4,19 @@ module Gitlab
module BackgroundMigration
module UserMentions
module Models
# Extract common no_quote_columns method used in determining the columns that do not need
# to be quoted for corresponding models
module MentionableMigrationMethods
extend ::ActiveSupport::Concern
module Concerns
# Extract common no_quote_columns method used in determining the columns that do not need
# to be quoted for corresponding models
module MentionableMigrationMethods
extend ::ActiveSupport::Concern
class_methods do
def no_quote_columns
[
:note_id,
user_mention_model.resource_foreign_key
]
class_methods do
def no_quote_columns
[
:note_id,
user_mention_model.resource_foreign_key
]
end
end
end
end
......
......@@ -7,7 +7,7 @@ module Gitlab
module Models
module DesignManagement
class Design < ActiveRecord::Base
include MentionableMigrationMethods
include Concerns::MentionableMigrationMethods
def self.user_mention_model
Gitlab::BackgroundMigration::UserMentions::Models::DesignUserMention
......
......@@ -6,9 +6,9 @@ module Gitlab
module UserMentions
module Models
class Epic < ActiveRecord::Base
include IsolatedMentionable
include Concerns::IsolatedMentionable
include Concerns::MentionableMigrationMethods
include CacheMarkdownField
include MentionableMigrationMethods
attr_mentionable :title, pipeline: :single_line
attr_mentionable :description
......
......@@ -6,7 +6,7 @@ module Gitlab
module UserMentions
module Models
class Note < ActiveRecord::Base
include IsolatedMentionable
include Concerns::IsolatedMentionable
include CacheMarkdownField
self.table_name = 'notes'
......
......@@ -6,9 +6,9 @@ module Gitlab
module UserMentions
module Models
class Snippet < ActiveRecord::Base
include IsolatedMentionable
include Concerns::IsolatedMentionable
include Concerns::MentionableMigrationMethods
include CacheMarkdownField
include MentionableMigrationMethods
attr_mentionable :title, pipeline: :single_line
attr_mentionable :description
......
......@@ -44,8 +44,6 @@ module Gitlab
end
end
helpers :reports
def value
@config[:reports] = reports_value if @config.key?(:reports)
@config
......
......@@ -49,8 +49,6 @@ module Gitlab
description: 'Environment variables available for this job.',
inherit: false
helpers :trigger, :needs, :variables
attributes :when, :allow_failure
def self.matching?(name, config)
......
......@@ -28,8 +28,6 @@ module Gitlab
entry :paths, Entry::Paths,
description: 'Specify which paths should be cached across builds.'
helpers :key
attributes :policy
def value
......
......@@ -61,8 +61,6 @@ module Gitlab
description: 'Default artifacts.',
inherit: false
helpers :before_script, :image, :services, :after_script, :cache
private
def overwrite_entry(deps, key, current_entry)
......
......@@ -128,11 +128,6 @@ module Gitlab
description: 'This job will produce a release.',
inherit: false
helpers :before_script, :script, :type, :after_script,
:cache, :image, :services, :variables,
:artifacts, :environment, :coverage, :retry,
:needs, :interruptible, :release, :tags
attributes :script, :tags, :allow_failure, :when, :dependencies,
:needs, :retry, :parallel, :start_in,
:interruptible, :timeout, :resource_group, :release
......
......@@ -54,8 +54,6 @@ module Gitlab
allowed_when: %w[on_success on_failure always never manual delayed].freeze
}
helpers :stage, :only, :except, :rules
attributes :extends, :rules
end
......
......@@ -33,8 +33,6 @@ module Gitlab
validates :description, type: String, presence: true
end
helpers :assets
def value
@config[:assets] = assets_value if @config.key?(:assets)
@config
......
......@@ -23,8 +23,6 @@ module Gitlab
validates :links, array_of_hashes: true, presence: true
end
helpers :links
def value
@config[:links] = links_value if @config.key?(:links)
@config
......
......@@ -67,9 +67,7 @@ module Gitlab
entry :workflow, Entry::Workflow,
description: 'List of evaluable rules to determine Pipeline status'
helpers :default, :stages, :types, :variables, :workflow
helpers :jobs, dynamic: true
dynamic_helpers :jobs
delegate :before_script_value,
:image_value,
......
......@@ -7,8 +7,13 @@ module Gitlab
##
# Entry that represents a configuration of Docker service.
#
class Service < Image
# TODO: remove duplication with Image superclass by defining a common
# Imageable concern.
# https://gitlab.com/gitlab-org/gitlab/issues/208774
class Service < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable
include ::Gitlab::Config::Entry::Configurable
ALLOWED_KEYS = %i[name entrypoint command alias ports].freeze
......@@ -16,9 +21,9 @@ module Gitlab
validates :config, hash_or_string: true
validates :config, allowed_keys: ALLOWED_KEYS
validates :config, disallowed_keys: %i[ports], unless: :with_image_ports?
validates :name, type: String, presence: true
validates :entrypoint, array_of_strings: true, allow_nil: true
validates :command, array_of_strings: true, allow_nil: true
validates :alias, type: String, allow_nil: true
validates :alias, type: String, presence: true, unless: ->(record) { record.ports.blank? }
......@@ -27,6 +32,8 @@ module Gitlab
entry :ports, Entry::Ports,
description: 'Ports used to expose the service'
attributes :ports
def alias
value[:alias]
end
......@@ -34,6 +41,29 @@ module Gitlab
def command
value[:command]
end
def name
value[:name]
end
def entrypoint
value[:entrypoint]
end
def value
return { name: @config } if string?
return @config if hash?
{}
end
def with_image_ports?
opt(:with_image_ports)
end
def skip_config_hash_validation?
true
end
end
end
end
......
......@@ -75,7 +75,8 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def entry(key, entry, description: nil, default: nil, inherit: nil, reserved: nil, metadata: {})
raise ArgumentError, "Entry #{key} already defined" if @nodes.to_h[key.to_sym]
entry_name = key.to_sym
raise ArgumentError, "Entry #{key} already defined" if @nodes.to_h[entry_name]
factory = ::Gitlab::Config::Entry::Factory.new(entry)
.with(description: description)
......@@ -84,10 +85,17 @@ module Gitlab
.with(reserved: reserved)
.metadata(metadata)
(@nodes ||= {}).merge!(key.to_sym => factory)
@nodes ||= {}
@nodes[entry_name] = factory
helpers(entry_name)
end
# rubocop: enable CodeReuse/ActiveRecord
def dynamic_helpers(*nodes)
helpers(*nodes, dynamic: true)
end
def helpers(*nodes, dynamic: false)
nodes.each do |symbol|
if method_defined?("#{symbol}_defined?") || method_defined?("#{symbol}_value")
......
# frozen_string_literal: true
module Gitlab
module Email
ProcessingError = Class.new(StandardError)
EmailUnparsableError = Class.new(ProcessingError)
SentNotificationNotFoundError = Class.new(ProcessingError)
ProjectNotFound = Class.new(ProcessingError)
EmptyEmailError = Class.new(ProcessingError)
AutoGeneratedEmailError = Class.new(ProcessingError)
UserNotFoundError = Class.new(ProcessingError)
UserBlockedError = Class.new(ProcessingError)
UserNotAuthorizedError = Class.new(ProcessingError)
NoteableNotFoundError = Class.new(ProcessingError)
InvalidRecordError = Class.new(ProcessingError)
InvalidNoteError = Class.new(InvalidRecordError)
InvalidIssueError = Class.new(InvalidRecordError)
InvalidMergeRequestError = Class.new(InvalidRecordError)
UnknownIncomingEmail = Class.new(ProcessingError)
InvalidAttachment = Class.new(ProcessingError)
end
end
......@@ -5,23 +5,6 @@ require_dependency 'gitlab/email/handler'
# Inspired in great part by Discourse's Email::Receiver
module Gitlab
module Email
ProcessingError = Class.new(StandardError)
EmailUnparsableError = Class.new(ProcessingError)
SentNotificationNotFoundError = Class.new(ProcessingError)
ProjectNotFound = Class.new(ProcessingError)
EmptyEmailError = Class.new(ProcessingError)
AutoGeneratedEmailError = Class.new(ProcessingError)
UserNotFoundError = Class.new(ProcessingError)
UserBlockedError = Class.new(ProcessingError)
UserNotAuthorizedError = Class.new(ProcessingError)
NoteableNotFoundError = Class.new(ProcessingError)
InvalidRecordError = Class.new(ProcessingError)
InvalidNoteError = Class.new(InvalidRecordError)
InvalidIssueError = Class.new(InvalidRecordError)
InvalidMergeRequestError = Class.new(InvalidRecordError)
UnknownIncomingEmail = Class.new(ProcessingError)
InvalidAttachment = Class.new(ProcessingError)
class Receiver
def initialize(raw)
@raw = raw
......
......@@ -14,7 +14,8 @@ module Gitlab
end
def drop_jobs!(search_metadata, timeout:)
completed = false
start_time = Gitlab::Metrics::System.monotonic_time
completed = true
deleted_jobs = 0
job_search_metadata =
......@@ -27,18 +28,16 @@ module Gitlab
raise NoMetadataError if job_search_metadata.empty?
raise InvalidQueueError unless queue
begin
Timeout.timeout(timeout) do
queue.each do |job|
next unless job_matches?(job, job_search_metadata)
queue.each do |job|
if timeout_exceeded?(start_time, timeout)
completed = false
break
end
job.delete
deleted_jobs += 1
end
next unless job_matches?(job, job_search_metadata)
completed = true
end
rescue Timeout::Error
job.delete
deleted_jobs += 1
end
{
......@@ -48,6 +47,8 @@ module Gitlab
}
end
private
def queue
strong_memoize(:queue) do
# Sidekiq::Queue.new always returns a queue, even if it doesn't
......@@ -59,5 +60,9 @@ module Gitlab
def job_matches?(job, job_search_metadata)
job_search_metadata.all? { |key, value| job[key] == value }
end
def timeout_exceeded?(start_time, timeout)
(Gitlab::Metrics::System.monotonic_time - start_time) > timeout
end
end
end
# frozen_string_literal: true
module Gitlab
module Utils
# This class estimates the JSON blob byte size of a ruby object using as
# little allocations as possible.
# The estimation should be quite accurate when using simple objects.
#
# Example:
#
# Gitlab::Utils::JsonSizeEstimator.estimate(["a", { b: 12, c: nil }])
class JsonSizeEstimator
ARRAY_BRACKETS_SIZE = 2 # []
OBJECT_BRACKETS_SIZE = 2 # {}
DOUBLEQUOTE_SIZE = 2 # ""
COLON_SIZE = 1 # : character size from {"a": 1}
MINUS_SIGN_SIZE = 1 # - character size from -1
NULL_SIZE = 4 # null
class << self
# Returns: integer (number of bytes)
def estimate(object)
case object
when Hash
estimate_hash(object)
when Array
estimate_array(object)
when String
estimate_string(object)
when Integer
estimate_integer(object)
when Float
estimate_float(object)
when DateTime, Time
estimate_time(object)
when NilClass
NULL_SIZE
else
# might be incorrect, but #to_s is safe, #to_json might be disabled for some objects: User
estimate_string(object.to_s)
end
end
private
def estimate_hash(hash)
size = 0
item_count = 0
hash.each do |key, value|
item_count += 1
size += estimate(key.to_s) + COLON_SIZE + estimate(value)
end
size + OBJECT_BRACKETS_SIZE + comma_count(item_count)
end
def estimate_array(array)
size = 0
item_count = 0
array.each do |item|
item_count += 1
size += estimate(item)
end
size + ARRAY_BRACKETS_SIZE + comma_count(item_count)
end
def estimate_string(string)
string.bytesize + DOUBLEQUOTE_SIZE
end
def estimate_float(float)
float.to_s.bytesize
end
def estimate_integer(integer)
if integer > 0
integer_string_size(integer)
elsif integer < 0
integer_string_size(integer.abs) + MINUS_SIGN_SIZE
else # 0
1
end
end
def estimate_time(time)
time.to_json.size
end
def integer_string_size(integer)
Math.log10(integer).floor + 1
end
def comma_count(item_count)
item_count == 0 ? 0 : item_count - 1
end
end
end
end
end
......@@ -13,7 +13,7 @@ module Gitlab
total_length = 0
limited_array = array.take_while do |arg|
total_length += arg.to_json.length
total_length += JsonSizeEstimator.estimate(arg)
total_length <= MAXIMUM_ARRAY_LENGTH
end
......
......@@ -3,7 +3,7 @@
module QA
context 'Release' do
describe 'Deploy key creation' do
it 'user adds a deploy key' do
it 'user adds a deploy key', quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/208761' do
Flow::Login.sign_in
key = Runtime::Key::RSA.new
......
......@@ -35,7 +35,7 @@ module QA
]
keys.each do |(key_class, bits)|
it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do
it "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines", quarantine: 'https://gitlab.com/gitlab-org/gitlab/issues/208761' do
key = key_class.new(*bits)
Resource::DeployKey.fabricate_via_browser_ui! do |resource|
......
......@@ -198,6 +198,63 @@ describe Projects::ReleasesController do
end
end
context 'GET #downloads' do
subject do
get :downloads, params: {
namespace_id: project.namespace,
project_id: project,
tag: tag,
filepath: filepath
}
end
before do
sign_in(user)
end
let(:release) { create(:release, project: project, tag: tag ) }
let(:tag) { 'v11.9.0-rc2' }
let(:db_filepath) { '/binaries/linux-amd64' }
let!(:link) do
create :release_link,
release: release,
name: 'linux-amd64 binaries',
filepath: db_filepath,
url: 'https://downloads.example.com/bin/gitlab-linux-amd64'
end
context 'valid filepath' do
let(:filepath) { CGI.escape('/binaries/linux-amd64') }
it 'redirects to the asset direct link' do
subject
expect(response).to redirect_to(link.url)
end
end
context 'invalid filepath' do
let(:filepath) { CGI.escape('/binaries/win32') }
it 'is not found' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'ignores filepath extension' do
let(:db_filepath) { '/binaries/linux-amd64.json' }
let(:filepath) { CGI.escape(db_filepath) }
it 'redirects to the asset direct link' do
subject
expect(response).to redirect_to(link.url)
end
end
end
describe 'GET #evidence' do
let_it_be(:tag_name) { "v1.1.0-evidence" }
let!(:release) { create(:release, :with_evidence, project: project, tag: tag_name) }
......
......@@ -6,6 +6,8 @@ FactoryBot.define do
starts_at { 1.day.ago }
ends_at { 1.day.from_now }
broadcast_type { :banner }
trait :expired do
starts_at { 5.days.ago }
ends_at { 3.days.ago }
......@@ -15,5 +17,9 @@ FactoryBot.define do
starts_at { 5.days.from_now }
ends_at { 6.days.from_now }
end
trait :notification do
broadcast_type { :notification }
end
end
end
......@@ -29,6 +29,38 @@ describe API::Helpers::CustomValidators do
end
end
describe API::Helpers::CustomValidators::GitSha do
let(:sha) { RepoHelpers.sample_commit.id }
let(:short_sha) { sha[0, Gitlab::Git::Commit::MIN_SHA_LENGTH] }
let(:too_short_sha) { sha[0, Gitlab::Git::Commit::MIN_SHA_LENGTH - 1] }
subject do
described_class.new(['test'], {}, false, scope.new)
end
context 'valid sha' do
it 'does not raise a validation error' do
expect_no_validation_error('test' => sha)
expect_no_validation_error('test' => short_sha)
end
end
context 'empty params' do
it 'raises a validation error' do
expect_validation_error('test' => nil)
expect_validation_error('test' => '')
end
end
context 'invalid sha' do
it 'raises a validation error' do
expect_validation_error('test' => "#{sha}2") # Sha length > 40
expect_validation_error('test' => 'somestring')
expect_validation_error('test' => too_short_sha) # sha length < MIN_SHA_LENGTH (7)
end
end
end
describe API::Helpers::CustomValidators::FilePath do
subject do
described_class.new(['test'], {}, false, scope.new)
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Lograge::CustomOptions do
describe '.call' do
let(:params) do
{
'controller' => 'ApplicationController',
'action' => 'show',
'format' => 'html',
'a' => 'b'
}
end
let(:event) do
ActiveSupport::Notifications::Event.new(
'test',
1,
2,
'transaction_id',
{ params: params, user_id: 'test' }
)
end
subject { described_class.call(event) }
it 'ignores some parameters' do
param_keys = subject[:params].map { |param| param[:key] }
expect(param_keys).not_to include(*described_class::IGNORE_PARAMS)
end
it 'formats the parameters' do
expect(subject[:params]).to eq([{ key: 'a', value: 'b' }])
end
it 'adds the current time' do
travel_to(5.days.ago) do
expected_time = Time.now.utc.iso8601(3)
expect(subject[:time]).to eq(expected_time)
end
end
it 'adds the user id' do
expect(subject[:user_id]).to eq('test')
end
end
end
......@@ -31,14 +31,7 @@ describe Gitlab::SidekiqQueue do
context 'when the queue is not processed in time' do
before do
calls = 0
allow(sidekiq_queue).to receive(:job_matches?).and_wrap_original do |m, *args|
raise Timeout::Error if calls > 0
calls += 1
m.call(*args)
end
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(1, 2, 12)
end
it 'returns a non-completion flag, the number of jobs deleted, and the remaining queue size' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Utils::JsonSizeEstimator do
RSpec::Matchers.define :match_json_bytesize_of do |expected|
match do |actual|
actual == expected.to_json.bytesize
end
end
def estimate(object)
described_class.estimate(object)
end
[
[],
[[[[]]]],
[1, "str", 3.14, ["str", { a: -1 }]],
{},
{ a: {} },
{ a: { b: { c: [1, 2, 3], e: Time.now, f: nil } } },
{ 100 => 500 },
{ '狸' => '狸' },
nil
].each do |example|
it { expect(estimate(example)).to match_json_bytesize_of(example) }
end
it 'calls #to_s on unknown object' do
klass = Class.new do
def to_s
'hello'
end
end
expect(estimate(klass.new)).to match_json_bytesize_of(klass.new.to_s) # "hello"
end
end
......@@ -2165,14 +2165,20 @@ describe API::Users, :do_not_mock_admin_mode do
end
describe 'POST /users/:id/block' do
let(:blocked_user) { create(:user, state: 'blocked') }
before do
admin
end
it 'blocks existing user' do
post api("/users/#{user.id}/block", admin)
expect(response).to have_gitlab_http_status(:created)
expect(user.reload.state).to eq('blocked')
aggregate_failures do
expect(response).to have_gitlab_http_status(:created)
expect(response.body).to eq('true')
expect(user.reload.state).to eq('blocked')
end
end
it 'does not re-block ldap blocked users' do
......@@ -2192,6 +2198,15 @@ describe API::Users, :do_not_mock_admin_mode do
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 User Not Found')
end
it 'returns a 201 if user is already blocked' do
post api("/users/#{blocked_user.id}/block", admin)
aggregate_failures do
expect(response).to have_gitlab_http_status(:created)
expect(response.body).to eq('null')
end
end
end
describe 'POST /users/:id/unblock' do
......
......@@ -130,14 +130,22 @@ describe PostReceiveService do
end
end
context 'broadcast message exists' do
context 'broadcast message banner exists' do
it 'outputs a broadcast message' do
broadcast_message = create(:broadcast_message, starts_at: 1.day.ago, ends_at: 1.day.from_now)
broadcast_message = create(:broadcast_message)
expect(subject).to include(build_alert_message(broadcast_message.message))
end
end
context 'broadcast message notification exists' do
it 'does not output a broadcast message' do
create(:broadcast_message, :notification)
expect(has_alert_messages?(subject)).to be_falsey
end
end
context 'broadcast message does not exist' do
it 'does not output a broadcast message' do
expect(has_alert_messages?(subject)).to be_falsey
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment