Module: Integrations::Base::Integration

Extended by:
ActiveSupport::Concern, Gitlab::Utils::Override
Included in:
Integration
Defined in:
app/models/concerns/integrations/base/integration.rb

Constant Summary collapse

UnknownType =
Class.new(StandardError)
INTEGRATION_NAMES =
%w[
  asana assembla bamboo bugzilla buildkite campfire clickup confluence custom_issue_tracker
  datadog diffblue_cover discord drone_ci emails_on_push ewm external_wiki
  gitlab_slack_application hangouts_chat harbor irker jira linear matrix
  mattermost mattermost_slash_commands microsoft_teams packagist phorge pipelines_email
  pivotaltracker pumble pushover redmine slack slack_slash_commands squash_tm teamcity telegram
  unify_circuit webex_teams youtrack zentao
].freeze
INSTANCE_LEVEL_ONLY_INTEGRATION_NAMES =

Integrations that can only be enabled on the instance-level

%w[
  beyond_identity
].freeze
PROJECT_LEVEL_ONLY_INTEGRATION_NAMES =

Integrations that can only be enabled on the project-level

%w[
  apple_app_store google_play jenkins
].freeze
PROJECT_AND_GROUP_LEVEL_ONLY_INTEGRATION_NAMES =

Integrations that cannot be enabled on the instance-level

%w[
  jira_cloud_app
].freeze
DEV_INTEGRATION_NAMES =

Fake integrations to help with local development.

%w[
  mock_ci mock_monitoring
].freeze
BASE_CLASSES =

Base classes which aren’t actual integrations.

%w[].freeze
BASE_ATTRIBUTES =
%w[id instance project_id group_id created_at updated_at
encrypted_properties encrypted_properties_iv properties organization_id].freeze
SECTION_TYPE_CONFIGURATION =
'configuration'
SECTION_TYPE_CONNECTION =
'connection'
SECTION_TYPE_TRIGGER =
'trigger'
SNOWPLOW_EVENT_ACTION =
'perform_integrations_action'
SNOWPLOW_EVENT_LABEL =
'redis_hll_counters.ecosystem.ecosystem_total_unique_counts_monthly'

Instance Method Summary collapse

Methods included from Gitlab::Utils::Override

extended, extensions, included, method_added, override, prepended, queue_verification, verify!

Instance Method Details

#activate!Object



815
816
817
# File 'app/models/concerns/integrations/base/integration.rb', line 815

def activate!
  update(active: true)
end

#activate_disabled_reasonObject



622
623
624
# File 'app/models/concerns/integrations/base/integration.rb', line 622

def activate_disabled_reason
  nil
end

#activated?Boolean

Returns:

  • (Boolean)


606
607
608
# File 'app/models/concerns/integrations/base/integration.rb', line 606

def activated?
  active
end

#after_build_from_integration(new_integration) ⇒ Object

Hook for integrations to configure associations after duplication. Override in subclasses when associations need the parent context.



584
585
586
# File 'app/models/concerns/integrations/base/integration.rb', line 584

def after_build_from_integration(new_integration)
  # no-op
end

#api_field_namesObject



704
705
706
707
708
# File 'app/models/concerns/integrations/base/integration.rb', line 704

def api_field_names
  fields # rubocop:disable Style/NumberedParameters -- existing code moved as is
    .reject { _1[:type] == :password || _1[:name] == 'webhook' || (_1.key?(:if) && _1[:if] != true) }
    .pluck(:name) # rubocop:disable Database/AvoidUsingPluckWithoutLimit -- existing code moved as is
end

#async_execute(data) ⇒ Object



779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
# File 'app/models/concerns/integrations/base/integration.rb', line 779

def async_execute(data)
  return if ::Gitlab::SilentMode.enabled?
  return unless active?

  data = data.with_indifferent_access

  # Temporarily log when we return within this method to gather data for
  # https://gitlab.com/gitlab-org/gitlab/-/issues/382999
  unless supported_events.include?(data[:object_kind].to_s)
    log_info(
      'async_execute did nothing due to event not being supported',
      event: data[:object_kind]
    )
    return
  end

  Integrations::ExecuteWorker.perform_async(id, data.deep_stringify_keys)
