Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/service.rb')
-rw-r--r--app/models/service.rb352
1 files changed, 188 insertions, 164 deletions
diff --git a/app/models/service.rb b/app/models/service.rb
index 40e7e5552d1..e63e06bf46f 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -9,12 +9,11 @@ class Service < ApplicationRecord
include DataFields
include IgnorableColumns
- ignore_columns %i[title description], remove_with: '13.4', remove_after: '2020-09-22'
ignore_columns %i[default], remove_with: '13.5', remove_after: '2020-10-22'
SERVICE_NAMES = %w[
alerts asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker discord
- drone_ci emails_on_push external_wiki flowdock hangouts_chat hipchat irker jira
+ drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack
].freeze
@@ -27,16 +26,17 @@ class Service < ApplicationRecord
default_value_for :active, false
default_value_for :alert_events, true
- default_value_for :push_events, true
- default_value_for :issues_events, true
- default_value_for :confidential_issues_events, true
+ default_value_for :category, 'common'
default_value_for :commit_events, true
- default_value_for :merge_requests_events, true
- default_value_for :tag_push_events, true
- default_value_for :note_events, true
+ default_value_for :confidential_issues_events, true
default_value_for :confidential_note_events, true
+ default_value_for :issues_events, true
default_value_for :job_events, true
+ default_value_for :merge_requests_events, true
+ default_value_for :note_events, true
default_value_for :pipeline_events, true
+ default_value_for :push_events, true
+ default_value_for :tag_push_events, true
default_value_for :wiki_page_events, true
after_initialize :initialize_properties
@@ -46,6 +46,7 @@ class Service < ApplicationRecord
after_commit :cache_project_has_external_wiki
belongs_to :project, inverse_of: :services
+ belongs_to :group, inverse_of: :services
has_one :service_hook
validates :project_id, presence: true, unless: -> { template? || instance? || group_id }
@@ -64,8 +65,9 @@ class Service < ApplicationRecord
scope :active, -> { where(active: true) }
scope :by_type, -> (type) { where(type: type) }
scope :by_active_flag, -> (flag) { where(active: flag) }
- scope :templates, -> { where(template: true, type: available_services_types) }
- scope :instances, -> { where(instance: true, type: available_services_types) }
+ scope :for_group, -> (group) { where(group_id: group, type: available_services_types) }
+ scope :for_template, -> { where(template: true, type: available_services_types) }
+ scope :for_instance, -> { where(instance: true, type: available_services_types) }
scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
@@ -81,7 +83,178 @@ class Service < ApplicationRecord
scope :alert_hooks, -> { where(alert_events: true, active: true) }
scope :deployment, -> { where(category: 'deployment') }
- default_value_for :category, 'common'
+ # Provide convenient accessor methods for each serialized property.
+ # Also keep track of updated properties in a similar way as ActiveModel::Dirty
+ def self.prop_accessor(*args)
+ args.each do |arg|
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ unless method_defined?(arg)
+ def #{arg}
+ properties['#{arg}']
+ end
+ end
+
+ def #{arg}=(value)
+ self.properties ||= {}
+ updated_properties['#{arg}'] = #{arg} unless #{arg}_changed?
+ self.properties['#{arg}'] = value
+ end
+
+ def #{arg}_changed?
+ #{arg}_touched? && #{arg} != #{arg}_was
+ end
+
+ def #{arg}_touched?
+ updated_properties.include?('#{arg}')
+ end
+
+ def #{arg}_was
+ updated_properties['#{arg}']
+ end
+ RUBY
+ end
+ end
+
+ # Provide convenient boolean accessor methods for each serialized property.
+ # Also keep track of updated properties in a similar way as ActiveModel::Dirty
+ def self.boolean_accessor(*args)
+ self.prop_accessor(*args)
+
+ args.each do |arg|
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
+ def #{arg}?
+ # '!!' is used because nil or empty string is converted to nil
+ !!ActiveRecord::Type::Boolean.new.cast(#{arg})
+ end
+ RUBY
+ end
+ end
+
+ def self.to_param
+ raise NotImplementedError
+ end
+
+ def self.event_names
+ self.supported_events.map { |event| ServicesHelper.service_event_field_name(event) }
+ end
+
+ def self.supported_event_actions
+ %w[]
+ end
+
+ def self.supported_events
+ %w[commit push tag_push issue confidential_issue merge_request wiki_page]
+ end
+
+ def self.event_description(event)
+ ServicesHelper.service_event_description(event)
+ end
+
+ def self.find_or_create_templates
+ create_nonexistent_templates
+ for_template
+ end
+
+ def self.create_nonexistent_templates
+ nonexistent_services = list_nonexistent_services_for(for_template)
+ return if nonexistent_services.empty?
+
+ # Create within a transaction to perform the lowest possible SQL queries.
+ transaction do
+ nonexistent_services.each do |service_type|
+ service_type.constantize.create(template: true)
+ end
+ end
+ end
+ private_class_method :create_nonexistent_templates
+
+ def self.find_or_initialize_integration(name, instance: false, group_id: nil)
+ if name.in?(available_services_names)
+ "#{name}_service".camelize.constantize.find_or_initialize_by(instance: instance, group_id: group_id)
+ end
+ end
+
+ def self.find_or_initialize_all(scope)
+ scope + build_nonexistent_services_for(scope)
+ end
+
+ def self.build_nonexistent_services_for(scope)
+ list_nonexistent_services_for(scope).map do |service_type|
+ service_type.constantize.new
+ end
+ end
+ private_class_method :build_nonexistent_services_for
+
+ def self.list_nonexistent_services_for(scope)
+ # Using #map instead of #pluck to save one query count. This is because
+ # ActiveRecord loaded the object here, so we don't need to query again later.
+ available_services_types - scope.map(&:type)
+ end
+ private_class_method :list_nonexistent_services_for
+
+ def self.available_services_names
+ service_names = services_names
+ service_names += dev_services_names
+
+ service_names.sort_by(&:downcase)
+ end
+
+ def self.services_names
+ SERVICE_NAMES
+ end
+
+ def self.dev_services_names
+ return [] unless Rails.env.development?
+
+ DEV_SERVICE_NAMES
+ end
+
+ def self.available_services_types
+ available_services_names.map { |service_name| "#{service_name}_service".camelize }
+ end
+
+ def self.services_types
+ services_names.map { |service_name| "#{service_name}_service".camelize }
+ end
+
+ def self.build_from_integration(project_id, integration)
+ service = integration.dup
+
+ if integration.supports_data_fields?
+ data_fields = integration.data_fields.dup
+ data_fields.service = service
+ end
+
+ service.template = false
+ service.instance = false
+ service.inherit_from_id = integration.id if integration.instance?
+ service.project_id = project_id
+ service.active = false if service.invalid?
+ service
+ end
+
+ def self.instance_exists_for?(type)
+ exists?(instance: true, type: type)
+ end
+
+ def self.default_integration(type, scope)
+ closest_group_integration(type, scope) || instance_level_integration(type)
+ end
+
+ def self.closest_group_integration(type, scope)
+ group_ids = scope.ancestors.select(:id)
+ array = group_ids.to_sql.present? ? "array(#{group_ids.to_sql})" : 'ARRAY[]'
+
+ where(type: type, group_id: group_ids)
+ .order(Arel.sql("array_position(#{array}::bigint[], services.group_id)"))
+ .first
+ end
+ private_class_method :closest_group_integration
+
+ def self.instance_level_integration(type)
+ find_by(type: type, instance: true)
+ end
+ private_class_method :instance_level_integration
def activated?
active
@@ -124,10 +297,6 @@ class Service < ApplicationRecord
self.class.to_param
end
- def self.to_param
- raise NotImplementedError
- end
-
def fields
# implement inside child
[]
@@ -137,11 +306,11 @@ class Service < ApplicationRecord
#
# This list is used in `Service#as_json(only: json_fields)`.
def json_fields
- %w(active)
+ %w[active]
end
def to_service_hash
- as_json(methods: :type, except: %w[id template instance project_id])
+ as_json(methods: :type, except: %w[id template instance project_id group_id])
end
def to_data_fields_hash
@@ -156,10 +325,6 @@ class Service < ApplicationRecord
self.class.event_names
end
- def self.event_names
- self.supported_events.map { |event| ServicesHelper.service_event_field_name(event) }
- end
-
def event_field(event)
nil
end
@@ -188,18 +353,10 @@ class Service < ApplicationRecord
self.class.supported_event_actions
end
- def self.supported_event_actions
- %w()
- end
-
def supported_events
self.class.supported_events
end
- def self.supported_events
- %w(commit push tag_push issue confidential_issue merge_request wiki_page)
- end
-
def execute(data)
# implement inside child
end
@@ -210,59 +367,10 @@ class Service < ApplicationRecord
{ success: result.present?, result: result }
end
- # Disable test for instance-level services.
+ # Disable test for instance-level and group-level services.
# https://gitlab.com/gitlab-org/gitlab/-/issues/213138
def can_test?
- !instance?
- end
-
- # Provide convenient accessor methods
- # for each serialized property.
- # Also keep track of updated properties in a similar way as ActiveModel::Dirty
- def self.prop_accessor(*args)
- args.each do |arg|
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- unless method_defined?(arg)
- def #{arg}
- properties['#{arg}']
- end
- end
-
- def #{arg}=(value)
- self.properties ||= {}
- updated_properties['#{arg}'] = #{arg} unless #{arg}_changed?
- self.properties['#{arg}'] = value
- end
-
- def #{arg}_changed?
- #{arg}_touched? && #{arg} != #{arg}_was
- end
-
- def #{arg}_touched?
- updated_properties.include?('#{arg}')
- end
-
- def #{arg}_was
- updated_properties['#{arg}']
- end
- RUBY
- end
- end
-
- # Provide convenient boolean accessor methods
- # for each serialized property.
- # Also keep track of updated properties in a similar way as ActiveModel::Dirty
- def self.boolean_accessor(*args)
- self.prop_accessor(*args)
-
- args.each do |arg|
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
- def #{arg}?
- # '!!' is used because nil or empty string is converted to nil
- !!ActiveRecord::Type::Boolean.new.cast(#{arg})
- end
- RUBY
- end
+ !instance? && !group_id
end
# Returns a hash of the properties that have been assigned a new value since last save,
@@ -289,86 +397,6 @@ class Service < ApplicationRecord
self.category == :issue_tracker
end
- def self.find_or_create_templates
- create_nonexistent_templates
- templates
- end
-
- private_class_method def self.create_nonexistent_templates
- nonexistent_services = list_nonexistent_services_for(templates)
- return if nonexistent_services.empty?
-
- # Create within a transaction to perform the lowest possible SQL queries.
- transaction do
- nonexistent_services.each do |service_type|
- service_type.constantize.create(template: true)
- end
- end
- end
-
- def self.find_or_initialize_instances
- instances + build_nonexistent_instances
- end
-
- private_class_method def self.build_nonexistent_instances
- list_nonexistent_services_for(instances).map do |service_type|
- service_type.constantize.new
- end
- end
-
- private_class_method def self.list_nonexistent_services_for(scope)
- available_services_types - scope.map(&:type)
- end
-
- def self.available_services_names
- service_names = services_names
- service_names += dev_services_names
-
- service_names.sort_by(&:downcase)
- end
-
- def self.services_names
- SERVICE_NAMES
- end
-
- def self.dev_services_names
- return [] unless Rails.env.development?
-
- DEV_SERVICE_NAMES
- end
-
- def self.available_services_types
- available_services_names.map { |service_name| "#{service_name}_service".camelize }
- end
-
- def self.services_types
- services_names.map { |service_name| "#{service_name}_service".camelize }
- end
-
- def self.build_from_integration(project_id, integration)
- service = integration.dup
-
- if integration.supports_data_fields?
- data_fields = integration.data_fields.dup
- data_fields.service = service
- end
-
- service.template = false
- service.instance = false
- service.inherit_from_id = integration.id if integration.instance?
- service.project_id = project_id
- service.active = false if service.invalid?
- service
- end
-
- def self.instance_exists_for?(type)
- exists?(instance: true, type: type)
- end
-
- def self.instance_for(type)
- find_by(instance: true, type: type)
- end
-
# override if needed
def supports_data_fields?
false
@@ -396,10 +424,6 @@ class Service < ApplicationRecord
end
end
- def self.event_description(event)
- ServicesHelper.service_event_description(event)
- end
-
def valid_recipients?
activated? && !importing?
end