diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-20 13:49:51 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-20 13:49:51 +0000 |
commit | 71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e (patch) | |
tree | 6a2d93ef3fb2d353bb7739e4b57e6541f51cdd71 /app/models/concerns | |
parent | a7253423e3403b8c08f8a161e5937e1488f5f407 (diff) | |
download | gitlab-ce-71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e.tar.gz |
Add latest changes from gitlab-org/gitlab@15-9-stable-eev15.9.0-rc42
Diffstat (limited to 'app/models/concerns')
29 files changed, 354 insertions, 46 deletions
diff --git a/app/models/concerns/analytics/cycle_analytics/stageable.rb b/app/models/concerns/analytics/cycle_analytics/stageable.rb index d1f948d1366..caac4f31e1a 100644 --- a/app/models/concerns/analytics/cycle_analytics/stageable.rb +++ b/app/models/concerns/analytics/cycle_analytics/stageable.rb @@ -4,7 +4,6 @@ module Analytics module CycleAnalytics module Stageable extend ActiveSupport::Concern - include RelativePositioning include Gitlab::Utils::StrongMemoize included do @@ -92,10 +91,6 @@ module Analytics end_event_identifier.to_s.eql?(stage_params[:end_event_identifier].to_s) end - def find_with_same_parent!(id) - parent.cycle_analytics_stages.find(id) - end - private def validate_stage_event_pairs diff --git a/app/models/concerns/ci/has_variable.rb b/app/models/concerns/ci/has_variable.rb index 3b437fbba16..77e7e5035a0 100644 --- a/app/models/concerns/ci/has_variable.rb +++ b/app/models/concerns/ci/has_variable.rb @@ -18,6 +18,7 @@ module Ci scope :by_key, -> (key) { where(key: key) } scope :order_key_asc, -> { reorder(key: :asc) } + scope :order_key_desc, -> { reorder(key: :desc) } attr_encrypted :value, mode: :per_attribute_iv_and_salt, @@ -30,6 +31,16 @@ module Ci end end + class_methods do + def order_by(method) + case method.to_s + when 'key_asc' then order_key_asc + when 'key_desc' then order_key_desc + else all + end + end + end + def to_runner_variable var_cache_key = to_runner_variable_cache_key diff --git a/app/models/concerns/ci/maskable.rb b/app/models/concerns/ci/maskable.rb index 62be0150ee0..e2cef0981d1 100644 --- a/app/models/concerns/ci/maskable.rb +++ b/app/models/concerns/ci/maskable.rb @@ -12,10 +12,28 @@ module Ci # * Characters must be from the Base64 alphabet (RFC4648) with the addition of '@', ':', '.', and '~' # * Absolutely no fun is allowed REGEX = %r{\A[a-zA-Z0-9_+=/@:.~-]{8,}\z}.freeze + # * Single line + # * No spaces + # * Minimal length of 8 characters + # * Some fun is allowed + MASK_AND_RAW_REGEX = %r{\A\S{8,}\z}.freeze included do validates :masked, inclusion: { in: [true, false] } - validates :value, format: { with: REGEX }, if: :masked? + validates :value, format: { with: REGEX }, if: :masked_and_expanded? + validates :value, format: { with: MASK_AND_RAW_REGEX }, if: :masked_and_raw? + end + + def masked_and_raw? + return false unless self.class.method_defined?(:raw) + + masked? && raw? + end + + def masked_and_expanded? + return masked? unless self.class.method_defined?(:raw) + + masked? && !raw? end def to_runner_variable diff --git a/app/models/concerns/ci/metadatable.rb b/app/models/concerns/ci/metadatable.rb index d93f4a150d5..d91f33452a0 100644 --- a/app/models/concerns/ci/metadatable.rb +++ b/app/models/concerns/ci/metadatable.rb @@ -22,7 +22,7 @@ module Ci delegate :set_cancel_gracefully, to: :metadata, prefix: false, allow_nil: false delegate :id_tokens, to: :metadata, allow_nil: true - before_create :ensure_metadata + before_validation :ensure_metadata, on: :create end def has_exposed_artifacts? @@ -34,7 +34,7 @@ module Ci end def ensure_metadata - metadata || build_metadata(project: project, partition_id: partition_id) + metadata || build_metadata(project: project) end def degenerated? diff --git a/app/models/concerns/commit_signature.rb b/app/models/concerns/commit_signature.rb index 7f1fbbefd94..5dac3c7833a 100644 --- a/app/models/concerns/commit_signature.rb +++ b/app/models/concerns/commit_signature.rb @@ -4,6 +4,7 @@ module CommitSignature included do include ShaAttribute + include EachBatch sha_attribute :commit_sha @@ -14,7 +15,8 @@ module CommitSignature other_user: 3, unverified_key: 4, unknown_key: 5, - multiple_signatures: 6 + multiple_signatures: 6, + revoked_key: 7 } belongs_to :project, class_name: 'Project', foreign_key: 'project_id', optional: false diff --git a/app/models/concerns/counter_attribute.rb b/app/models/concerns/counter_attribute.rb index 784afd1f231..58ea57962c5 100644 --- a/app/models/concerns/counter_attribute.rb +++ b/app/models/concerns/counter_attribute.rb @@ -93,7 +93,7 @@ module CounterAttribute run_after_commit_or_now do new_value = counter(attribute).increment(increment) - log_increment_counter(attribute, increment.amount, new_value) + log_increment_counter(attribute, increment, new_value) end end @@ -101,7 +101,7 @@ module CounterAttribute run_after_commit_or_now do new_value = counter(attribute).bulk_increment(increments) - log_increment_counter(attribute, increments.sum(&:amount), new_value) + log_bulk_increment_counter(attribute, increments, new_value) end end @@ -198,7 +198,8 @@ module CounterAttribute message: 'Increment counter attribute', attribute: attribute, project_id: project_id, - increment: increment, + increment: increment.amount, + ref: increment.ref, new_counter_value: new_value, current_db_value: read_attribute(attribute) ) @@ -206,6 +207,16 @@ module CounterAttribute Gitlab::AppLogger.info(payload) end + def log_bulk_increment_counter(attribute, increments, new_value) + if Feature.enabled?(:split_log_bulk_increment_counter, type: :ops) + increments.each do |increment| + log_increment_counter(attribute, increment, new_value) + end + else + log_increment_counter(attribute, Gitlab::Counters::Increment.new(amount: increments.sum(&:amount)), new_value) + end + end + def log_clear_counter(attribute) payload = Gitlab::ApplicationContext.current.merge( message: 'Clear counter attribute', diff --git a/app/models/concerns/cross_database_modification.rb b/app/models/concerns/cross_database_modification.rb index 273d5f35e76..df4f4f0bfe1 100644 --- a/app/models/concerns/cross_database_modification.rb +++ b/app/models/concerns/cross_database_modification.rb @@ -102,6 +102,10 @@ module CrossDatabaseModification :gitlab_main when 'Ci::ApplicationRecord' :gitlab_ci + when 'MainClusterwide::ApplicationRecord' + :gitlab_main_clusterwide + when 'PackageMetadata::ApplicationRecord' + :gitlab_pm else Gitlab::Database::GitlabSchema.table_schema(table_name) if table_name end diff --git a/app/models/concerns/enums/package_metadata.rb b/app/models/concerns/enums/package_metadata.rb new file mode 100644 index 00000000000..e15fe758e69 --- /dev/null +++ b/app/models/concerns/enums/package_metadata.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Enums + class PackageMetadata + PURL_TYPES = { + composer: 1, + conan: 2, + gem: 3, + golang: 4, + maven: 5, + npm: 6, + nuget: 7, + pypi: 8 + }.with_indifferent_access.freeze + + def self.purl_types + PURL_TYPES + end + end +end diff --git a/app/models/concerns/exportable.rb b/app/models/concerns/exportable.rb new file mode 100644 index 00000000000..066a44912be --- /dev/null +++ b/app/models/concerns/exportable.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Exportable + extend ActiveSupport::Concern + + def readable_records(association, current_user: nil) + association_records = try(association) + return unless association_records.present? + + if has_many_association?(association) + DeclarativePolicy.user_scope do + association_records.select { |record| readable_record?(record, current_user) } + end + else + readable_record?(association_records, current_user) ? association_records : nil + end + end + + def exportable_association?(association, current_user: nil) + return false unless respond_to?(association) + return true if has_many_association?(association) + + readable = try(association) + return true if readable.nil? + + readable_record?(readable, current_user) + end + + def restricted_associations(keys) + exportable_restricted_associations & keys + end + + def has_many_association?(association_name) + self.class.reflect_on_association(association_name)&.macro == :has_many + end + + private + + def exportable_restricted_associations + [] + end + + def readable_record?(record, user) + if record.respond_to?(:exportable_record?) + record.exportable_record?(user) + else + record.readable_by?(user) + end + end +end diff --git a/app/models/concerns/group_descendant.rb b/app/models/concerns/group_descendant.rb index b376537a418..224ac8930b5 100644 --- a/app/models/concerns/group_descendant.rb +++ b/app/models/concerns/group_descendant.rb @@ -21,7 +21,7 @@ module GroupDescendant descendants = Array.wrap(descendants).uniq return [] if descendants.empty? - unless descendants.all? { |hierarchy| hierarchy.is_a?(GroupDescendant) } + unless descendants.all?(GroupDescendant) raise ArgumentError, _('element is not a hierarchy') end diff --git a/app/models/concerns/id_in_ordered.rb b/app/models/concerns/id_in_ordered.rb index b89409e6841..39067574520 100644 --- a/app/models/concerns/id_in_ordered.rb +++ b/app/models/concerns/id_in_ordered.rb @@ -5,7 +5,7 @@ module IdInOrdered included do scope :id_in_ordered, -> (ids) do - raise ArgumentError, "ids must be an array of integers" unless ids.is_a?(Enumerable) && ids.all? { |id| id.is_a?(Integer) } + raise ArgumentError, "ids must be an array of integers" unless ids.is_a?(Enumerable) && ids.all?(Integer) # No need to sort if no more than 1 and the sorting code doesn't work # with an empty array diff --git a/app/models/concerns/integrations/has_web_hook.rb b/app/models/concerns/integrations/has_web_hook.rb index e622faf4a51..dcf14a4c7dc 100644 --- a/app/models/concerns/integrations/has_web_hook.rb +++ b/app/models/concerns/integrations/has_web_hook.rb @@ -42,9 +42,9 @@ module Integrations end # Execute the webhook, creating it if necessary. - def execute_web_hook!(*args) + def execute_web_hook!(...) update_web_hook! - service_hook.execute(*args) + service_hook.execute(...) end end end diff --git a/app/models/concerns/issuable_link.rb b/app/models/concerns/issuable_link.rb index c319d685362..7f29083d6c6 100644 --- a/app/models/concerns/issuable_link.rb +++ b/app/models/concerns/issuable_link.rb @@ -20,6 +20,12 @@ module IssuableLink def issuable_type raise NotImplementedError end + + # Used to get the available types for the API + # overriden in EE + def available_link_types + [TYPE_RELATES_TO] + end end included do diff --git a/app/models/concerns/issue_parent.rb b/app/models/concerns/issue_parent.rb new file mode 100644 index 00000000000..c1fcbdcfc12 --- /dev/null +++ b/app/models/concerns/issue_parent.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# == IssuParent +# +# Used as a common ancestor for Group and Project so we can allow a polymorphic +# Types::GlobalIDType[::IssueParent] in the GraphQL API +# +# Used by Project, Group +# +module IssueParent +end diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb index eed396f785b..7addcf9e2ec 100644 --- a/app/models/concerns/noteable.rb +++ b/app/models/concerns/noteable.rb @@ -106,9 +106,9 @@ module Noteable relations << discussion_notes.select( "'notes' AS table_name", - 'discussion_id', 'MIN(id) AS id', - 'MIN(created_at) AS created_at' + 'MIN(created_at) AS created_at', + 'ARRAY_AGG(id) AS ids' ).with_notes_filter(notes_filter) .group(:discussion_id) @@ -116,17 +116,19 @@ module Noteable relations += synthetic_note_ids_relations end - Note.from_union(relations, remove_duplicates: false).fresh + Note.from_union(relations, remove_duplicates: false) + .select(:table_name, :id, :created_at, :ids) + .fresh end def capped_notes_count(max) notes.limit(max).count end - def grouped_diff_discussions(*args) + def grouped_diff_discussions(...) # Doesn't use `discussion_notes`, because this may include commit diff notes # besides MR diff notes, that we do not want to display on the MR Changes tab. - notes.inc_relations_for_view(self).grouped_diff_discussions(*args) + notes.inc_relations_for_view(self).grouped_diff_discussions(...) end # rubocop:disable Gitlab/ModuleWithInstanceVariables @@ -223,15 +225,16 @@ module Noteable # currently multiple models include Noteable concern, but not all of them support # all resource events, so we check if given model supports given resource event. if respond_to?(:resource_label_events) - relations << resource_label_events.select("'resource_label_events'", "'NULL'", :id, :created_at) + relations << resource_label_events.select("'resource_label_events'", 'MIN(id)', :created_at, 'ARRAY_AGG(id)') + .group(:created_at, :user_id) end if respond_to?(:resource_state_events) - relations << resource_state_events.select("'resource_state_events'", "'NULL'", :id, :created_at) + relations << resource_state_events.select("'resource_state_events'", :id, :created_at, 'ARRAY_FILL(id, ARRAY[1])') end if respond_to?(:resource_milestone_events) - relations << resource_milestone_events.select("'resource_milestone_events'", "'NULL'", :id, :created_at) + relations << resource_milestone_events.select("'resource_milestone_events'", :id, :created_at, 'ARRAY_FILL(id, ARRAY[1])') end relations diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb index df297017119..b85ac9ad4a6 100644 --- a/app/models/concerns/prometheus_adapter.rb +++ b/app/models/concerns/prometheus_adapter.rb @@ -49,7 +49,9 @@ module PrometheusAdapter query_class = query_klass_for(query_name) query_args = build_query_args(*args) - with_reactive_cache(query_class.name, *query_args, &query_class.method(:transform_reactive_result)) + with_reactive_cache(query_class.name, *query_args) do |result| + query_class.transform_reactive_result(result) + end end # Cache metrics for specific environment diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb index 9ed2070d11c..aa0fced99c4 100644 --- a/app/models/concerns/reactive_caching.rb +++ b/app/models/concerns/reactive_caching.rb @@ -122,8 +122,8 @@ module ReactiveCaching worker_class.perform_async(self.class, id, *args) end - def keep_alive_reactive_cache!(*args) - Rails.cache.write(alive_reactive_cache_key(*args), true, expires_in: self.class.reactive_cache_lifetime) + def keep_alive_reactive_cache!(...) + Rails.cache.write(alive_reactive_cache_key(...), true, expires_in: self.class.reactive_cache_lifetime) end def full_reactive_cache_key(*qualifiers) @@ -145,8 +145,8 @@ module ReactiveCaching Gitlab::ExclusiveLease.cancel(full_reactive_cache_key(*args), uuid) end - def within_reactive_cache_lifetime?(*args) - Rails.cache.exist?(alive_reactive_cache_key(*args)) + def within_reactive_cache_lifetime?(...) + Rails.cache.exist?(alive_reactive_cache_key(...)) end def enqueuing_update(*args) diff --git a/app/models/concerns/require_email_verification.rb b/app/models/concerns/require_email_verification.rb index cf6a31e6ebd..5ff4f520d24 100644 --- a/app/models/concerns/require_email_verification.rb +++ b/app/models/concerns/require_email_verification.rb @@ -45,8 +45,9 @@ module RequireEmailVerification private def override_devise_lockable? - strong_memoize(:override_devise_lockable) do - Feature.enabled?(:require_email_verification, self) && !two_factor_enabled? - end + Feature.enabled?(:require_email_verification, self) && + !two_factor_enabled? && + Feature.disabled?(:skip_require_email_verification, self, type: :ops) end + strong_memoize_attr :override_devise_lockable? end diff --git a/app/models/concerns/sensitive_serializable_hash.rb b/app/models/concerns/sensitive_serializable_hash.rb index 794748483e4..5a9b75d4db8 100644 --- a/app/models/concerns/sensitive_serializable_hash.rb +++ b/app/models/concerns/sensitive_serializable_hash.rb @@ -24,12 +24,12 @@ module SensitiveSerializableHash options[:except].concat self.class.attributes_exempt_from_serializable_hash - if self.class.respond_to?(:encrypted_attributes) - options[:except].concat self.class.encrypted_attributes.keys + if self.class.respond_to?(:attr_encrypted_attributes) + options[:except].concat self.class.attr_encrypted_attributes.keys # Per https://github.com/attr-encrypted/attr_encrypted/blob/a96693e9a2a25f4f910bf915e29b0f364f277032/lib/attr_encrypted.rb#L413 - options[:except].concat self.class.encrypted_attributes.values.map { |v| v[:attribute] } - options[:except].concat self.class.encrypted_attributes.values.map { |v| "#{v[:attribute]}_iv" } + options[:except].concat self.class.attr_encrypted_attributes.values.map { |v| v[:attribute] } + options[:except].concat self.class.attr_encrypted_attributes.values.map { |v| "#{v[:attribute]}_iv" } end super(options) diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index 701d2fda5c5..35c48c15fb2 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -22,7 +22,7 @@ module ShaAttribute class_methods do def sha_attribute(name) - return if ENV['STATIC_VERIFICATION'] + return if Gitlab::Environment.static_verification? sha_attribute_fields << name @@ -34,7 +34,7 @@ module ShaAttribute end def sha256_attribute(name) - return if ENV['STATIC_VERIFICATION'] + return if Gitlab::Environment.static_verification? sha256_attribute_fields << name diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb index d27b451892a..fba923e843a 100644 --- a/app/models/concerns/spammable.rb +++ b/app/models/concerns/spammable.rb @@ -123,6 +123,6 @@ module Spammable # Override in Spammable if differs def allow_possible_spam? - Feature.enabled?(:allow_possible_spam, project) + Gitlab::CurrentSettings.allow_possible_spam end end diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 05addcf83d2..f9eba4cc2fe 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -24,10 +24,28 @@ module Taskable (\s.+) # followed by whitespace and some text. }x.freeze + # ignore tasks in code or html comment blocks. HTML blocks + # are ok as we allow tasks inside <detail> blocks + REGEX = %r{ + #{::Gitlab::Regex.markdown_code_or_html_comments} + | + (?<task_item> + #{ITEM_PATTERN} + ) + }mx.freeze + def self.get_tasks(content) - content.to_s.scan(ITEM_PATTERN).map do |prefix, checkbox, label| - TaskList::Item.new("#{prefix} #{checkbox}", label.strip) + items = [] + + content.to_s.scan(REGEX) do + next unless $~[:task_item] + + $~[:task_item].scan(ITEM_PATTERN) do |prefix, checkbox, label| + items << TaskList::Item.new("#{prefix.strip} #{checkbox}", label.strip) + end end + + items end def self.get_updated_tasks(old_content:, new_content:) @@ -67,10 +85,10 @@ module Taskable checklist_item_noun = n_('checklist item', 'checklist items', sum.item_count) if short format(s_('Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}'), -checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count) + checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count) else format(s_('Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed'), -checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count) + checklist_item_noun: checklist_item_noun, complete_count: sum.complete_count, total_count: sum.item_count) end end diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index d91ec161b84..cc3e8f174b3 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -86,7 +86,7 @@ module TokenAuthenticatable def token_authenticatable_module @token_authenticatable_module ||= - const_set(:TokenAuthenticatable, Module.new).tap(&method(:include)) + const_set(:TokenAuthenticatable, Module.new).tap { |mod| include mod } end end end diff --git a/app/models/concerns/vulnerability_finding_helpers.rb b/app/models/concerns/vulnerability_finding_helpers.rb index b5d48260072..1e8a290c050 100644 --- a/app/models/concerns/vulnerability_finding_helpers.rb +++ b/app/models/concerns/vulnerability_finding_helpers.rb @@ -49,7 +49,7 @@ module VulnerabilityFindingHelpers finding_data = report_finding.to_hash.except(:compare_key, :identifiers, :location, :scanner, :links, :signatures, :flags, :evidence) - identifiers = report_finding.identifiers.map do |identifier| + identifiers = report_finding.identifiers.uniq(&:fingerprint).map do |identifier| Vulnerabilities::Identifier.new(identifier.to_hash.merge({ project: project })) end signatures = report_finding.signatures.map do |signature| diff --git a/app/models/concerns/web_hooks/auto_disabling.rb b/app/models/concerns/web_hooks/auto_disabling.rb new file mode 100644 index 00000000000..2cc17a6f185 --- /dev/null +++ b/app/models/concerns/web_hooks/auto_disabling.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module WebHooks + module AutoDisabling + extend ActiveSupport::Concern + + included do + # A hook is disabled if: + # + # - we are no longer in the grace-perod (recent_failures > ?) + # - and either: + # - disabled_until is nil (i.e. this was set by WebHook#fail!) + # - or disabled_until is in the future (i.e. this was set by WebHook#backoff!) + scope :disabled, -> do + where('recent_failures > ? AND (disabled_until IS NULL OR disabled_until >= ?)', + WebHook::FAILURE_THRESHOLD, Time.current) + end + + # A hook is executable if: + # + # - we are still in the grace-period (recent_failures <= ?) + # - OR we have exceeded the grace period and neither of the following is true: + # - disabled_until is nil (i.e. this was set by WebHook#fail!) + # - disabled_until is in the future (i.e. this was set by WebHook#backoff!) + scope :executable, -> do + where('recent_failures <= ? OR (recent_failures > ? AND (disabled_until IS NOT NULL) AND (disabled_until < ?))', + WebHook::FAILURE_THRESHOLD, WebHook::FAILURE_THRESHOLD, Time.current) + end + end + + def executable? + !temporarily_disabled? && !permanently_disabled? + end + + def temporarily_disabled? + return false if recent_failures <= WebHook::FAILURE_THRESHOLD + + disabled_until.present? && disabled_until >= Time.current + end + + def permanently_disabled? + return false if disabled_until.present? + + recent_failures > WebHook::FAILURE_THRESHOLD + end + + def disable! + return if permanently_disabled? + + super + end + + def backoff! + return if permanently_disabled? || (backoff_count >= WebHook::MAX_FAILURES && temporarily_disabled?) + + super + end + + def alert_status + if temporarily_disabled? + :temporarily_disabled + elsif permanently_disabled? + :disabled + else + :executable + end + end + end +end diff --git a/app/models/concerns/web_hooks/has_web_hooks.rb b/app/models/concerns/web_hooks/has_web_hooks.rb new file mode 100644 index 00000000000..161ce106b9b --- /dev/null +++ b/app/models/concerns/web_hooks/has_web_hooks.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module WebHooks + module HasWebHooks + extend ActiveSupport::Concern + + WEB_HOOK_CACHE_EXPIRY = 1.hour + + def any_hook_failed? + hooks.disabled.exists? + end + + def web_hook_failure_redis_key + "any_web_hook_failed:#{id}" + end + + def last_failure_redis_key + "web_hooks:last_failure:project-#{id}" + end + + def get_web_hook_failure + Gitlab::Redis::SharedState.with do |redis| + current = redis.get(web_hook_failure_redis_key) + + Gitlab::Utils.to_boolean(current) if current + end + end + + def fetch_web_hook_failure + Gitlab::Redis::SharedState.with do |_redis| + current = get_web_hook_failure + next current unless current.nil? + + cache_web_hook_failure + end + end + + def cache_web_hook_failure(state = any_hook_failed?) + Gitlab::Redis::SharedState.with do |redis| + redis.set(web_hook_failure_redis_key, state.to_s, ex: WEB_HOOK_CACHE_EXPIRY) + + state + end + end + end +end diff --git a/app/models/concerns/web_hooks/unstoppable.rb b/app/models/concerns/web_hooks/unstoppable.rb new file mode 100644 index 00000000000..26284fe3c36 --- /dev/null +++ b/app/models/concerns/web_hooks/unstoppable.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module WebHooks + module Unstoppable + extend ActiveSupport::Concern + + included do + scope :executable, -> { all } + + scope :disabled, -> { none } + end + + def executable? + true + end + + def temporarily_disabled? + false + end + + def permanently_disabled? + false + end + + def alert_status + :executable + end + end +end diff --git a/app/models/concerns/work_item_resource_event.rb b/app/models/concerns/work_item_resource_event.rb index d0323feb029..ddf39787f63 100644 --- a/app/models/concerns/work_item_resource_event.rb +++ b/app/models/concerns/work_item_resource_event.rb @@ -5,6 +5,18 @@ module WorkItemResourceEvent included do belongs_to :work_item, foreign_key: 'issue_id' + + scope :with_work_item, -> { preload(:work_item) } + + # These events are created also on non work items, e.g. MRs, Epic however system notes subscription + # is only implemented on work items, so we do check if this event is linked to an work item. This can be + # expanded to other issuables later on. + after_commit :trigger_note_subscription_create, on: :create, if: -> { work_item.present? } + end + + # System notes are not updated or deleted, so firing just the noteCreated event. + def trigger_note_subscription_create(events: self) + GraphqlTriggers.work_item_note_created(work_item.to_gid, events) end def work_item_synthetic_system_note(events: nil) diff --git a/app/models/concerns/x509_serial_number_attribute.rb b/app/models/concerns/x509_serial_number_attribute.rb index 9dc53859ac0..b65736b7924 100644 --- a/app/models/concerns/x509_serial_number_attribute.rb +++ b/app/models/concerns/x509_serial_number_attribute.rb @@ -5,7 +5,7 @@ module X509SerialNumberAttribute class_methods do def x509_serial_number_attribute(name) - return if ENV['STATIC_VERIFICATION'] + return if Gitlab::Environment.static_verification? validate_binary_column_exists!(name) unless Rails.env.production? |