end

#attributesObject



671
672
673
# File 'app/models/concerns/integrations/base/integration.rb', line 671

def attributes
  super.except('properties')
end

#attribution_noticeObject



650
651
652
# File 'app/models/concerns/integrations/base/integration.rb', line 650

def attribution_notice
  self.class.attribution_notice
end

#categoryObject



626
627
628
# File 'app/models/concerns/integrations/base/integration.rb', line 626

def category
  read_attribute(:category).to_sym
end

#chat?Boolean

Returns:

  • (Boolean)


803
804
805
# File 'app/models/concerns/integrations/base/integration.rb', line 803

def chat?
  category == :chat
end

#ci?Boolean

Returns:

  • (Boolean)


807
808
809
# File 'app/models/concerns/integrations/base/integration.rb', line 807

def ci?
  category == :ci
end

#configurable_eventsObject



714
715
716
717
718
719
720
721
722
723
# File 'app/models/concerns/integrations/base/integration.rb', line 714

def configurable_events
  events = supported_events

  # No need to disable individual triggers when there is only one
  if events.count == 1
    []
  else
    events
  end
end

#deactivate!Object



811
812
813
# File 'app/models/concerns/integrations/base/integration.rb', line 811

def deactivate!
  update(active: false)
end

#default_test_eventObject



729
730
731
# File 'app/models/concerns/integrations/base/integration.rb', line 729

def default_test_event
  self.class.default_test_event
end

#descriptionObject



638
639
640
# File 'app/models/concerns/integrations/base/integration.rb', line 638

def description
  self.class.description
end

#dupObject



590
591
592
593
594
595
596
597
598
599
600
# File 'app/models/concerns/integrations/base/integration.rb', line 590

def dup
  new_integration = super
  new_integration.assign_attributes(reencrypt_properties)

  if supports_data_fields?
    fields = data_fields.dup
    fields.integration = new_integration
  end

  new_integration
end

#editable?Boolean

Returns:

  • (Boolean)


618
619
620
# File 'app/models/concerns/integrations/base/integration.rb', line 618

def editable?
  true
end

#event_channel_namesObject



696
697
698
# File 'app/models/concerns/integrations/base/integration.rb', line 696

def event_channel_names
  []
end

#event_namesObject



700
701
702
# File 'app/models/concerns/integrations/base/integration.rb', line 700

def event_names
  self.class.event_names
end

#execute(data) ⇒ Object



733
734
735
# File 'app/models/concerns/integrations/base/integration.rb', line 733

def execute(data)
  # implement inside child
end

#fieldsObject



578
579
580
# File 'app/models/concerns/integrations/base/integration.rb', line 578

def fields
  self.class.fields.dup
end

#form_fieldsObject



710
711
712
# File 'app/models/concerns/integrations/base/integration.rb', line 710

def form_fields
  fields.reject { _1[:api_only] == true || (_1.key?(:if) && _1[:if] != true) } # rubocop:disable Style/NumberedParameters -- existing code moved as is
end

#group_level?Boolean

Returns:

  • (Boolean)


753
754
755
# File 'app/models/concerns/integrations/base/integration.rb', line 753

def group_level?
  group_id.present?
end

#helpObject



642
643
644
# File 'app/models/concerns/integrations/base/integration.rb', line 642

def help
  self.class.help
end

#inheritable?Boolean

Returns:

  • (Boolean)


602
603
604
# File 'app/models/concerns/integrations/base/integration.rb', line 602

def inheritable?
  instance_level? || group_level?
end

#initialize_propertiesObject



630
631
632
# File 'app/models/concerns/integrations/base/integration.rb', line 630

def initialize_properties
  self.properties = {} if has_attribute?(:encrypted_properties) && encrypted_properties.nil?
end

#instance_level?Boolean

Returns:

  • (Boolean)


757
758
759
# File 'app/models/concerns/integrations/base/integration.rb', line 757

def instance_level?
  instance?
end

#json_fieldsObject

Expose a list of fields in the JSON endpoint.

This list is used in ‘Integration#as_json(only: json_fields)`.



665
666
667
# File 'app/models/concerns/integrations/base/integration.rb', line 665

def json_fields
  %w[active]
end

