File: ios.rb

package info (click to toggle)
ruby-noticed 3.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 324 kB
  • sloc: ruby: 1,186; makefile: 4
file content (95 lines) | stat: -rw-r--r-- 3,279 bytes parent folder | download
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
require "apnotic"

module Noticed
  module DeliveryMethods
    class Ios < DeliveryMethod
      cattr_accessor :development_connection_pool, :production_connection_pool

      required_options :bundle_identifier, :key_id, :team_id, :apns_key, :device_tokens

      def deliver
        evaluate_option(:device_tokens).each do |device_token|
          apn = Apnotic::Notification.new(device_token)
          format_notification(apn)

          connection_pool = (!!evaluate_option(:development)) ? development_pool : production_pool
          connection_pool.with do |connection|
            response = connection.push(apn)
            raise "Timeout sending iOS push notification" unless response
            connection.close

            if bad_token?(response) && config[:invalid_token]
              # Allow notification to cleanup invalid iOS device tokens
              notification.instance_exec(device_token, &config[:invalid_token])
            elsif !response.ok?
              raise "Request failed #{response.body}"
            end
          end
        end
      end

      private

      def format_notification(apn)
        apn.topic = evaluate_option(:bundle_identifier)

        method = config[:format]
        # Call method on Notifier if defined
        if method&.is_a?(Symbol) && event.respond_to?(method, true)
          event.send(method, notification, apn)
        # If Proc, evaluate it on the Notification
        elsif method&.respond_to?(:call)
          notification.instance_exec(apn, &method)
        elsif notification.params.try(:has_key?, :message)
          apn.alert = notification.params[:message]
        else
          raise ArgumentError, "No message for iOS delivery. Either include message in params or add the 'format' option in 'deliver_by :ios'."
        end
      end

      def bad_token?(response)
        response.status == "410" || (response.status == "400" && response.body["reason"] == "BadDeviceToken")
      end

      def development_pool
        self.class.development_connection_pool ||= new_connection_pool(development: true)
      end

      def production_pool
        self.class.production_connection_pool ||= new_connection_pool(development: false)
      end

      def new_connection_pool(development:)
        handler = proc do |connection|
          connection.on(:error) do |exception|
            Rails.logger.info "Apnotic exception raised: #{exception}"
            if config[:error_handler].respond_to?(:call)
              notification.instance_exec(exception, &config[:error_handler])
            end
          end
        end

        if development
          Apnotic::ConnectionPool.development(connection_pool_options, pool_options, &handler)
        else
          Apnotic::ConnectionPool.new(connection_pool_options, pool_options, &handler)
        end
      end

      def connection_pool_options
        {
          auth_method: :token,
          cert_path: StringIO.new(evaluate_option(:apns_key)),
          key_id: evaluate_option(:key_id),
          team_id: evaluate_option(:team_id)
        }
      end

      def pool_options
        {size: evaluate_option(:pool_size) || 5}
      end
    end
  end
end

ActiveSupport.run_load_hooks :noticed_delivery_methods_ios, Noticed::DeliveryMethods::Ios