diff --git a/.rubocop_todo/layout/class_structure.yml b/.rubocop_todo/layout/class_structure.yml index 615306df72dcc5288d11bb600370c1dd95099829..e6054eb5e88e27ff7b3279f0b53d1b3ebbe91835 100644 --- a/.rubocop_todo/layout/class_structure.yml +++ b/.rubocop_todo/layout/class_structure.yml @@ -69,9 +69,6 @@ Layout/ClassStructure: - 'app/models/gpg_key.rb' - 'app/models/hooks/web_hook.rb' - 'app/models/identity.rb' - - 'app/models/integrations/base_ci.rb' - - 'app/models/integrations/base_issue_tracker.rb' - - 'app/models/integrations/base_slash_commands.rb' - 'app/models/integrations/buildkite.rb' - 'app/models/integrations/clickup.rb' - 'app/models/integrations/confluence.rb' diff --git a/.rubocop_todo/layout/line_continuation_spacing.yml b/.rubocop_todo/layout/line_continuation_spacing.yml index ddfa81c64c4265afef2ed75770f69e3e485571b5..0d1c22b5738977ef0953b132b728fead031a2ec4 100644 --- a/.rubocop_todo/layout/line_continuation_spacing.yml +++ b/.rubocop_todo/layout/line_continuation_spacing.yml @@ -9,7 +9,6 @@ Layout/LineContinuationSpacing: - 'app/helpers/tags_helper.rb' - 'app/helpers/tree_helper.rb' - 'app/models/concerns/spammable.rb' - - 'app/models/integrations/base_third_party_wiki.rb' - 'app/models/integrations/teamcity.rb' - 'app/models/work_items/parent_link.rb' - 'app/services/feature_flags/update_service.rb' diff --git a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml index 2eb541cf8c2876148f1aae5bef30cdfcf7986842..ffa710adbb44807307e1499407aac67fd907efc2 100644 --- a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml +++ b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml @@ -15,7 +15,6 @@ Layout/LineEndStringConcatenationIndentation: - 'app/models/concerns/spammable.rb' - 'app/models/concerns/taskable.rb' - 'app/models/integrations/bamboo.rb' - - 'app/models/integrations/base_third_party_wiki.rb' - 'app/models/integrations/diffblue_cover.rb' - 'app/models/integrations/gitlab_slack_application.rb' - 'app/models/integrations/hangouts_chat.rb' diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index bd6600e325efe06b6c0d86d2247d0678cc9f94ab..7bf12778f7ccc83a6eeea9c02596ad0d17ca2143 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -156,7 +156,6 @@ Layout/LineLength: - 'app/models/incident_management/project_incident_management_setting.rb' - 'app/models/instance_configuration.rb' - 'app/models/integrations/asana.rb' - - 'app/models/integrations/base_issue_tracker.rb' - 'app/models/integrations/chat_message/merge_message.rb' - 'app/models/integrations/chat_message/note_message.rb' - 'app/models/integrations/chat_message/pipeline_message.rb' diff --git a/.rubocop_todo/performance/string_identifier_argument.yml b/.rubocop_todo/performance/string_identifier_argument.yml index de4492b95452eb009ade4cb6cbba506bf789a582..c6cf705eb7a9f9247a2af4ce37958522b6c615f0 100644 --- a/.rubocop_todo/performance/string_identifier_argument.yml +++ b/.rubocop_todo/performance/string_identifier_argument.yml @@ -21,7 +21,6 @@ Performance/StringIdentifierArgument: - 'app/models/concerns/sanitizable.rb' - 'app/models/concerns/signature_type.rb' - 'app/models/concerns/token_authenticatable.rb' - - 'app/models/integrations/base_third_party_wiki.rb' - 'app/models/integrations/field.rb' - 'app/models/namespace_statistics.rb' - 'app/models/packages/debian/file_entry.rb' diff --git a/.rubocop_todo/rspec/any_instance_of.yml b/.rubocop_todo/rspec/any_instance_of.yml index ac22761ac473a9fbd5f428b837db3ceddf696214..443788bc1647ade165677cd6bb4c13ea23921d70 100644 --- a/.rubocop_todo/rspec/any_instance_of.yml +++ b/.rubocop_todo/rspec/any_instance_of.yml @@ -289,7 +289,6 @@ RSpec/AnyInstanceOf: - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb' - 'spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb' - 'spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb' - - 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb' - 'spec/support/shared_examples/models/mentionable_shared_examples.rb' - 'spec/support/shared_examples/models/with_uploads_shared_examples.rb' - 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb' diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml index a9c27e1e4b66cb5e907fc558a042a457b98c548c..44852f41735aa475dca1b98cad62ba4013334b6d 100644 --- a/.rubocop_todo/rspec/context_wording.yml +++ b/.rubocop_todo/rspec/context_wording.yml @@ -2718,7 +2718,6 @@ RSpec/ContextWording: - 'spec/support/shared_examples/models/concerns/repository_storage_movable_shared_examples.rb' - 'spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb' - 'spec/support/shared_examples/models/diff_positionable_note_shared_examples.rb' - - 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb' - 'spec/support/shared_examples/models/mentionable_shared_examples.rb' - 'spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb' - 'spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb' diff --git a/.rubocop_todo/rspec/example_without_description.yml b/.rubocop_todo/rspec/example_without_description.yml index 44ae7dc6868cbfef24f063868b9310c6ad7579e7..9223560de1d54b4f3371b825c7424afd1cbf7f2e 100644 --- a/.rubocop_todo/rspec/example_without_description.yml +++ b/.rubocop_todo/rspec/example_without_description.yml @@ -576,7 +576,6 @@ RSpec/ExampleWithoutDescription: - 'spec/support/shared_examples/mailers/notify_shared_examples.rb' - 'spec/support/shared_examples/models/concerns/linkable_items_shared_examples.rb' - 'spec/support/shared_examples/models/concerns/protected_ref_access_shared_examples.rb' - - 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb' - 'spec/support/shared_examples/models/issuable_link_shared_examples.rb' - 'spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb' - 'spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb' diff --git a/.rubocop_todo/style/guard_clause.yml b/.rubocop_todo/style/guard_clause.yml index 45626acaa05d101bb7e0894bb0b435eed4713735..42e551789da4af6abd27eb17b8e534db18e8ddd3 100644 --- a/.rubocop_todo/style/guard_clause.yml +++ b/.rubocop_todo/style/guard_clause.yml @@ -81,8 +81,6 @@ Style/GuardClause: - 'app/models/error_tracking/project_error_tracking_setting.rb' - 'app/models/generic_commit_status.rb' - 'app/models/grafana_integration.rb' - - 'app/models/integrations/base_issue_tracker.rb' - - 'app/models/integrations/base_third_party_wiki.rb' - 'app/models/integrations/confluence.rb' - 'app/models/integrations/datadog.rb' - 'app/models/integrations/emails_on_push.rb' diff --git a/.rubocop_todo/style/inline_disable_annotation.yml b/.rubocop_todo/style/inline_disable_annotation.yml index 75d609eee41f49970e3dfc023edd45e031e9e9ac..c1ecdb4fd801f984429d603164c8ca120c820bab 100644 --- a/.rubocop_todo/style/inline_disable_annotation.yml +++ b/.rubocop_todo/style/inline_disable_annotation.yml @@ -403,8 +403,6 @@ Style/InlineDisableAnnotation: - 'app/models/group_deploy_key.rb' - 'app/models/hooks/web_hook.rb' - 'app/models/hooks/web_hook_log.rb' - - 'app/models/integrations/base_slash_commands.rb' - - 'app/models/integrations/base_third_party_wiki.rb' - 'app/models/integrations/google_play.rb' - 'app/models/issue.rb' - 'app/models/key.rb' diff --git a/.rubocop_todo/style/redundant_self.yml b/.rubocop_todo/style/redundant_self.yml index 97ed6716fcfcaae1f54c52b18bb5c46e047eec33..c38a0e7b103b29866045d4ab1f3d8dc90b10b378 100644 --- a/.rubocop_todo/style/redundant_self.yml +++ b/.rubocop_todo/style/redundant_self.yml @@ -75,9 +75,6 @@ Style/RedundantSelf: - 'app/models/group_group_link.rb' - 'app/models/hooks/web_hook_log.rb' - 'app/models/identity.rb' - - 'app/models/integrations/base_ci.rb' - - 'app/models/integrations/base_issue_tracker.rb' - - 'app/models/integrations/base_slash_commands.rb' - 'app/models/integrations/emails_on_push.rb' - 'app/models/integrations/pipelines_email.rb' - 'app/models/integrations/zentao.rb' diff --git a/app/controllers/projects/integrations/slash_commands_controller.rb b/app/controllers/projects/integrations/slash_commands_controller.rb index 891a7c1a7497bd53760e04ee27cb7fa12e5541b8..bd3682141509c1028b551d97850b130cfd0a058e 100644 --- a/app/controllers/projects/integrations/slash_commands_controller.rb +++ b/app/controllers/projects/integrations/slash_commands_controller.rb @@ -41,7 +41,10 @@ def cached_params end def cache_key - @cache_key ||= Kernel.format(::Integrations::BaseSlashCommands::CACHE_KEY, secret: request_params[:command_id]) + @cache_key ||= Kernel.format( + ::Integrations::Base::SlashCommands::CACHE_KEY, + secret: request_params[:command_id] + ) end def integration diff --git a/app/models/concerns/integrations/base/ci.rb b/app/models/concerns/integrations/base/ci.rb new file mode 100644 index 0000000000000000000000000000000000000000..0abd280d559a417fa84d49d71d0a21b126a666ef --- /dev/null +++ b/app/models/concerns/integrations/base/ci.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Base module for CI integrations +# List methods you need to implement to get your CI integration +# working with GitLab merge requests +module Integrations + module Base + module Ci + extend ActiveSupport::Concern + + class_methods do + def supported_events + %w[push] + end + end + + included do + attribute :category, default: 'ci' + end + + def valid_token?(token) + respond_to?(:token) && + self.token.present? && + ActiveSupport::SecurityUtils.secure_compare(token, self.token) + end + + # Return complete url to build page + # + # Ex. + # http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c + # + def build_page(sha, ref) + # override this method in the including class + end + + # Return string with build status or :error symbol + # + # Allowed states: 'success', 'failed', 'running', 'pending', 'skipped' + # + # + # Ex. + # @integration.commit_status('13be4ac', 'master') + # # => 'success' + # + # @integration.commit_status('2abe4ac', 'dev') + # # => 'running' + # + # + def commit_status(sha, ref) + # override this method in the including class + end + end + end +end diff --git a/app/models/concerns/integrations/base/integration.rb b/app/models/concerns/integrations/base/integration.rb index dd6fd217ef3bc1cbd6bdb83bc2db838ce9282e7f..2fb3a3757b0452dd4324b27339a4f6fb54a062b6 100644 --- a/app/models/concerns/integrations/base/integration.rb +++ b/app/models/concerns/integrations/base/integration.rb @@ -38,13 +38,7 @@ module Integration ].freeze # Base classes which aren't actual integrations. - BASE_CLASSES = %w[ - Integrations::BaseCi - Integrations::BaseIssueTracker - Integrations::BaseMonitoring - Integrations::BaseSlashCommands - Integrations::BaseThirdPartyWiki - ].freeze + BASE_CLASSES = %w[].freeze BASE_ATTRIBUTES = %w[id instance project_id group_id created_at updated_at encrypted_properties encrypted_properties_iv properties].freeze diff --git a/app/models/concerns/integrations/base/issue_tracker.rb b/app/models/concerns/integrations/base/issue_tracker.rb new file mode 100644 index 0000000000000000000000000000000000000000..d09acef98b9bcd3811d9726e40a1ddf7f706d294 --- /dev/null +++ b/app/models/concerns/integrations/base/issue_tracker.rb @@ -0,0 +1,171 @@ +# frozen_string_literal: true + +module Integrations + module Base + module IssueTracker + extend ActiveSupport::Concern + + REFERENCE_PATTERN_LONG_REGEXP = /(\b[A-Z][A-Z0-9_]*-)#{Gitlab::Regex.issue}/ + REFERENCE_PATTERN_REGEXP = /(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})#{Gitlab::Regex.issue}/ + + included do + validate :one_issue_tracker, if: :activated?, on: :manual_change + + attribute :category, default: 'issue_tracker' + + before_validation :handle_properties + before_validation :set_default_data, on: :create + end + + class_methods do + def supported_events + %w[push] + end + + # Pattern used to extract links from comments + # Override this method on services that uses different patterns + # This pattern does not support cross-project references + # The other code assumes that this pattern is a superset of all + # overridden patterns. See ReferenceRegexes.external_pattern + def base_reference_pattern(only_long: false) + return REFERENCE_PATTERN_LONG_REGEXP if only_long + + REFERENCE_PATTERN_REGEXP + end + end + + def reference_pattern(only_long: false) + self.class.base_reference_pattern(only_long: only_long) + end + + def handle_properties + # this has been moved from initialize_properties and should be improved + # as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 + return unless properties.present? + + safe_keys = data_fields.attributes.keys.grep_v(/encrypted/) - %w[id service_id created_at] + + @legacy_properties_data = properties.dup # rubocop:disable Gitlab/ModuleWithInstanceVariables -- Legacy use + + data_values = properties.slice(*safe_keys) + data_values.reject! { |key| data_fields.changed.include?(key) } + + data_fields.assign_attributes(data_values) if data_values.present? + + self.properties = {} + end + + def legacy_properties_data + @legacy_properties_data ||= {} + end + + def supports_data_fields? + true + end + + def data_fields + issue_tracker_data || build_issue_tracker_data + end + + def default? + default + end + + def issue_url(iid) + issues_url.gsub(':id', iid.to_s) + end + + def issue_tracker_path + project_url + end + + def new_issue_path + new_issue_url + end + + def issue_path(iid) + issue_url(iid) + end + + # Initialize with default properties values + def set_default_data + return unless issues_tracker.present? + + # we don't want to override if we have set something + return if project_url || issues_url || new_issue_url + + data_fields.project_url = issues_tracker['project_url'] + data_fields.issues_url = issues_tracker['issues_url'] + data_fields.new_issue_url = issues_tracker['new_issue_url'] + end + + def execute(data) + return unless supported_events.include?(data[:object_kind]) + + message = "#{type} was unable to reach #{project_url}. Check the url and try again." + result = false + + begin + response = Gitlab::HTTP.head(project_url, verify: true) + + if response + message = "#{type} received response #{response.code} when attempting to connect to #{project_url}" + result = true + end + rescue Gitlab::HTTP::Error, + Timeout::Error, + SocketError, + Errno::ECONNRESET, + Errno::ECONNREFUSED, + OpenSSL::SSL::SSLError => e + message = "#{type} had an error when trying to connect to #{project_url}: #{e.message}" + end + log_info(message) + result + end + + def support_close_issue? + false + end + + def support_cross_reference? + false + end + + def create_cross_reference_note(external_issue, mentioned_in, author) + # override this method in the including class + end + + def activate_disabled_reason + { trackers: other_external_issue_trackers } if other_external_issue_trackers.any? + end + + private + + def other_external_issue_trackers + return [] unless project_level? + + @other_external_issue_trackers ||= project.integrations.external_issue_trackers.where.not(id: id) # rubocop:disable Gitlab/ModuleWithInstanceVariables -- Legacy use + end + + def enabled_in_gitlab_config + Gitlab.config.issues_tracker && + Gitlab.config.issues_tracker.values.any? && + issues_tracker + end + + def issues_tracker + Gitlab.config.issues_tracker[to_param] + end + + def one_issue_tracker + return if instance? + return if project.blank? + return unless other_external_issue_trackers.any? + + errors.add(:base, _('Another issue tracker is already in use. ' \ + 'Only one issue tracker service can be active at a time')) + end + end + end +end diff --git a/app/models/concerns/integrations/base/monitoring.rb b/app/models/concerns/integrations/base/monitoring.rb new file mode 100644 index 0000000000000000000000000000000000000000..7d2fea6be01ff453030cd21f84d2b4660d572a3f --- /dev/null +++ b/app/models/concerns/integrations/base/monitoring.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +# Base module for monitoring services +# +# These services integrate with a deployment solution like Prometheus +# to provide additional features for environments. +module Integrations + module Base + module Monitoring + extend ActiveSupport::Concern + + class_methods do + def supported_events + %w[] + end + end + + included do + attribute :category, default: 'monitoring' + end + + def can_query? + raise NotImplementedError + end + + def query(_, *_) + raise NotImplementedError + end + end + end +end diff --git a/app/models/concerns/integrations/base/slash_commands.rb b/app/models/concerns/integrations/base/slash_commands.rb new file mode 100644 index 0000000000000000000000000000000000000000..7be60585483b5d8db2d4883d367646bd07ba06ed --- /dev/null +++ b/app/models/concerns/integrations/base/slash_commands.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# Base module for ChatOps integrations +module Integrations + module Base + module SlashCommands + extend ActiveSupport::Concern + + CACHE_KEY = "slash-command-requests:%{secret}" + CACHE_EXPIRATION_TIME = 3.minutes + + class_methods do + def supported_events + %w[] + end + end + + included do + attribute :category, default: 'chat' + end + + def trigger(params) + return unless valid_token?(params[:token]) + + chat_user = find_chat_user(params) + user = chat_user&.user + + return unknown_user_message(params) unless user + + unless user.can?(:use_slash_commands) + return Gitlab::SlashCommands::Presenters::Access.new.deactivated if user.deactivated? + + return Gitlab::SlashCommands::Presenters::Access.new.access_denied(project) + end + + if Gitlab::SlashCommands::VerifyRequest.new(self, chat_user).valid? + Gitlab::SlashCommands::Command.new(project, chat_user, params).execute + else + command_id = cache_slash_commands_request!(params) + Gitlab::SlashCommands::Presenters::Access.new.confirm(confirmation_url(command_id, params)) + end + end + + def valid_token?(token) + respond_to?(:token) && + self.token.present? && + ActiveSupport::SecurityUtils.secure_compare(token, self.token) + end + + def testable? + false + end + + private + + def find_chat_user(params) + ChatNames::FindUserService.new(params[:team_id], params[:user_id]).execute # rubocop: disable CodeReuse/ServiceClass -- Legacy use + end + + def authorize_chat_name_url(params) + ChatNames::AuthorizeUserService.new(params).execute # rubocop: disable CodeReuse/ServiceClass -- Legacy use + end + + def unknown_user_message(params) + url = authorize_chat_name_url(params) + Gitlab::SlashCommands::Presenters::Access.new(url).authorize + end + + def cache_slash_commands_request!(params) + secret = SecureRandom.uuid + Kernel.format(CACHE_KEY, secret: secret).tap do |cache_key| + Rails.cache.write(cache_key, params, expires_in: CACHE_EXPIRATION_TIME) + end + + secret + end + end + end +end diff --git a/app/models/concerns/integrations/base/third_party_wiki.rb b/app/models/concerns/integrations/base/third_party_wiki.rb new file mode 100644 index 0000000000000000000000000000000000000000..1265be94c1ec85f92cacbb1a114fb1d1d1780d37 --- /dev/null +++ b/app/models/concerns/integrations/base/third_party_wiki.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Integrations + module Base + module ThirdPartyWiki + extend ActiveSupport::Concern + include SafeFormatHelper + + class_methods do + def supported_events + %w[] + end + end + + included do + attribute :category, default: 'third_party_wiki' + + validate :only_one_third_party_wiki, if: :activated?, on: :manual_change + + after_commit :cache_project_has_integration + end + + private + + def only_one_third_party_wiki + return unless project_level? + + return if project.integrations.third_party_wikis.id_not_in(id).empty? + + errors.add(:base, _('Another third-party wiki is already in use. ' \ + 'Only one third-party wiki integration can be active at a time')) + end + + def cache_project_has_integration + return unless project && !project.destroyed? + + project_setting = project.project_setting + + project_setting.public_send(:"#{project_settings_cache_key}=", active?) # rubocop:disable GitlabSecurity/PublicSend -- Legacy use + project_setting.save! + end + + def project_settings_cache_key + "has_#{self.class.to_param}" + end + end + end +end diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb index b5634ba3b6d07bddaa31403e9df9f5bcceb451f0..755060e0ff6c4bc9a3fad7d27cb03b7d1f87de60 100644 --- a/app/models/concerns/mentionable/reference_regexes.rb +++ b/app/models/concerns/mentionable/reference_regexes.rb @@ -27,7 +27,7 @@ def self.default_pattern def self.external_pattern strong_memoize(:external_pattern) do - issue_pattern = Integrations::BaseIssueTracker.base_reference_pattern + issue_pattern = Integrations::Base::IssueTracker::REFERENCE_PATTERN_REGEXP link_patterns = URI::DEFAULT_PARSER.make_regexp(%w[http https]) reference_pattern(link_patterns, issue_pattern) end diff --git a/app/models/integrations/bamboo.rb b/app/models/integrations/bamboo.rb index f84a5682cd68bfb6e4d1be62d0a4d276cf67920d..2ad573ad3bc66e367fbb7866f878e1ece134e17c 100644 --- a/app/models/integrations/bamboo.rb +++ b/app/models/integrations/bamboo.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Bamboo < BaseCi + class Bamboo < Integration + include Base::Ci include ReactivelyCached prepend EnableSslVerification diff --git a/app/models/integrations/base_ci.rb b/app/models/integrations/base_ci.rb deleted file mode 100644 index db29f228e600738cba909ba29018483f4b798248..0000000000000000000000000000000000000000 --- a/app/models/integrations/base_ci.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -# Base class for CI integrations -# List methods you need to implement to get your CI integration -# working with GitLab merge requests -module Integrations - class BaseCi < Integration - attribute :category, default: 'ci' - - def valid_token?(token) - self.respond_to?(:token) && self.token.present? && ActiveSupport::SecurityUtils.secure_compare(token, self.token) - end - - def self.supported_events - %w[push] - end - - # Return complete url to build page - # - # Ex. - # http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c - # - def build_page(sha, ref) - # implement inside child - end - - # Return string with build status or :error symbol - # - # Allowed states: 'success', 'failed', 'running', 'pending', 'skipped' - # - # - # Ex. - # @integration.commit_status('13be4ac', 'master') - # # => 'success' - # - # @integration.commit_status('2abe4ac', 'dev') - # # => 'running' - # - # - def commit_status(sha, ref) - # implement inside child - end - end -end diff --git a/app/models/integrations/base_issue_tracker.rb b/app/models/integrations/base_issue_tracker.rb deleted file mode 100644 index b59aee6743df2725558d477c1fcc9ba890202cb1..0000000000000000000000000000000000000000 --- a/app/models/integrations/base_issue_tracker.rb +++ /dev/null @@ -1,157 +0,0 @@ -# frozen_string_literal: true - -module Integrations - class BaseIssueTracker < Integration - validate :one_issue_tracker, if: :activated?, on: :manual_change - - attribute :category, default: 'issue_tracker' - - before_validation :handle_properties - before_validation :set_default_data, on: :create - - # Pattern used to extract links from comments - # Override this method on services that uses different patterns - # This pattern does not support cross-project references - # The other code assumes that this pattern is a superset of all - # overridden patterns. See ReferenceRegexes.external_pattern - def self.base_reference_pattern(only_long: false) - if only_long - /(\b[A-Z][A-Z0-9_]*-)#{Gitlab::Regex.issue}/ - else - /(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})#{Gitlab::Regex.issue}/ - end - end - - def reference_pattern(only_long: false) - self.class.base_reference_pattern(only_long: only_long) - end - - def handle_properties - # this has been moved from initialize_properties and should be improved - # as part of https://gitlab.com/gitlab-org/gitlab/issues/29404 - return unless properties.present? - - safe_keys = data_fields.attributes.keys.grep_v(/encrypted/) - %w[id service_id created_at] - - @legacy_properties_data = properties.dup - - data_values = properties.slice(*safe_keys) - data_values.reject! { |key| data_fields.changed.include?(key) } - - data_fields.assign_attributes(data_values) if data_values.present? - - self.properties = {} - end - - def legacy_properties_data - @legacy_properties_data ||= {} - end - - def supports_data_fields? - true - end - - def data_fields - issue_tracker_data || self.build_issue_tracker_data - end - - def default? - default - end - - def issue_url(iid) - issues_url.gsub(':id', iid.to_s) - end - - def issue_tracker_path - project_url - end - - def new_issue_path - new_issue_url - end - - def issue_path(iid) - issue_url(iid) - end - - # Initialize with default properties values - def set_default_data - return unless issues_tracker.present? - - # we don't want to override if we have set something - return if project_url || issues_url || new_issue_url - - data_fields.project_url = issues_tracker['project_url'] - data_fields.issues_url = issues_tracker['issues_url'] - data_fields.new_issue_url = issues_tracker['new_issue_url'] - end - - def self.supported_events - %w[push] - end - - def execute(data) - return unless supported_events.include?(data[:object_kind]) - - message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again." - result = false - - begin - response = Gitlab::HTTP.head(self.project_url, verify: true) - - if response - message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}" - result = true - end - rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => e - message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{e.message}" - end - log_info(message) - result - end - - def support_close_issue? - false - end - - def support_cross_reference? - false - end - - def create_cross_reference_note(external_issue, mentioned_in, author) - # implement inside child - end - - def activate_disabled_reason - { trackers: other_external_issue_trackers } if other_external_issue_trackers.any? - end - - private - - def other_external_issue_trackers - return [] unless project_level? - - @other_external_issue_trackers ||= project.integrations.external_issue_trackers.where.not(id: id) - end - - def enabled_in_gitlab_config - Gitlab.config.issues_tracker && - Gitlab.config.issues_tracker.values.any? && - issues_tracker - end - - def issues_tracker - Gitlab.config.issues_tracker[to_param] - end - - def one_issue_tracker - return if instance? - return if project.blank? - - if other_external_issue_trackers.any? - errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time')) - end - end - end -end diff --git a/app/models/integrations/base_monitoring.rb b/app/models/integrations/base_monitoring.rb deleted file mode 100644 index 12ea57f59a3d8c9c4aa2a855cc78eb902134e19b..0000000000000000000000000000000000000000 --- a/app/models/integrations/base_monitoring.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -# Base class for monitoring services -# -# These services integrate with a deployment solution like Prometheus -# to provide additional features for environments. -module Integrations - class BaseMonitoring < Integration - attribute :category, default: 'monitoring' - - def self.supported_events - %w[] - end - - def can_query? - raise NotImplementedError - end - - def query(_, *_) - raise NotImplementedError - end - end -end diff --git a/app/models/integrations/base_slash_commands.rb b/app/models/integrations/base_slash_commands.rb deleted file mode 100644 index f477263303f8b19db1fb5ae269d4b64976ce9306..0000000000000000000000000000000000000000 --- a/app/models/integrations/base_slash_commands.rb +++ /dev/null @@ -1,72 +0,0 @@ -# frozen_string_literal: true - -# Base class for ChatOps integrations -# This class is not meant to be used directly, but only to inherrit from. -module Integrations - class BaseSlashCommands < Integration - CACHE_KEY = "slash-command-requests:%{secret}" - CACHE_EXPIRATION_TIME = 3.minutes - - attribute :category, default: 'chat' - - def valid_token?(token) - self.respond_to?(:token) && - self.token.present? && - ActiveSupport::SecurityUtils.secure_compare(token, self.token) - end - - def self.supported_events - %w[] - end - - def testable? - false - end - - def trigger(params) - return unless valid_token?(params[:token]) - - chat_user = find_chat_user(params) - user = chat_user&.user - - return unknown_user_message(params) unless user - - unless user.can?(:use_slash_commands) - return Gitlab::SlashCommands::Presenters::Access.new.deactivated if user.deactivated? - - return Gitlab::SlashCommands::Presenters::Access.new.access_denied(project) - end - - if Gitlab::SlashCommands::VerifyRequest.new(self, chat_user).valid? - Gitlab::SlashCommands::Command.new(project, chat_user, params).execute - else - command_id = cache_slash_commands_request!(params) - Gitlab::SlashCommands::Presenters::Access.new.confirm(confirmation_url(command_id, params)) - end - end - - private - - def find_chat_user(params) - ChatNames::FindUserService.new(params[:team_id], params[:user_id]).execute # rubocop: disable CodeReuse/ServiceClass - end - - def authorize_chat_name_url(params) - ChatNames::AuthorizeUserService.new(params).execute # rubocop: disable CodeReuse/ServiceClass - end - - def unknown_user_message(params) - url = authorize_chat_name_url(params) - Gitlab::SlashCommands::Presenters::Access.new(url).authorize - end - - def cache_slash_commands_request!(params) - secret = SecureRandom.uuid - Kernel.format(CACHE_KEY, secret: secret).tap do |cache_key| - Rails.cache.write(cache_key, params, expires_in: CACHE_EXPIRATION_TIME) - end - - secret - end - end -end diff --git a/app/models/integrations/base_third_party_wiki.rb b/app/models/integrations/base_third_party_wiki.rb deleted file mode 100644 index 27e324fb3012673aed6ed754885fbc9c06352a1e..0000000000000000000000000000000000000000 --- a/app/models/integrations/base_third_party_wiki.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module Integrations - class BaseThirdPartyWiki < Integration - include SafeFormatHelper - - attribute :category, default: 'third_party_wiki' - - validate :only_one_third_party_wiki, if: :activated?, on: :manual_change - - after_commit :cache_project_has_integration - - def self.supported_events - %w[] - end - - private - - def only_one_third_party_wiki - return unless project_level? - - if project.integrations.third_party_wikis.id_not_in(id).any? - errors.add(:base, _('Another third-party wiki is already in use. '\ - 'Only one third-party wiki integration can be active at a time')) - end - end - - def cache_project_has_integration - return unless project && !project.destroyed? - - project_setting = project.project_setting - - project_setting.public_send("#{project_settings_cache_key}=", active?) # rubocop:disable GitlabSecurity/PublicSend - project_setting.save! - end - - def project_settings_cache_key - "has_#{self.class.to_param}" - end - end -end diff --git a/app/models/integrations/bugzilla.rb b/app/models/integrations/bugzilla.rb index d5083fe7c8528b303e47fbd37bbcf4757b9631f8..fd3a4e8f2b80989db4418333b0a03d5b8c45f1d1 100644 --- a/app/models/integrations/bugzilla.rb +++ b/app/models/integrations/bugzilla.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Bugzilla < BaseIssueTracker + class Bugzilla < Integration + include Base::IssueTracker include Integrations::HasIssueTrackerFields include HasAvatar diff --git a/app/models/integrations/buildkite.rb b/app/models/integrations/buildkite.rb index be0a0785bb0808ead1afd5b2a89bc6c3c6cb5d7a..a573422a5a2eaaea25a16ffb5eb53f5220f2b2f6 100644 --- a/app/models/integrations/buildkite.rb +++ b/app/models/integrations/buildkite.rb @@ -3,7 +3,8 @@ require "addressable/uri" module Integrations - class Buildkite < BaseCi + class Buildkite < Integration + include Base::Ci include HasWebHook include ReactivelyCached include HasAvatar diff --git a/app/models/integrations/clickup.rb b/app/models/integrations/clickup.rb index 6f88c8f95a8eca6bd79744503e7c8ac9ac35f998..daf3147180f828e69327786e78de945a24c3d28e 100644 --- a/app/models/integrations/clickup.rb +++ b/app/models/integrations/clickup.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Clickup < BaseIssueTracker + class Clickup < Integration + include Base::IssueTracker include HasIssueTrackerFields include HasAvatar diff --git a/app/models/integrations/confluence.rb b/app/models/integrations/confluence.rb index ae5596c89c730349e4d15183fe0b1cb96f262247..24656a0d7678677697a2e53a0eda795c1408fa1b 100644 --- a/app/models/integrations/confluence.rb +++ b/app/models/integrations/confluence.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true module Integrations - class Confluence < BaseThirdPartyWiki + class Confluence < Integration + include Base::ThirdPartyWiki + VALID_SCHEME_MATCH = %r{\Ahttps?\Z} VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z} VALID_PATH_MATCH = %r{\A/wiki(/|\Z)} diff --git a/app/models/integrations/custom_issue_tracker.rb b/app/models/integrations/custom_issue_tracker.rb index 7ce4eb08496d945271a2473c5d926baae67fbfb1..a489c15b98ac2090879ddf1bf83796cf1f5019cd 100644 --- a/app/models/integrations/custom_issue_tracker.rb +++ b/app/models/integrations/custom_issue_tracker.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class CustomIssueTracker < BaseIssueTracker + class CustomIssueTracker < Integration + include Base::IssueTracker include HasIssueTrackerFields validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? diff --git a/app/models/integrations/drone_ci.rb b/app/models/integrations/drone_ci.rb index a5be640cfcebebfdb9c2a7d3da7ef26aa72f8382..d7e7356bb5526614c8f713dda59a31b07888ea28 100644 --- a/app/models/integrations/drone_ci.rb +++ b/app/models/integrations/drone_ci.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class DroneCi < BaseCi + class DroneCi < Integration + include Base::Ci include HasWebHook include HasAvatar include PushDataValidations diff --git a/app/models/integrations/ewm.rb b/app/models/integrations/ewm.rb index 085b82d4c1003c330bacb32c060c351349b78879..736fd140b085c31192dd99ed3238c58f200ddd55 100644 --- a/app/models/integrations/ewm.rb +++ b/app/models/integrations/ewm.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Ewm < BaseIssueTracker + class Ewm < Integration + include Base::IssueTracker include HasIssueTrackerFields validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? diff --git a/app/models/integrations/jenkins.rb b/app/models/integrations/jenkins.rb index b7981f0be28edeeb5ec65d73dc9eb393a5a84bf9..032ed7fce3cf3595c7fc1b501df9c705a0c5d2ba 100644 --- a/app/models/integrations/jenkins.rb +++ b/app/models/integrations/jenkins.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Jenkins < BaseCi + class Jenkins < Integration + include Base::Ci include HasWebHook prepend EnableSslVerification diff --git a/app/models/integrations/jira.rb b/app/models/integrations/jira.rb index b4c7c99ad8fdd5cfdc8eac710637c5a30dcb35a1..8ed2dee95cf09a8c4214375afd862e1d26be336f 100644 --- a/app/models/integrations/jira.rb +++ b/app/models/integrations/jira.rb @@ -2,7 +2,8 @@ # Accessible as Project#external_issue_tracker module Integrations - class Jira < BaseIssueTracker + class Jira < Integration + include Base::IssueTracker include Gitlab::Routing include ApplicationHelper include SafeFormatHelper diff --git a/app/models/integrations/mattermost_slash_commands.rb b/app/models/integrations/mattermost_slash_commands.rb index b43c779244531bfc41222839ae223b222c11a7fa..6292aed0901c051f8a5246df5b7e678aaaccf349 100644 --- a/app/models/integrations/mattermost_slash_commands.rb +++ b/app/models/integrations/mattermost_slash_commands.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class MattermostSlashCommands < BaseSlashCommands + class MattermostSlashCommands < Integration + include Base::SlashCommands include Ci::TriggersHelper MATTERMOST_URL = '%{ORIGIN}/%{TEAM}/channels/%{CHANNEL}' diff --git a/app/models/integrations/mock_ci.rb b/app/models/integrations/mock_ci.rb index 8b9f2f0dd5dd1446303bf5a0e35b08d05126376c..53780eedc2fb151771af38734f46163084670822 100644 --- a/app/models/integrations/mock_ci.rb +++ b/app/models/integrations/mock_ci.rb @@ -2,7 +2,8 @@ # For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service module Integrations - class MockCi < BaseCi + class MockCi < Integration + include Base::Ci prepend EnableSslVerification ALLOWED_STATES = %w[failed canceled running pending success success-with-warnings skipped not_found].freeze diff --git a/app/models/integrations/mock_monitoring.rb b/app/models/integrations/mock_monitoring.rb index 9e474078b28b5352669c789ed6368eb97b628b7d..f3debecd967f80e667fc7cf3aed8aa31e32a7cb9 100644 --- a/app/models/integrations/mock_monitoring.rb +++ b/app/models/integrations/mock_monitoring.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true module Integrations - class MockMonitoring < BaseMonitoring + class MockMonitoring < Integration + include Base::Monitoring + def self.title 'Mock monitoring' end diff --git a/app/models/integrations/phorge.rb b/app/models/integrations/phorge.rb index 959186be32cfd167f4860603dc6cd47a5a4c1f1b..d6533ca301defad8f7f1fd7bffb65818cc8dd540 100644 --- a/app/models/integrations/phorge.rb +++ b/app/models/integrations/phorge.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Phorge < BaseIssueTracker + class Phorge < Integration + include Base::IssueTracker include HasIssueTrackerFields include HasAvatar diff --git a/app/models/integrations/prometheus.rb b/app/models/integrations/prometheus.rb index 97dab35601867dfe459ec565ede3961cb03690fe..13a385456773908c912a2d7d50298088446c1082 100644 --- a/app/models/integrations/prometheus.rb +++ b/app/models/integrations/prometheus.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Prometheus < BaseMonitoring + class Prometheus < Integration + include Base::Monitoring include PrometheusAdapter include Gitlab::Utils::StrongMemoize diff --git a/app/models/integrations/redmine.rb b/app/models/integrations/redmine.rb index 4ac5249787aa5e1ebd9bf6aaa2e82a193ff0682e..176fd18ffd06f69cbf3c4675f1a8c38b2f3a08ce 100644 --- a/app/models/integrations/redmine.rb +++ b/app/models/integrations/redmine.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Redmine < BaseIssueTracker + class Redmine < Integration + include Base::IssueTracker include Integrations::HasIssueTrackerFields validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? diff --git a/app/models/integrations/slack_slash_commands.rb b/app/models/integrations/slack_slash_commands.rb index ff1ac22fee7dc73f0a5c9f20f71f378e2bbadfd5..2af1bbc599cb6fb94b1f33913fe5f2338b833087 100644 --- a/app/models/integrations/slack_slash_commands.rb +++ b/app/models/integrations/slack_slash_commands.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class SlackSlashCommands < BaseSlashCommands + class SlackSlashCommands < Integration + include Base::SlashCommands include Ci::TriggersHelper SLACK_REDIRECT_URL = 'slack://channel?team=%{TEAM}&id=%{CHANNEL}' diff --git a/app/models/integrations/teamcity.rb b/app/models/integrations/teamcity.rb index 5e88aaa0b49624caa01be004d8b08b6d21ddf846..19e57e47569285f79f608ba52984b663dda867e1 100644 --- a/app/models/integrations/teamcity.rb +++ b/app/models/integrations/teamcity.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Teamcity < BaseCi + class Teamcity < Integration + include Base::Ci include PushDataValidations include ReactivelyCached include HasAvatar diff --git a/app/models/integrations/youtrack.rb b/app/models/integrations/youtrack.rb index f2bfb75cf62b1a8e4582586e30f9eb03714aecf9..7e751ed9209e674c797bfa6473303a742f5ee839 100644 --- a/app/models/integrations/youtrack.rb +++ b/app/models/integrations/youtrack.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Youtrack < BaseIssueTracker + class Youtrack < Integration + include Base::IssueTracker include Integrations::HasIssueTrackerFields include HasAvatar diff --git a/app/models/integrations/zentao.rb b/app/models/integrations/zentao.rb index a16734fb6e7b20b519074adba992a4cc5f78038a..5c9ff3c3740cf70e4016fd2177f6f3a4297e0ca5 100644 --- a/app/models/integrations/zentao.rb +++ b/app/models/integrations/zentao.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Integrations - class Zentao < BaseIssueTracker + class Zentao < Integration + include Base::IssueTracker include Gitlab::Routing self.field_storage = :data_fields diff --git a/db/docs/integrations.yml b/db/docs/integrations.yml index 4be931202e97e1c5f820efa9b14204eb0e8ddd57..ac0956954e2ddf1095142c07ae3586846cbe9d3e 100644 --- a/db/docs/integrations.yml +++ b/db/docs/integrations.yml @@ -6,11 +6,6 @@ classes: - Integrations::Asana - Integrations::Assembla - Integrations::Bamboo -- Integrations::BaseCi -- Integrations::BaseIssueTracker -- Integrations::BaseMonitoring -- Integrations::BaseSlashCommands -- Integrations::BaseThirdPartyWiki - Integrations::BeyondIdentity - Integrations::Bugzilla - Integrations::Buildkite diff --git a/doc/development/integrations/index.md b/doc/development/integrations/index.md index e8d67d3600040bd1b4c32a432681cbb9ab309657..4a0490fdee2bd2db95a5c76cc456621be89702bc 100644 --- a/doc/development/integrations/index.md +++ b/doc/development/integrations/index.md @@ -21,13 +21,13 @@ if you need clarification or spot any outdated information. 1. Add a new model in `app/models/integrations` extending from `Integration`. - For example, `Integrations::FooBar` in `app/models/integrations/foo_bar.rb`. - - For certain types of integrations, you can include these base modules (or inherit from them if they are classes): + - For certain types of integrations, you can include these base modules: - `Integrations::Base::ChatNotification` - - `Integrations::BaseCi` - - `Integrations::BaseIssueTracker` - - `Integrations::BaseMonitoring` - - `Integrations::BaseSlashCommands` - - `Integrations::BaseThirdPartyWiki` + - `Integrations::Base::Ci` + - `Integrations::Base::IssueTracker` + - `Integrations::Base::Monitoring` + - `Integrations::Base::SlashCommands` + - `Integrations::Base::ThirdPartyWiki` - For integrations that primarily trigger HTTP calls to external services, you can also use the `Integrations::HasWebHook` concern. This reuses the [webhook functionality](../../user/project/integrations/webhooks.md) in GitLab through an associated `ServiceHook` model, and automatically records request logs diff --git a/spec/factories/integrations.rb b/spec/factories/integrations.rb index adbe228431065a4ad1dfaddb0576fcae434c3f30..72ebefb9ae4142820bb6eb293b913fa15807a57d 100644 --- a/spec/factories/integrations.rb +++ b/spec/factories/integrations.rb @@ -509,14 +509,14 @@ issue_tracker_data { nil } create_data { false } - after(:build) do - Integrations::BaseIssueTracker.skip_callback(:validation, :before, :handle_properties) + after(:build) do |integration| + integration.class.skip_callback(:validation, :before, :handle_properties) end to_create { |instance| instance.save!(validate: false) } - after(:create) do - Integrations::BaseIssueTracker.set_callback(:validation, :before, :handle_properties) + after(:create) do |integration| + integration.class.set_callback(:validation, :before, :handle_properties) end end diff --git a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb index 8ec916e17f8ebbad779a1fbeeb098fd9ceacab50..9075db8ad79e641d54bd791956215dedd11a5327 100644 --- a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Database::Count::ReltuplesCountStrategy do +RSpec.describe Gitlab::Database::Count::ReltuplesCountStrategy, feature_category: :database do before do create_list(:project, 3) create_list(:ci_instance_variable, 2) @@ -27,7 +27,7 @@ end context 'when models using single-type inheritance are used' do - let(:models) { [Group, Integrations::BaseCi, Namespace] } + let(:models) { [Group, Namespace] } before do models.each do |model| diff --git a/spec/models/integrations/base_issue_tracker_spec.rb b/spec/models/concerns/integrations/base/issue_tracker_spec.rb similarity index 78% rename from spec/models/integrations/base_issue_tracker_spec.rb rename to spec/models/concerns/integrations/base/issue_tracker_spec.rb index 1bb24876222ddd1a4415aaa091a3645fb2a0f442..69696c2765674dca9c619aec8a94e1e806065584 100644 --- a/spec/models/integrations/base_issue_tracker_spec.rb +++ b/spec/models/concerns/integrations/base/issue_tracker_spec.rb @@ -2,13 +2,21 @@ require 'spec_helper' -RSpec.describe Integrations::BaseIssueTracker, feature_category: :integrations do - let(:integration) { build(:redmine_integration, project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) } +RSpec.describe Integrations::Base::IssueTracker, feature_category: :integrations do + let(:integration) do + build( + :redmine_integration, + project: project, + active: true, + issue_tracker_data: + build(:issue_tracker_data) + ) + end let_it_be_with_refind(:project) { create(:project) } describe 'default values' do - it { expect(subject.category).to eq(:issue_tracker) } + it { expect(integration.category).to eq(:issue_tracker) } end describe 'Validations' do @@ -46,7 +54,7 @@ end context 'when there is no existing issue tracker integration' do - it { is_expected.to be(nil) } + it { is_expected.to be_nil } end end end diff --git a/spec/models/integrations/bamboo_spec.rb b/spec/models/integrations/bamboo_spec.rb index 62080fa7a128ebef4d39096feb5e47385018c2a2..8a25d368b80e6486b45c9027d398fecd51c2763e 100644 --- a/spec/models/integrations/bamboo_spec.rb +++ b/spec/models/integrations/bamboo_spec.rb @@ -12,7 +12,7 @@ subject(:integration) { build(:bamboo_integration, project: project, bamboo_url: bamboo_url) } - it_behaves_like Integrations::BaseCi + it_behaves_like Integrations::Base::Ci it_behaves_like Integrations::ResetSecretFields diff --git a/spec/models/integrations/buildkite_spec.rb b/spec/models/integrations/buildkite_spec.rb index 793046a9869a1416f5033206a05759a0b68b9fa2..32efcaf5a17a3989be56667fd046000d5633ce22 100644 --- a/spec/models/integrations/buildkite_spec.rb +++ b/spec/models/integrations/buildkite_spec.rb @@ -18,7 +18,7 @@ ) end - it_behaves_like Integrations::BaseCi + it_behaves_like Integrations::Base::Ci it_behaves_like Integrations::ResetSecretFields diff --git a/spec/models/integrations/drone_ci_spec.rb b/spec/models/integrations/drone_ci_spec.rb index ffd24981c89e66379346c43a6e0e9d833a2248f6..ab01d4641e46290958824539dd0c5738a4317049 100644 --- a/spec/models/integrations/drone_ci_spec.rb +++ b/spec/models/integrations/drone_ci_spec.rb @@ -9,7 +9,7 @@ let_it_be(:project) { create(:project, :repository, name: 'project') } - it_behaves_like Integrations::BaseCi + it_behaves_like Integrations::Base::Ci it_behaves_like Integrations::ResetSecretFields do let(:integration) { subject } diff --git a/spec/models/integrations/jenkins_spec.rb b/spec/models/integrations/jenkins_spec.rb index 0924305130a9cbc1a33dea06506c8cb7510cd4fd..14445ee95cfe8a200a4fe9068be17bc289e6ba15 100644 --- a/spec/models/integrations/jenkins_spec.rb +++ b/spec/models/integrations/jenkins_spec.rb @@ -23,7 +23,7 @@ } end - it_behaves_like Integrations::BaseCi + it_behaves_like Integrations::Base::Ci it_behaves_like Integrations::ResetSecretFields do let(:integration) { jenkins_integration } diff --git a/spec/models/integrations/mattermost_slash_commands_spec.rb b/spec/models/integrations/mattermost_slash_commands_spec.rb index 183e9643859e8c92483c6788f8ead4408be183fa..940e96eaaddd700480abd3cd3784588fbeecdd0c 100644 --- a/spec/models/integrations/mattermost_slash_commands_spec.rb +++ b/spec/models/integrations/mattermost_slash_commands_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Integrations::MattermostSlashCommands, feature_category: :integrations do - it_behaves_like Integrations::BaseSlashCommands + it_behaves_like Integrations::Base::SlashCommands describe 'Mattermost API' do let_it_be_with_reload(:project) { create(:project) } diff --git a/spec/models/integrations/mock_ci_spec.rb b/spec/models/integrations/mock_ci_spec.rb index 3ff47ab2f0b3cbe1474ced6e6b8c1bbc89c63e43..7b9d7d4257c15b661523aec434e7f393c5cdc607 100644 --- a/spec/models/integrations/mock_ci_spec.rb +++ b/spec/models/integrations/mock_ci_spec.rb @@ -2,12 +2,12 @@ require 'spec_helper' -RSpec.describe Integrations::MockCi do +RSpec.describe Integrations::MockCi, feature_category: :integrations do let_it_be(:project) { build(:project) } subject(:integration) { described_class.new(project: project, mock_service_url: generate(:url)) } - it_behaves_like Integrations::BaseCi + it_behaves_like Integrations::Base::Ci include_context Integrations::EnableSslVerification diff --git a/spec/models/integrations/prometheus_spec.rb b/spec/models/integrations/prometheus_spec.rb index 2366a8d16c76b3646fd80559a7823d21d23f2a0d..d0cc1a902a23e987217e63c4d2c8dc2a3db96242 100644 --- a/spec/models/integrations/prometheus_spec.rb +++ b/spec/models/integrations/prometheus_spec.rb @@ -12,7 +12,7 @@ let(:integration) { project.prometheus_integration } - it_behaves_like Integrations::BaseMonitoring + it_behaves_like Integrations::Base::Monitoring context 'redirects' do it 'does not follow redirects' do diff --git a/spec/models/integrations/slack_slash_commands_spec.rb b/spec/models/integrations/slack_slash_commands_spec.rb index 5d6f214a5d5814324255fb7d139ed9e4610f51b7..77e7df0f3bd5f57abddc704f7ae76eb4ec9c3c18 100644 --- a/spec/models/integrations/slack_slash_commands_spec.rb +++ b/spec/models/integrations/slack_slash_commands_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Integrations::SlackSlashCommands, feature_category: :integrations do - it_behaves_like Integrations::BaseSlashCommands + it_behaves_like Integrations::Base::SlashCommands describe '#trigger' do context 'when an auth url is generated' do diff --git a/spec/models/integrations/teamcity_spec.rb b/spec/models/integrations/teamcity_spec.rb index daea849f6aa122ed0354296a3ac349e1b26006b2..f98088aa6482307e7803134bbc95168b6770ec2c 100644 --- a/spec/models/integrations/teamcity_spec.rb +++ b/spec/models/integrations/teamcity_spec.rb @@ -24,7 +24,7 @@ ) end - it_behaves_like Integrations::BaseCi + it_behaves_like Integrations::Base::Ci it_behaves_like Integrations::ResetSecretFields diff --git a/spec/support/shared_examples/models/integrations/base_ci_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/base/ci_shared_examples.rb similarity index 71% rename from spec/support/shared_examples/models/integrations/base_ci_shared_examples.rb rename to spec/support/shared_examples/models/concerns/integrations/base/ci_shared_examples.rb index 08fab45e41b1f922ef667015a2c8a8e2b28f462d..171f0d9fe4d9501785b9f7e6c86eedd5c9ef8a08 100644 --- a/spec/support/shared_examples/models/integrations/base_ci_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/integrations/base/ci_shared_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples Integrations::BaseCi do +RSpec.shared_examples Integrations::Base::Ci do describe 'default values' do it { expect(subject.category).to eq(:ci) } end diff --git a/spec/support/shared_examples/models/integrations/base_monitoring_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/base/monitoring_shared_examples.rb similarity index 69% rename from spec/support/shared_examples/models/integrations/base_monitoring_shared_examples.rb rename to spec/support/shared_examples/models/concerns/integrations/base/monitoring_shared_examples.rb index 5d7e7633a23e8aa3abbf88a5ac75fd40489011d6..4885bc41e17cf69588ee2e5812ff901a319bae78 100644 --- a/spec/support/shared_examples/models/integrations/base_monitoring_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/integrations/base/monitoring_shared_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples Integrations::BaseMonitoring do +RSpec.shared_examples Integrations::Base::Monitoring do describe 'default values' do it { expect(subject.category).to eq(:monitoring) } end diff --git a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/base/slash_commands_shared_examples.rb similarity index 91% rename from spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb rename to spec/support/shared_examples/models/concerns/integrations/base/slash_commands_shared_examples.rb index 90bb75d402f608afa6e80f5e1e66878e3919d354..7de625540344b4de7db97789ff91673f881c6508 100644 --- a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/integrations/base/slash_commands_shared_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples Integrations::BaseSlashCommands do +RSpec.shared_examples Integrations::Base::SlashCommands do describe "Associations" do it { is_expected.to respond_to :token } end @@ -32,7 +32,7 @@ describe '#trigger' do subject { described_class.new } - context 'no token is passed' do + context 'when token is not passed' do let(:params) { {} } it 'returns nil' do @@ -48,7 +48,7 @@ allow(subject).to receive(:token).and_return('token') end - context 'no user can be found' do + context 'when user does not exist' do context 'when no url can be generated' do it 'responds with the authorize url' do response = subject.trigger(params) @@ -99,14 +99,14 @@ end it 'triggers the command' do - expect_any_instance_of(Gitlab::SlashCommands::Command).to receive(:execute) + expect_any_instance_of(Gitlab::SlashCommands::Command).to receive(:execute) # rubocop:disable RSpec/AnyInstanceOf -- Legacy use subject.trigger(params) end shared_examples_for 'blocks command execution' do - it do - expect_any_instance_of(Gitlab::SlashCommands::Command).not_to receive(:execute) + it 'blocks command execution' do + expect_any_instance_of(Gitlab::SlashCommands::Command).not_to receive(:execute) # rubocop:disable RSpec/AnyInstanceOf -- Legacy use result = subject.trigger(params) expect(result[:text]).to match(error_message) @@ -154,7 +154,7 @@ it 'caches the slash command params and returns confirmation message' do expect(Rails.cache).to receive(:write).with(an_instance_of(String), params, { expires_in: 3.minutes }) - expect_any_instance_of(Gitlab::SlashCommands::Presenters::Access).to receive(:confirm) + expect_any_instance_of(Gitlab::SlashCommands::Presenters::Access).to receive(:confirm) # rubocop:disable RSpec/AnyInstanceOf -- Legacy use subject.trigger(params) end