openSUSE/open-build-service

View on GitHub
src/api/app/models/token/workflow.rb

Summary

Maintainability
A
45 mins
Test Coverage
A
100%
class Token::Workflow < Token
  AUTHENTICATION_DOCUMENTATION_LINK = "#{::Workflow::SCM_CI_DOCUMENTATION_URL}#sec.obs.obs_scm_ci_workflow_integration.setup.token_authentication.how_to_authenticate_scm_with_obs".freeze

  has_many :workflow_runs, dependent: :destroy, foreign_key: 'token_id', inverse_of: false
  has_and_belongs_to_many :users,
                          join_table: :workflow_token_users,
                          foreign_key: :token_id,
                          association_foreign_key: :user_id,
                          dependent: :destroy,
                          inverse_of: :users
  has_and_belongs_to_many :groups,
                          join_table: :workflow_token_groups,
                          foreign_key: :token_id,
                          association_foreign_key: :group_id,
                          dependent: :destroy,
                          inverse_of: :groups

  validates :scm_token, presence: true
  # Either a url referring to the worklflow configuration file or a filepath to the config inside the
  # SCM repository has to be provided
  validates :workflow_configuration_path, presence: true, unless: -> { workflow_configuration_url.present? }
  validates :workflow_configuration_url, presence: true, unless: -> { workflow_configuration_path.present? }

  def call(options)
    set_triggered_at
    @scm_webhook = options[:scm_webhook]
    workflow_run = options[:workflow_run]
    raise Token::Errors::MissingPayload, 'A payload is required' if @scm_webhook.payload.blank?

    workflow_run.update(response_url: @scm_webhook.payload[:api_endpoint])

    # We return early with a ping event, since it doesn't make sense to perform payload checks with it, just respond
    if @scm_webhook.ping_event?
      SCMStatusReporter.new(@scm_webhook.payload, @scm_webhook.payload, scm_token, workflow_run, 'success', initial_report: true).call
      return []
    end

    yaml_file = Workflows::YAMLDownloader.new(@scm_webhook.payload, token: self).call
    @workflows = Workflows::YAMLToWorkflowsService.new(yaml_file: yaml_file, scm_webhook: @scm_webhook, token: self, workflow_run: workflow_run).call

    return validation_errors unless validation_errors.none?

    # This is just an initial generic report to give a feedback asap. Initial status pending
    SCMStatusReporter.new(@scm_webhook.payload, @scm_webhook.payload, scm_token, workflow_run, initial_report: true).call
    @workflows.each do |workflow|
      return workflow.errors.full_messages if workflow.invalid?(:call)

      workflow.call
    end
    SCMStatusReporter.new(@scm_webhook.payload, @scm_webhook.payload, scm_token, workflow_run, 'success', initial_report: true).call
    # Always returning validation errors to report them back to the SCM in order to help users debug their workflows
    validation_errors
  rescue Octokit::Unauthorized, Gitlab::Error::Unauthorized
    raise Token::Errors::SCMTokenInvalid, "Your SCM token secret is not properly set in your OBS workflow token.\nCheck #{AUTHENTICATION_DOCUMENTATION_LINK}"
  end

  def owned_by?(some_user)
    # TODO: remove the first condition if we migrate, with a data migration, the Token.executor to Token.users
    executor == some_user || users.include?(some_user) || groups.map(&:users).flatten.include?(some_user)
  end

  def workflow_configuration_path_default?
    workflow_configuration_path == '.obs/workflows.yml'
  end

  private

  def validation_errors
    @validation_errors ||= begin
      error_messages = []

      error_messages << @scm_webhook.errors.full_messages unless @scm_webhook.valid?
      @workflows.each do |workflow|
        error_messages << workflow.errors.full_messages unless workflow.valid?
      end

      error_messages.flatten
    end
  end
end

# == Schema Information
#
# Table name: tokens
#
#  id                          :integer          not null, primary key
#  description                 :string(64)       default("")
#  scm_token                   :string(255)      indexed
#  string                      :string(255)      indexed
#  triggered_at                :datetime
#  type                        :string(255)
#  workflow_configuration_path :string(255)      default(".obs/workflows.yml")
#  workflow_configuration_url  :string(255)
#  executor_id                 :integer          not null, indexed
#  package_id                  :integer          indexed
#
# Indexes
#
#  index_tokens_on_scm_token  (scm_token)
#  index_tokens_on_string     (string) UNIQUE
#  package_id                 (package_id)
#  user_id                    (executor_id)
#
# Foreign Keys
#
#  tokens_ibfk_1  (executor_id => users.id)
#  tokens_ibfk_2  (package_id => packages.id)
#