Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
26169f534c
commit
b491556459
|
|
@ -160,7 +160,7 @@ gdk-qa-reliable:
|
|||
QA_RUN_TYPE: gdk-qa-blocking
|
||||
parallel: 10
|
||||
rules:
|
||||
- if: $CI_MERGE_REQUEST_LABELS =~ /devops::govern|devops::create/
|
||||
- if: $CI_MERGE_REQUEST_LABELS =~ /devops::govern|devops::create|devops::verify/
|
||||
- when: on_success
|
||||
allow_failure: true
|
||||
|
||||
|
|
|
|||
|
|
@ -3148,7 +3148,6 @@ RSpec/MissingFeatureCategory:
|
|||
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb'
|
||||
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb'
|
||||
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb'
|
||||
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb'
|
||||
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb'
|
||||
- 'spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb'
|
||||
- 'spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb'
|
||||
|
|
|
|||
|
|
@ -301,6 +301,8 @@ class GraphqlController < ApplicationController
|
|||
end
|
||||
|
||||
def introspection_query_can_use_cache?
|
||||
return false if Gitlab.dev_or_test_env?
|
||||
|
||||
CACHED_INTROSPECTION_QUERY_STRING == graphql_query_object.query_string.squish
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module Analytics
|
|||
module CycleAnalytics
|
||||
class IssueStageEvent < ApplicationRecord
|
||||
include StageEventModel
|
||||
include Awardable
|
||||
extend SuppressCompositePrimaryKeyWarning
|
||||
|
||||
validates(*%i[stage_event_hash_id issue_id group_id project_id start_event_timestamp], presence: true)
|
||||
|
|
@ -11,6 +12,8 @@ module Analytics
|
|||
alias_attribute :state, :state_id
|
||||
enum state: Issue.available_states, _suffix: true
|
||||
|
||||
has_one :epic_issue, primary_key: 'issue_id', foreign_key: 'issue_id' # rubocop: disable Rails/InverseOf
|
||||
|
||||
scope :assigned_to, ->(user) do
|
||||
assignees_class = IssueAssignee
|
||||
condition = assignees_class.where(user_id: user).where(arel_table[:issue_id].eq(assignees_class.arel_table[:issue_id]))
|
||||
|
|
|
|||
|
|
@ -803,7 +803,7 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
|
|||
# Restricting the validation to `on: :update` only to avoid cyclical dependencies with
|
||||
# License <--> ApplicationSetting. This method calls a license check when we create
|
||||
# ApplicationSetting from defaults which in turn depends on ApplicationSetting record.
|
||||
# The currect default is defined in the `defaults` method so we don't need to validate
|
||||
# The correct default is defined in the `defaults` method so we don't need to validate
|
||||
# it here.
|
||||
validates :disable_feed_token,
|
||||
inclusion: { in: [true, false], message: N_('must be a boolean value') }, on: :update
|
||||
|
|
|
|||
|
|
@ -13,26 +13,26 @@ module Awardable
|
|||
end
|
||||
|
||||
class_methods do
|
||||
def awarded(user, name = nil)
|
||||
def awarded(user, name = nil, base_class_name = base_class.name, awardable_id_column = :id)
|
||||
award_emoji_table = Arel::Table.new('award_emoji')
|
||||
inner_query = award_emoji_table
|
||||
.project('true')
|
||||
.where(award_emoji_table[:user_id].eq(user.id))
|
||||
.where(award_emoji_table[:awardable_type].eq(base_class.name))
|
||||
.where(award_emoji_table[:awardable_id].eq(self.arel_table[:id]))
|
||||
.where(award_emoji_table[:awardable_type].eq(base_class_name))
|
||||
.where(award_emoji_table[:awardable_id].eq(self.arel_table[awardable_id_column]))
|
||||
|
||||
inner_query = inner_query.where(award_emoji_table[:name].eq(name)) if name.present?
|
||||
|
||||
where(inner_query.exists)
|
||||
end
|
||||
|
||||
def not_awarded(user, name = nil)
|
||||
def not_awarded(user, name = nil, base_class_name = base_class.name, awardable_id_column = :id)
|
||||
award_emoji_table = Arel::Table.new('award_emoji')
|
||||
inner_query = award_emoji_table
|
||||
.project('true')
|
||||
.where(award_emoji_table[:user_id].eq(user.id))
|
||||
.where(award_emoji_table[:awardable_type].eq(base_class.name))
|
||||
.where(award_emoji_table[:awardable_id].eq(self.arel_table[:id]))
|
||||
.where(award_emoji_table[:awardable_type].eq(base_class_name))
|
||||
.where(award_emoji_table[:awardable_id].eq(self.arel_table[awardable_id_column]))
|
||||
|
||||
inner_query = inner_query.where(award_emoji_table[:name].eq(name)) if name.present?
|
||||
|
||||
|
|
@ -52,14 +52,14 @@ module Awardable
|
|||
end
|
||||
|
||||
# Order votes by emoji, optional sort order param `descending` defaults to true
|
||||
def order_votes(emoji_name, direction)
|
||||
def order_votes(emoji_name, direction, base_class_name = base_class.name, awardable_id_column = :id)
|
||||
awardable_table = self.arel_table
|
||||
awards_table = AwardEmoji.arel_table
|
||||
|
||||
join_clause = awardable_table
|
||||
.join(awards_table, Arel::Nodes::OuterJoin)
|
||||
.on(awards_table[:awardable_id].eq(awardable_table[:id])
|
||||
.and(awards_table[:awardable_type].eq(base_class.name).and(awards_table[:name].eq(emoji_name))))
|
||||
.on(awards_table[:awardable_id].eq(awardable_table[awardable_id_column])
|
||||
.and(awards_table[:awardable_type].eq(base_class_name).and(awards_table[:name].eq(emoji_name))))
|
||||
.join_sources
|
||||
|
||||
joins(join_clause).group(awardable_table[:id]).reorder(
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ Rails.application.configure do
|
|||
# Only use best-standards-support built into browsers
|
||||
config.action_dispatch.best_standards_support = :builtin
|
||||
|
||||
# There is no need to check if assets are precompiled locally
|
||||
# To debug AssetNotPrecompiled errors locally, set CHECK_PRECOMPILED_ASSETS to true
|
||||
config.assets.check_precompiled_asset = Gitlab::Utils.to_boolean(ENV['CHECK_PRECOMPILED_ASSETS'], default: false)
|
||||
|
||||
# Do not compress assets
|
||||
config.assets.compress = false
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: enable_hamilton_in_usage_quotas_ui
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123480
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/411855
|
||||
milestone: '16.1'
|
||||
type: development
|
||||
group: group::purchase
|
||||
default_enabled: false
|
||||
582
db/structure.sql
582
db/structure.sql
File diff suppressed because it is too large
Load Diff
|
|
@ -22,6 +22,10 @@ module Gitlab
|
|||
:page,
|
||||
:stage_id,
|
||||
:end_event_filter,
|
||||
:weight,
|
||||
:epic_id,
|
||||
:my_reaction_emoji,
|
||||
:iteration_id,
|
||||
label_name: [].freeze,
|
||||
assignee_username: [].freeze,
|
||||
project_ids: [].freeze
|
||||
|
|
@ -46,6 +50,10 @@ module Gitlab
|
|||
attribute :page
|
||||
attribute :stage_id
|
||||
attribute :end_event_filter
|
||||
attribute :weight
|
||||
attribute :epic_id
|
||||
attribute :my_reaction_emoji
|
||||
attribute :iteration_id
|
||||
|
||||
FINDER_PARAM_NAMES.each do |param_name|
|
||||
attribute param_name
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ module Gitlab
|
|||
module Pipeline
|
||||
module Expression
|
||||
ExpressionError = Class.new(StandardError)
|
||||
RuntimeError = Class.new(ExpressionError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,23 +5,17 @@ module Gitlab
|
|||
module Pipeline
|
||||
module Expression
|
||||
module Lexeme
|
||||
require_dependency 're2'
|
||||
|
||||
class Pattern < Lexeme::Value
|
||||
PATTERN = %r{^\/([^\/]|\\/)+[^\\]\/[ismU]*}
|
||||
|
||||
def initialize(regexp)
|
||||
super(regexp.gsub(%r{\\/}, '/'))
|
||||
|
||||
unless Gitlab::UntrustedRegexp::RubySyntax.valid?(@value)
|
||||
raise Lexer::SyntaxError, 'Invalid regular expression!'
|
||||
end
|
||||
raise Lexer::SyntaxError, 'Invalid regular expression!' unless cached_regexp.valid?
|
||||
end
|
||||
|
||||
def evaluate(variables = {})
|
||||
Gitlab::UntrustedRegexp::RubySyntax.fabricate!(@value)
|
||||
rescue RegexpError
|
||||
raise Expression::RuntimeError, 'Invalid regular expression!'
|
||||
cached_regexp.expression
|
||||
end
|
||||
|
||||
def inspect
|
||||
|
|
@ -47,6 +41,12 @@ module Gitlab
|
|||
|
||||
new_pattern.evaluate(variables)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cached_regexp
|
||||
@cached_regexp ||= RegularExpression.new(@value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Pipeline
|
||||
module Expression
|
||||
module Lexeme
|
||||
class Pattern
|
||||
require_dependency 're2'
|
||||
class RegularExpression
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
def expression
|
||||
Gitlab::SafeRequestStore.fetch("#{self.class}#unsafe_regexp:#{value}") do
|
||||
Gitlab::UntrustedRegexp::RubySyntax.fabricate!(value)
|
||||
end
|
||||
end
|
||||
strong_memoize_attr :expression
|
||||
|
||||
def valid?
|
||||
!!expression
|
||||
rescue RegexpError
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -471,62 +471,81 @@ RSpec.describe GraphqlController, feature_category: :integrations do
|
|||
context 'when querying an IntrospectionQuery', :use_clean_rails_memory_store_caching do
|
||||
let_it_be(:query) { File.read(Rails.root.join('spec/fixtures/api/graphql/introspection.graphql')) }
|
||||
|
||||
it 'caches IntrospectionQuery even when operationName is not given' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:once)
|
||||
context 'in dev or test env' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:dev_or_test_env?).and_return(true)
|
||||
end
|
||||
|
||||
post :execute, params: { query: query }
|
||||
post :execute, params: { query: query }
|
||||
end
|
||||
|
||||
it 'caches the IntrospectionQuery' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:once)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
end
|
||||
|
||||
it 'caches separately for both remove_deprecated set to true and false' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:twice)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
|
||||
|
||||
# We clear this instance variable to reset remove_deprecated
|
||||
subject.remove_instance_variable(:@context) if subject.instance_variable_defined?(:@context)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
|
||||
end
|
||||
|
||||
it 'has a different cache for each Gitlab.revision' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:twice)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
|
||||
allow(Gitlab).to receive(:revision).and_return('new random value')
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
end
|
||||
|
||||
context 'when there is an unknown introspection query' do
|
||||
let(:query) { File.read(Rails.root.join('spec/fixtures/api/graphql/fake_introspection.graphql')) }
|
||||
|
||||
it 'does not cache an unknown introspection query' do
|
||||
it 'does not cache IntrospectionQuery' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:twice)
|
||||
|
||||
post :execute, params: { query: query }
|
||||
post :execute, params: { query: query }
|
||||
end
|
||||
end
|
||||
|
||||
context 'in env different from dev or test' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
|
||||
end
|
||||
|
||||
it 'caches IntrospectionQuery even when operationName is not given' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:once)
|
||||
|
||||
post :execute, params: { query: query }
|
||||
post :execute, params: { query: query }
|
||||
end
|
||||
|
||||
it 'caches the IntrospectionQuery' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:once)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
end
|
||||
end
|
||||
|
||||
it 'hits the cache even if the whitespace in the query differs' do
|
||||
query_1 = File.read(Rails.root.join('spec/fixtures/api/graphql/introspection.graphql'))
|
||||
query_2 = "#{query_1} " # add a couple of spaces to change the fingerprint
|
||||
it 'caches separately for both remove_deprecated set to true and false' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:twice)
|
||||
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:once)
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: true }
|
||||
|
||||
post :execute, params: { query: query_1, operationName: 'IntrospectionQuery' }
|
||||
post :execute, params: { query: query_2, operationName: 'IntrospectionQuery' }
|
||||
# We clear this instance variable to reset remove_deprecated
|
||||
subject.remove_instance_variable(:@context) if subject.instance_variable_defined?(:@context)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery', remove_deprecated: false }
|
||||
end
|
||||
|
||||
it 'has a different cache for each Gitlab.revision' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:twice)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
|
||||
allow(Gitlab).to receive(:revision).and_return('new random value')
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
end
|
||||
|
||||
context 'when there is an unknown introspection query' do
|
||||
let(:query) { File.read(Rails.root.join('spec/fixtures/api/graphql/fake_introspection.graphql')) }
|
||||
|
||||
it 'does not cache an unknown introspection query' do
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:twice)
|
||||
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
post :execute, params: { query: query, operationName: 'IntrospectionQuery' }
|
||||
end
|
||||
end
|
||||
|
||||
it 'hits the cache even if the whitespace in the query differs' do
|
||||
query_1 = File.read(Rails.root.join('spec/fixtures/api/graphql/introspection.graphql'))
|
||||
query_2 = "#{query_1} " # add a couple of spaces to change the fingerprint
|
||||
|
||||
expect(GitlabSchema).to receive(:execute).exactly(:once)
|
||||
|
||||
post :execute, params: { query: query_1, operationName: 'IntrospectionQuery' }
|
||||
post :execute, params: { query: query_2, operationName: 'IntrospectionQuery' }
|
||||
end
|
||||
end
|
||||
|
||||
it 'fails if the GraphiQL gem version is not 1.8.0' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern::RegularExpression, feature_category: :continuous_integration do
|
||||
describe '#initialize' do
|
||||
it 'initializes the pattern' do
|
||||
pattern = described_class.new('/foo/')
|
||||
|
||||
expect(pattern.value).to eq('/foo/')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
subject { described_class.new(pattern).valid? }
|
||||
|
||||
context 'with valid expressions' do
|
||||
let(:pattern) { '/foo\\/bar/' }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when the value is not a valid regular expression' do
|
||||
let(:pattern) { 'foo' }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expression' do
|
||||
subject { described_class.new(pattern).expression }
|
||||
|
||||
context 'with valid expressions' do
|
||||
let(:pattern) { '/bar/' }
|
||||
|
||||
it { is_expected.to eq Gitlab::UntrustedRegexp.new('bar') }
|
||||
end
|
||||
|
||||
context 'when the value is not a valid regular expression' do
|
||||
let(:pattern) { 'foo' }
|
||||
|
||||
it { expect { subject }.to raise_error(RegexpError) }
|
||||
end
|
||||
|
||||
context 'when the request store is activated', :request_store do
|
||||
let(:pattern) { '/foo\\/bar/' }
|
||||
|
||||
it 'fabricates once' do
|
||||
expect(Gitlab::UntrustedRegexp::RubySyntax).to receive(:fabricate!).once.and_call_original
|
||||
|
||||
2.times do
|
||||
expect(described_class.new(pattern).expression).to be_a(Gitlab::UntrustedRegexp)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do
|
||||
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern, feature_category: :continuous_integration do
|
||||
describe '#initialize' do
|
||||
context 'when the value is a valid regular expression' do
|
||||
it 'initializes the pattern' do
|
||||
|
|
@ -164,14 +164,5 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do
|
|||
|
||||
expect(regexp.evaluate).to eq Gitlab::UntrustedRegexp.new('abc')
|
||||
end
|
||||
|
||||
it 'raises error if evaluated regexp is not valid' do
|
||||
allow(Gitlab::UntrustedRegexp::RubySyntax).to receive(:valid?).and_return(true)
|
||||
|
||||
regexp = described_class.new('/invalid ( .*/')
|
||||
|
||||
expect { regexp.evaluate }
|
||||
.to raise_error(Gitlab::Ci::Pipeline::Expression::RuntimeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2240,7 +2240,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
context 'when the Slack app setting is not enabled' do
|
||||
before do
|
||||
stub_application_setting(slack_app_enabled: false)
|
||||
allow(Rails.env).to receive(:test?).and_return(false, true)
|
||||
allow(Rails.env).to receive(:test?).and_return(false)
|
||||
end
|
||||
|
||||
it 'includes all projects' do
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ RSpec.shared_examples 'unlicensed cycle analytics request params' do
|
|||
}
|
||||
end
|
||||
|
||||
subject { described_class.new(params) }
|
||||
let(:request_params) { described_class.new(params) }
|
||||
|
||||
subject { request_params }
|
||||
|
||||
before do
|
||||
root_group.add_owner(user)
|
||||
|
|
@ -114,13 +116,13 @@ RSpec.shared_examples 'unlicensed cycle analytics request params' do
|
|||
end
|
||||
|
||||
describe 'use_aggregated_data_collector param' do
|
||||
subject(:value) { described_class.new(params).to_data_collector_params[:use_aggregated_data_collector] }
|
||||
subject(:value) { request_params.to_data_collector_params[:use_aggregated_data_collector] }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
|
||||
describe 'feature availablity data attributes' do
|
||||
subject(:value) { described_class.new(params).to_data_attributes }
|
||||
subject(:value) { request_params.to_data_attributes }
|
||||
|
||||
it 'disables all paid features' do
|
||||
is_expected.to match(a_hash_including(enable_tasks_by_type_chart: 'false',
|
||||
|
|
@ -128,4 +130,26 @@ RSpec.shared_examples 'unlicensed cycle analytics request params' do
|
|||
enable_projects_filter: 'false'))
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_data_collector_params' do
|
||||
context 'when adding licensed parameters' do
|
||||
subject(:data_collector_params) { request_params.to_data_collector_params }
|
||||
|
||||
before do
|
||||
params.merge!(
|
||||
weight: 1,
|
||||
epic_id: 2,
|
||||
iteration_id: 3,
|
||||
my_reaction_emoji: 'tumbsup'
|
||||
)
|
||||
end
|
||||
|
||||
it 'excludes the attributes from the data collector params' do
|
||||
expect(data_collector_params).to exclude(:weight)
|
||||
expect(data_collector_params).to exclude(:epic_id)
|
||||
expect(data_collector_params).to exclude(:iteration_id)
|
||||
expect(data_collector_params).to exclude(:my_reaction_emoji)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue