summaryrefslogtreecommitdiff
path: root/app/workers/mail_scheduler/notification_service_worker.rb
blob: 12e8de4491e5bd82ac5e16c105f8e0b8761a3daf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# frozen_string_literal: true

require 'active_job/arguments'

module MailScheduler
  class NotificationServiceWorker # rubocop:disable Scalability/IdempotentWorker
    include ApplicationWorker

    data_consistency :always

    sidekiq_options retry: 3
    include MailSchedulerQueue

    feature_category :team_planning
    worker_resource_boundary :cpu
    loggable_arguments 0

    def perform(meth, *args)
      check_arguments!(args)

      if ::Feature.enabled?(:verify_mail_scheduler_notification_service_worker_method_names) &&
          NotificationService.permitted_actions.exclude?(meth.to_sym)

        raise(ArgumentError, "#{meth} not allowed for #{self.class.name}")
      end

      deserialized_args = ActiveJob::Arguments.deserialize(args)
      notification_service.public_send(meth, *deserialized_args) # rubocop:disable GitlabSecurity/PublicSend
    rescue ActiveJob::DeserializationError
      # No-op.
      # This exception gets raised when an argument
      # is correct (deserializeable), but it still cannot be deserialized.
      # This can happen when an object has been deleted after
      # rails passes this job to sidekiq, but before
      # sidekiq gets it for execution.
      # In this case just do nothing.
    end

    def self.perform_async(*args)
      super(*ActiveJob::Arguments.serialize(args))
    end

    private

    # This is copied over from https://github.com/rails/rails/blob/v6.0.1/activejob/lib/active_job/arguments.rb#L50
    # because it is declared as a private constant
    PERMITTED_TYPES = [NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass].freeze

    private_constant :PERMITTED_TYPES

    # If an argument is in the PERMITTED_TYPES list,
    # it means the argument cannot be deserialized.
    # Which means there's something wrong with our code.
    def check_arguments!(args)
      args.each do |arg|
        if arg.class.in?(PERMITTED_TYPES)
          raise(ArgumentError, "Argument `#{arg}` cannot be deserialized because of its type")
        end
      end
    end
  end
end