#manual_activation?Boolean

Returns:

  • (Boolean)


614
615
616
# File 'app/models/concerns/integrations/base/integration.rb', line 614

def manual_activation?
  true
end

#operating?Boolean

Returns:

  • (Boolean)


610
611
612
# File 'app/models/concerns/integrations/base/integration.rb', line 610

def operating?
  active && persisted?
end

#parentObject



761
762
763
# File 'app/models/concerns/integrations/base/integration.rb', line 761

def parent
  project || group
end

#project_level?Boolean

Returns:

  • (Boolean)


749
750
751
# File 'app/models/concerns/integrations/base/integration.rb', line 749

def project_level?
  project_id.present?
end

#reencrypt_propertiesObject



685
686
687
688
689
690
691
692
693
694
# File 'app/models/concerns/integrations/base/integration.rb', line 685

def reencrypt_properties
  unless properties.nil? || properties.empty?
    attr_encrypted_attributes = self.class.attr_encrypted_encrypted_attributes[:properties]
    key = dynamic_encryption_key_for_operation(attr_encrypted_attributes[:key])
    iv = generate_iv(attr_encrypted_attributes[:algorithm])
    ep = self.class.attr_encrypted_encrypt(:properties, properties, { key: key, iv: iv })
  end

  { 'encrypted_properties' => ep, 'encrypted_properties_iv' => iv }
end

#reset_updated_propertiesObject



775
776
777
# File 'app/models/concerns/integrations/base/integration.rb', line 775

def reset_updated_properties
  @updated_properties = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables -- legacy use
end

#secret_fieldsObject



658
659
660
# File 'app/models/concerns/integrations/base/integration.rb', line 658

def secret_fields
  fields.select(&:secret?).pluck(:name) # rubocop:disable Database/AvoidUsingPluckWithoutLimit -- existing code moved as is
end

#sectionsObject



654
655
656
# File 'app/models/concerns/integrations/base/integration.rb', line 654

def sections
  []
end

#supported_eventsObject



725
726
727
# File 'app/models/concerns/integrations/base/integration.rb', line 725

def supported_events
  self.class.supported_events
end

#supports_data_fields?Boolean

override if needed

Returns:

  • (Boolean)


799
800
801
# File 'app/models/concerns/integrations/base/integration.rb', line 799

def supports_data_fields?
  false
end

#test(data) ⇒ Object



737
738
739
740
741
# File 'app/models/concerns/integrations/base/integration.rb', line 737

def test(data)
  # default implementation
  result = execute(data)
  { success: result.present?, result: result }
end

#testable?Boolean

Disable test for instance-level and group-level integrations. gitlab.com/gitlab-org/gitlab/-/issues/213138

Returns:

  • (Boolean)


745
746
747
# File 'app/models/concerns/integrations/base/integration.rb', line 745

def testable?
  project_level?
end

#titleObject



634
635
636
# File 'app/models/concerns/integrations/base/integration.rb', line 634

def title
  self.class.title
end

#to_database_hashObject

Returns a hash of attributes (columns => values) used for inserting into the database.



676
677
678
679
680
681
682
683
# File 'app/models/concerns/integrations/base/integration.rb', line 676

def to_database_hash
  column = self.class.attribute_aliases.fetch('type', 'type')

  attributes_for_database
    .except(*BASE_ATTRIBUTES)
    .merge(column => type)
    .merge(reencrypt_properties)
end

#to_paramObject



646
647
648
# File 'app/models/concerns/integrations/base/integration.rb', line 646

def to_param
  self.class.to_param
end

#toggle!Object



819
820
821
# File 'app/models/concerns/integrations/base/integration.rb', line 819

def toggle!
  active? ? deactivate! : activate!
end

#updated_propertiesObject

Returns a hash of the properties that have been assigned a new value since last save, indicating their original values (attr => original value). ActiveRecord does not provide a mechanism to track changes in serialized keys, so we need a specific implementation for integration properties. This allows to track changes to properties set with the accessor methods, but not direct manipulation of properties hash.



771
772
773
# File 'app/models/concerns/integrations/base/integration.rb', line 771

def updated_properties
  @updated_properties ||= ActiveSupport::HashWithIndifferentAccess.new
end