firebase-admin-sdk 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -21
- data/firebase-admin-sdk.gemspec +1 -0
- data/lib/firebase-admin-sdk.rb +21 -1
- data/lib/firebase/admin/app.rb +0 -6
- data/lib/firebase/admin/auth/client.rb +8 -0
- data/lib/firebase/admin/auth/token_verifier.rb +1 -1
- data/lib/firebase/admin/auth/user_info.rb +6 -6
- data/lib/firebase/admin/auth/user_manager.rb +2 -2
- data/lib/firebase/admin/auth/user_record.rb +5 -5
- data/lib/firebase/admin/config.rb +4 -2
- data/lib/firebase/admin/internal/http_client.rb +1 -0
- data/lib/firebase/admin/messaging/android_config.rb +77 -0
- data/lib/firebase/admin/messaging/android_fcm_options.rb +19 -0
- data/lib/firebase/admin/messaging/android_notification.rb +221 -0
- data/lib/firebase/admin/messaging/apns_config.rb +38 -0
- data/lib/firebase/admin/messaging/apns_fcm_options.rb +27 -0
- data/lib/firebase/admin/messaging/apns_payload.rb +28 -0
- data/lib/firebase/admin/messaging/aps.rb +82 -0
- data/lib/firebase/admin/messaging/aps_alert.rb +110 -0
- data/lib/firebase/admin/messaging/client.rb +181 -0
- data/lib/firebase/admin/messaging/critical_sound.rb +37 -0
- data/lib/firebase/admin/messaging/error.rb +36 -0
- data/lib/firebase/admin/messaging/error_info.rb +25 -0
- data/lib/firebase/admin/messaging/fcm_options.rb +19 -0
- data/lib/firebase/admin/messaging/light_settings.rb +34 -0
- data/lib/firebase/admin/messaging/message.rb +83 -0
- data/lib/firebase/admin/messaging/message_encoder.rb +355 -0
- data/lib/firebase/admin/messaging/multicast_message.rb +67 -0
- data/lib/firebase/admin/messaging/notification.rb +34 -0
- data/lib/firebase/admin/messaging/topic_management_response.rb +41 -0
- data/lib/firebase/admin/messaging/utils.rb +78 -0
- data/lib/firebase/admin/version.rb +1 -1
- metadata +36 -2
@@ -0,0 +1,37 @@
|
|
1
|
+
module Firebase
|
2
|
+
module Admin
|
3
|
+
module Messaging
|
4
|
+
# Critical alert sound configuration that can be included in an {APS}
|
5
|
+
class CriticalSound
|
6
|
+
# @return [String]
|
7
|
+
# The name of a sound file in the app's main bundle or in the `Library/Sounds` folder of the app's container
|
8
|
+
# directory. Specify the string "default" to play the system sound.
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
# @return [Boolean, nil]
|
12
|
+
# The critical alert flag. Set to `true` to enable the critical alert.
|
13
|
+
attr_accessor :critical
|
14
|
+
|
15
|
+
# @return [Float, nil]
|
16
|
+
# The volume for the critical alert's sound. Must be a value between 0.0 (silent) and 1.0 (full volume).
|
17
|
+
attr_accessor :volume
|
18
|
+
|
19
|
+
# Initializes a {CriticalSound}.
|
20
|
+
#
|
21
|
+
# @param [String] name
|
22
|
+
# The name of a sound file in the app's main bundle or in the `Library/Sounds` folder of teh app's container
|
23
|
+
# directory.
|
24
|
+
# @param [Boolean, nil] critical
|
25
|
+
# The critical alert flag (optional).
|
26
|
+
# @param [Float, nil] volume
|
27
|
+
# The volume for the critical alert's sound (optional). Must be a value between 0.0 (silent) and 1.0 (full
|
28
|
+
# volume).
|
29
|
+
def initialize(name: "default", critical: nil, volume: nil)
|
30
|
+
self.name = name
|
31
|
+
self.critical = critical
|
32
|
+
self.volume = volume
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Firebase
|
2
|
+
module Admin
|
3
|
+
module Messaging
|
4
|
+
# A base class for errors raised by the admin sdk messaging client.
|
5
|
+
class Error < Firebase::Admin::Error
|
6
|
+
attr_reader :info
|
7
|
+
|
8
|
+
def initialize(msg, info = nil)
|
9
|
+
@info = info
|
10
|
+
super(msg)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# No more information is available about this error.
|
15
|
+
class UnspecifiedError < Error; end
|
16
|
+
|
17
|
+
# Request parameters were invalid.
|
18
|
+
class InvalidArgumentError < Error; end
|
19
|
+
|
20
|
+
# A message targeted to an iOS device or a web push registration could not be sent.
|
21
|
+
# Check the validity of your development and production credentials.
|
22
|
+
class ThirdPartyAuthError < Error; end
|
23
|
+
|
24
|
+
# This error can be caused by exceeded message rate quota, exceeded device message rate quota, or
|
25
|
+
# exceeded topic message rate quota.
|
26
|
+
class QuotaExceededError < Error; end
|
27
|
+
|
28
|
+
# The authenticated sender ID is different from the sender ID for the registration token.
|
29
|
+
class SenderIdMismatchError < Error; end
|
30
|
+
|
31
|
+
# App instance was unregistered from FCM. This usually means that the token used is no longer valid and
|
32
|
+
# a new one must be used.
|
33
|
+
class UnregisteredError < Error; end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Firebase
|
2
|
+
module Admin
|
3
|
+
module Messaging
|
4
|
+
# Information on an error encountered when performing a topic management operation.
|
5
|
+
class ErrorInfo
|
6
|
+
# @return [Integer] The index of the registration token the error is related to.
|
7
|
+
attr_accessor :index
|
8
|
+
|
9
|
+
# @return [String] The description of the error encountered.
|
10
|
+
attr_accessor :reason
|
11
|
+
|
12
|
+
# Initializes an {ErrorInfo}.
|
13
|
+
#
|
14
|
+
# @param [Integer] index
|
15
|
+
# The index of the registration token the error is related to.
|
16
|
+
# @param [String] reason
|
17
|
+
# The description of the error encountered.
|
18
|
+
def initialize(index:, reason:)
|
19
|
+
self.index = index
|
20
|
+
self.reason = reason
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Firebase
|
2
|
+
module Admin
|
3
|
+
module Messaging
|
4
|
+
# Represents options for features provided by the FCM SDK.
|
5
|
+
class FCMOptions
|
6
|
+
# @return [String, nil] Label associated with the message's analytics data.
|
7
|
+
attr_accessor :analytics_label
|
8
|
+
|
9
|
+
# Initializes an {FCMOptions}.
|
10
|
+
#
|
11
|
+
# @param [String, nil] analytics_label
|
12
|
+
# The label associated with the message's analytics data (optional).
|
13
|
+
def initialize(analytics_label: nil)
|
14
|
+
self.analytics_label = analytics_label
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Firebase
|
2
|
+
module Admin
|
3
|
+
module Messaging
|
4
|
+
# Represents settings to control notification LED that can be included in an {AndroidNotification}.
|
5
|
+
class LightSettings
|
6
|
+
# @return [String]
|
7
|
+
# Sets color of the LED in `#rrggbb` or `#rrggbbaa` format.
|
8
|
+
attr_accessor :color
|
9
|
+
|
10
|
+
# @return [Numeric]
|
11
|
+
# Along with {light_off_duration}, defines the blink rate of LED flashes.
|
12
|
+
attr_accessor :light_on_duration
|
13
|
+
|
14
|
+
# @return [Numeric]
|
15
|
+
# Along with {light_on_duration}, defines the blink rate of LED flashes.
|
16
|
+
attr_accessor :light_off_duration
|
17
|
+
|
18
|
+
# Initializes a {LightSettings}.
|
19
|
+
#
|
20
|
+
# @param [String] color
|
21
|
+
# The color of the LED in `#rrggbb` or `#rrggbbaa` format.
|
22
|
+
# @param [Numeric] light_on_duration
|
23
|
+
# Along with {light_off_duration}, defines the blink rate of LED flashes.
|
24
|
+
# @param [Numeric] light_off_duration
|
25
|
+
# Along with {light_on_duration}, defines the blink rate of LED flashes.
|
26
|
+
def initialize(color:, light_on_duration:, light_off_duration:)
|
27
|
+
self.color = color
|
28
|
+
self.light_on_duration = light_on_duration
|
29
|
+
self.light_off_duration = light_off_duration
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Firebase
|
2
|
+
module Admin
|
3
|
+
module Messaging
|
4
|
+
# A message that can be sent via Firebase Cloud Messaging.
|
5
|
+
#
|
6
|
+
# Contains payload information as well as recipient information. In particular, the message must contain exactly
|
7
|
+
# one of token, topic or condition fields.
|
8
|
+
class Message
|
9
|
+
# @return [Hash<String, String>, nil]
|
10
|
+
# A hash of data fields (optional). All keys and values must be strings.
|
11
|
+
attr_accessor :data
|
12
|
+
|
13
|
+
# @return [Notification, nil]
|
14
|
+
# A {Notification} (optional).
|
15
|
+
attr_accessor :notification
|
16
|
+
|
17
|
+
# @return [AndroidConfig, nil]
|
18
|
+
# An {AndroidConfig} (optional).
|
19
|
+
attr_accessor :android
|
20
|
+
|
21
|
+
# @return [APNSConfig, nil]
|
22
|
+
# An {APNSConfig} (optional).
|
23
|
+
attr_accessor :apns
|
24
|
+
|
25
|
+
# @return [FCMOptions, nil]
|
26
|
+
# An {FCMOptions} (optional).
|
27
|
+
attr_accessor :fcm_options
|
28
|
+
|
29
|
+
# @return [String, nil]
|
30
|
+
# Registration token of the device to which the message should be sent (optional).
|
31
|
+
attr_accessor :token
|
32
|
+
|
33
|
+
# @return [String, nil]
|
34
|
+
# Name of the FCM topic to which the message should be sent (optional). Topic name may contain the `/topics/`
|
35
|
+
# prefix.
|
36
|
+
attr_accessor :topic
|
37
|
+
|
38
|
+
# @return [String, nil]
|
39
|
+
# The FCM condition to which the message should be sent (optional).
|
40
|
+
attr_accessor :condition
|
41
|
+
|
42
|
+
# Initializes a {Message}.
|
43
|
+
#
|
44
|
+
# @param [Hash<String, String>, nil] data
|
45
|
+
# A hash of data fields (optional). All keys and values must be strings.
|
46
|
+
# @param [Notification, nil] notification
|
47
|
+
# A {Notification} (optional).
|
48
|
+
# @param [AndroidConfig, nil] android
|
49
|
+
# An {AndroidConfig} (optional).
|
50
|
+
# @param [APNSConfig, nil] apns
|
51
|
+
# An {APNSConfig} (optional).
|
52
|
+
# @param [FCMOptions, nil] fcm_options
|
53
|
+
# An {FCMOptions} (optional).
|
54
|
+
# @param [String, nil] token
|
55
|
+
# A registration token of the device to send the message to (optional).
|
56
|
+
# @param [String, nil] topic
|
57
|
+
# The name of the FCM topic to send the message to (optional).
|
58
|
+
# The topic name may contain the `/topics/` prefix.
|
59
|
+
# @param [String, nil] condition
|
60
|
+
# The FCM condition to which the message should be sent (optional).
|
61
|
+
def initialize(
|
62
|
+
data: nil,
|
63
|
+
notification: nil,
|
64
|
+
android: nil,
|
65
|
+
apns: nil,
|
66
|
+
fcm_options: nil,
|
67
|
+
token: nil,
|
68
|
+
topic: nil,
|
69
|
+
condition: nil
|
70
|
+
)
|
71
|
+
self.data = data
|
72
|
+
self.notification = notification
|
73
|
+
self.android = android
|
74
|
+
self.apns = apns
|
75
|
+
self.fcm_options = fcm_options
|
76
|
+
self.token = token
|
77
|
+
self.topic = topic
|
78
|
+
self.condition = condition
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,355 @@
|
|
1
|
+
module Firebase
|
2
|
+
module Admin
|
3
|
+
module Messaging
|
4
|
+
class MessageEncoder
|
5
|
+
# Encodes a {Message}.
|
6
|
+
#
|
7
|
+
# @param [Message] message
|
8
|
+
# The message to encode.
|
9
|
+
# @return [Hash]
|
10
|
+
def encode(message)
|
11
|
+
raise ArgumentError, "message must be a Message" unless message.is_a?(Message)
|
12
|
+
result = {
|
13
|
+
android: encode_android(message.android),
|
14
|
+
apns: encode_apns(message.apns),
|
15
|
+
condition: check_string("Message.condition", message.condition, non_empty: true),
|
16
|
+
data: check_string_hash("Message.data", message.data),
|
17
|
+
notification: encode_notification(message.notification),
|
18
|
+
token: check_string("Message.token", message.token, non_empty: true),
|
19
|
+
topic: check_string("Message.topic", message.topic, non_empty: true),
|
20
|
+
fcm_options: encode_fcm_options(message.fcm_options)
|
21
|
+
}
|
22
|
+
result[:topic] = sanitize_topic_name(result[:topic])
|
23
|
+
result = remove_nil_values(result)
|
24
|
+
unless result.count { |k, _| [:token, :topic, :condition].include?(k) } == 1
|
25
|
+
raise ArgumentError, "Exactly one token, topic or condition must be specified"
|
26
|
+
end
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String, nil]
|
31
|
+
def sanitize_topic_name(topic, strip_prefix: true)
|
32
|
+
return nil unless topic
|
33
|
+
prefix = "/topics/"
|
34
|
+
if topic.start_with?(prefix)
|
35
|
+
topic = topic[prefix.length..]
|
36
|
+
end
|
37
|
+
unless /\A[a-zA-Z0-9\-_.~%]+\Z/.match?(topic)
|
38
|
+
raise ArgumentError, "Malformed topic name."
|
39
|
+
end
|
40
|
+
strip_prefix ? topic : "/topics/#{topic}"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @return [Hash, nil]
|
46
|
+
def encode_android(v)
|
47
|
+
return nil unless v
|
48
|
+
raise ArgumentError, "Message.android must be an AndroidConfig." unless v.is_a?(AndroidConfig)
|
49
|
+
result = {
|
50
|
+
collapse_key: check_string("AndroidConfig.collapse_key", v.collapse_key),
|
51
|
+
data: check_string_hash("AndroidConfig.data", v.data),
|
52
|
+
notification: encode_android_notification(v.notification),
|
53
|
+
priority: check_string("AndroidConfig.priority", v.priority, non_empty: true),
|
54
|
+
restricted_package_name: check_string("AndroidConfig.restricted_package_name", v.restricted_package_name),
|
55
|
+
ttl: encode_duration("AndroidConfig.ttl", v.ttl),
|
56
|
+
fcm_options: encode_android_fcm_options(v.fcm_options)
|
57
|
+
}
|
58
|
+
result = remove_nil_values(result)
|
59
|
+
if result.key?(:priority) && !%w[normal high].include?(result[:priority])
|
60
|
+
raise ArgumentError, "AndroidConfig.priority must be 'normal' or 'high'"
|
61
|
+
end
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Hash, nil]
|
66
|
+
def encode_android_notification(v)
|
67
|
+
return nil unless v
|
68
|
+
unless v.is_a?(AndroidNotification)
|
69
|
+
raise ArgumentError, "AndroidConfig.notification must be an AndroidNotification"
|
70
|
+
end
|
71
|
+
|
72
|
+
result = {
|
73
|
+
body: check_string("AndroidNotification.body", v.body),
|
74
|
+
body_loc_key: check_string("AndroidNotification.body_loc_key", v.body_loc_key),
|
75
|
+
body_loc_args: check_string_array("AndroidNotification.body_loc_args", v.body_loc_args),
|
76
|
+
click_action: check_string("AndroidNotification.click_action", v.click_action),
|
77
|
+
color: check_color("AndroidNotification.color", v.color, allow_alpha: true, required: false),
|
78
|
+
icon: check_string("AndroidNotification.icon", v.icon),
|
79
|
+
sound: check_string("AndroidNotification.sound", v.sound),
|
80
|
+
tag: check_string("AndroidNotification.tag", v.tag),
|
81
|
+
title: check_string("AndroidNotification.title", v.title),
|
82
|
+
title_loc_key: check_string("AndroidNotification.title_loc_key", v.title_loc_key),
|
83
|
+
title_loc_args: check_string_array("AndroidNotification.title_loc_args", v.title_loc_args),
|
84
|
+
channel_id: check_string("AndroidNotification.channel_id", v.channel_id),
|
85
|
+
image: check_string("AndroidNotification.image", v.image),
|
86
|
+
ticker: check_string("AndroidNotification.ticker", v.ticker),
|
87
|
+
sticky: v.sticky,
|
88
|
+
event_time: check_time("AndroidNotification.event_time", v.event_time),
|
89
|
+
local_only: v.local_only,
|
90
|
+
notification_priority: check_string("AndroidNotification.priority", v.priority, non_empty: true),
|
91
|
+
vibrate_timings: check_numeric_array("AndroidNotification.vibrate_timings", v.vibrate_timings),
|
92
|
+
default_vibrate_timings: v.default_vibrate_timings,
|
93
|
+
default_sound: v.default_sound,
|
94
|
+
default_light_settings: v.default_light_settings,
|
95
|
+
light_settings: encode_light_settings(v.light_settings),
|
96
|
+
visibility: check_string("AndroidNotification.visibility", v.visibility, non_empty: true),
|
97
|
+
notification_count: check_numeric("AndroidNotification.notification_count", v.notification_count)
|
98
|
+
}
|
99
|
+
result = remove_nil_values(result)
|
100
|
+
|
101
|
+
if result.key?(:body_loc_args) && !result.key?(:body_loc_key)
|
102
|
+
raise ArgumentError, "AndroidNotification.body_loc_key is required when specifying body_loc_args"
|
103
|
+
elsif result.key?(:title_loc_args) && !result.key?(:title_loc_key)
|
104
|
+
raise ArgumentError, "AndroidNotification.title_loc_key is required when specifying title_loc_args"
|
105
|
+
end
|
106
|
+
|
107
|
+
if (event_time = result[:event_time])
|
108
|
+
event_time = event_time.dup.utc unless event_time.utc?
|
109
|
+
result[:event_time] = event_time.strftime("%Y-%m-%dT%H:%M:%S.%6NZ")
|
110
|
+
end
|
111
|
+
|
112
|
+
if (priority = result[:notification_priority])
|
113
|
+
unless %w[min low default high max].include?(priority)
|
114
|
+
raise ArgumentError, "AndroidNotification.priority must be 'default', 'min', 'low', 'high' or 'max'."
|
115
|
+
end
|
116
|
+
result[:notification_priority] = "PRIORITY_#{priority.upcase}"
|
117
|
+
end
|
118
|
+
|
119
|
+
if (visibility = result[:visibility])
|
120
|
+
unless %w[private public secret].include?(visibility)
|
121
|
+
raise ArgumentError, "AndroidNotification.visibility must be 'private', 'public' or 'secret'"
|
122
|
+
end
|
123
|
+
result[:visibility] = visibility.upcase
|
124
|
+
end
|
125
|
+
|
126
|
+
if (vibrate_timings = result[:vibrate_timings])
|
127
|
+
vibrate_timing_strings = vibrate_timings.map do |t|
|
128
|
+
encode_duration("AndroidNotification.vibrate_timings", t)
|
129
|
+
end
|
130
|
+
result[:vibrate_timings] = vibrate_timing_strings
|
131
|
+
end
|
132
|
+
|
133
|
+
result
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [Hash, nil]
|
137
|
+
def encode_android_fcm_options(v)
|
138
|
+
return nil unless v
|
139
|
+
unless v.is_a?(AndroidFCMOptions)
|
140
|
+
raise ArgumentError, "AndroidConfig.fcm_options must be an AndroidFCMOptions"
|
141
|
+
end
|
142
|
+
result = {
|
143
|
+
analytics_label: check_analytics_label("AndroidFCMOptions.analytics_label", v.analytics_label)
|
144
|
+
}
|
145
|
+
remove_nil_values(result)
|
146
|
+
end
|
147
|
+
|
148
|
+
# @return [String, nil]
|
149
|
+
def encode_duration(label, value)
|
150
|
+
return nil unless value
|
151
|
+
raise ArgumentError, "#{label} must be a numeric duration in seconds" unless value.is_a?(Numeric)
|
152
|
+
raise ArgumentError, "#{label} must not be negative" if value < 0
|
153
|
+
to_seconds_string(value)
|
154
|
+
end
|
155
|
+
|
156
|
+
# @return [Hash, nil]
|
157
|
+
def encode_light_settings(v)
|
158
|
+
return nil unless v
|
159
|
+
raise ArgumentError, "AndroidNotification.light_settings must be a LightSettings." unless v.is_a?(LightSettings)
|
160
|
+
result = {
|
161
|
+
color: encode_color("LightSettings.color", v.color, allow_alpha: true),
|
162
|
+
light_on_duration: encode_duration("LightSettings.light_on_duration", v.light_on_duration),
|
163
|
+
light_off_duration: encode_duration("LightSettings.light_off_duration", v.light_off_duration)
|
164
|
+
}
|
165
|
+
result = remove_nil_values(result)
|
166
|
+
unless result.key?(:light_on_duration)
|
167
|
+
raise ArgumentError, "LightSettings.light_on_duration is required"
|
168
|
+
end
|
169
|
+
unless result.key?(:light_off_duration)
|
170
|
+
raise ArgumentError, "LightSettings.light_off_duration is required"
|
171
|
+
end
|
172
|
+
result
|
173
|
+
end
|
174
|
+
|
175
|
+
# @return [Hash]
|
176
|
+
def encode_color(label, value, allow_alpha: false)
|
177
|
+
value = check_color(label, value, allow_alpha: allow_alpha, required: true)
|
178
|
+
value += "FF" if value&.length == 7
|
179
|
+
r = value[1..2].to_i(16) / 255.0
|
180
|
+
g = value[3..4].to_i(16) / 255.0
|
181
|
+
b = value[5..6].to_i(16) / 255.0
|
182
|
+
a = value[7..8].to_i(16) / 255.0
|
183
|
+
{red: r, green: g, blue: b, alpha: a}
|
184
|
+
end
|
185
|
+
|
186
|
+
# @return [Hash, nil]
|
187
|
+
def encode_apns(apns)
|
188
|
+
return nil unless apns
|
189
|
+
raise ArgumentError, "Message.apns must be an APNSConfig" unless apns.is_a?(APNSConfig)
|
190
|
+
result = {
|
191
|
+
headers: check_string_hash("APNSConfig.headers", apns.headers),
|
192
|
+
payload: encode_apns_payload(apns.payload),
|
193
|
+
fcm_options: encode_apns_fcm_options(apns.fcm_options)
|
194
|
+
}
|
195
|
+
remove_nil_values(result)
|
196
|
+
end
|
197
|
+
|
198
|
+
# @return [Hash, nil]
|
199
|
+
def encode_apns_payload(payload)
|
200
|
+
return nil unless payload
|
201
|
+
raise ArgumentError, "APNSConfig.payload must be an APNSPayload" unless payload.is_a?(APNSPayload)
|
202
|
+
result = {
|
203
|
+
aps: encode_aps(payload.aps)
|
204
|
+
}
|
205
|
+
payload.data&.each do |k, v|
|
206
|
+
result[k] = v
|
207
|
+
end
|
208
|
+
remove_nil_values(result)
|
209
|
+
end
|
210
|
+
|
211
|
+
# @return [Hash, nil]
|
212
|
+
def encode_apns_fcm_options(options)
|
213
|
+
return nil unless options
|
214
|
+
raise ArgumentError, "APNSConfig.fcm_options must be an APNSFCMOptions" unless options.is_a?(APNSFCMOptions)
|
215
|
+
result = {
|
216
|
+
analytics_label: check_analytics_label("APNSFCMOptions.analytics_label", options.analytics_label),
|
217
|
+
image: check_string("APNSFCMOptions.image", options.image)
|
218
|
+
}
|
219
|
+
remove_nil_values(result)
|
220
|
+
end
|
221
|
+
|
222
|
+
# @return [Hash]
|
223
|
+
def encode_aps(aps)
|
224
|
+
raise ArgumentError, "APNSPayload.aps is required" unless aps
|
225
|
+
raise ArgumentError, "APNSPayload.aps must be an APS" unless aps.is_a?(APS)
|
226
|
+
result = {
|
227
|
+
alert: encode_aps_alert(aps.alert),
|
228
|
+
badge: check_numeric("APS.badge", aps.badge),
|
229
|
+
sound: encode_aps_sound(aps.sound),
|
230
|
+
category: check_string("APS.category", aps.category),
|
231
|
+
"thread-id": check_string("APS.thread_id", aps.thread_id)
|
232
|
+
}
|
233
|
+
|
234
|
+
result[:"content-available"] = 1 if aps.content_available
|
235
|
+
result[:"mutable-content"] = 1 if aps.mutable_content
|
236
|
+
|
237
|
+
if (custom_data = aps.custom_data)
|
238
|
+
raise ArgumentError, "APS.custom_data must be a hash" unless custom_data.is_a?(Hash)
|
239
|
+
custom_data.each do |k, v|
|
240
|
+
unless k.is_a?(String) || k.is_a?(Symbol)
|
241
|
+
raise ArgumentError, "APS.custom_data key #{k}, must be a string or symbol"
|
242
|
+
end
|
243
|
+
k = k.to_sym
|
244
|
+
raise ArgumentError, "Multiple specifications for #{k} in APS" if result.key?(k)
|
245
|
+
result[k] = v
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
remove_nil_values(result)
|
250
|
+
end
|
251
|
+
|
252
|
+
# @return [Hash, String, nil]
|
253
|
+
def encode_aps_alert(alert)
|
254
|
+
return nil unless alert
|
255
|
+
return alert if alert.is_a?(String)
|
256
|
+
raise ArgumentError, "APS.alert must be a string or an an APSAlert" unless alert.is_a?(APSAlert)
|
257
|
+
|
258
|
+
result = {
|
259
|
+
title: check_string("APSAlert.title", alert.title),
|
260
|
+
subtitle: check_string("APSAlert.subtitle", alert.subtitle),
|
261
|
+
body: check_string("APSAlert.body", alert.body),
|
262
|
+
"title-loc-key": check_string("APSAlert.title_loc_key", alert.title_loc_key),
|
263
|
+
"title-loc-args": check_string_array("APSAlert.title_loc_args", alert.title_loc_args),
|
264
|
+
"subtitle-loc-key": check_string("APSAlert.subtitle_loc_key", alert.subtitle_loc_key),
|
265
|
+
"subtitle-loc-args": check_string_array("APSAlert.subtitle_loc_args", alert.subtitle_loc_args),
|
266
|
+
"loc-key": check_string("APSAlert.loc_key", alert.loc_key),
|
267
|
+
"loc-args": check_string_array("ASPAlert.loc_args", alert.loc_args),
|
268
|
+
"action-loc-key": check_string("APSAlert.action_loc_key", alert.action_loc_key),
|
269
|
+
"launch-image": check_string("APSAlert.launch_image", alert.launch_image)
|
270
|
+
}
|
271
|
+
result = remove_nil_values(result)
|
272
|
+
|
273
|
+
if result.key?(:"loc-args") && !result.key?(:"loc-key")
|
274
|
+
raise ArgumentError, "APSAlert.loc_key is required when specifying loc_args"
|
275
|
+
elsif result.key?(:"title-loc-args") && !result.key?(:"title-loc-key")
|
276
|
+
raise ArgumentError, "APSAlert.title_loc_key is required when specifying title_loc_args"
|
277
|
+
elsif result.key?(:"subtitle-loc-args") && !result.key?(:"subtitle-loc-key")
|
278
|
+
raise ArgumentError, "APSAlert.subtitle_loc_key is required when specifying subtitle_loc_args"
|
279
|
+
end
|
280
|
+
|
281
|
+
if (custom_data = alert.custom_data)
|
282
|
+
raise ArgumentError, "APSAlert.custom_data must be a hash" unless custom_data.is_a?(Hash)
|
283
|
+
custom_data.each do |k, v|
|
284
|
+
unless k.is_a?(String) || k.is_a?(Symbol)
|
285
|
+
raise ArgumentError, "APSAlert.custom_data key #{k}, must be a string or symbol"
|
286
|
+
end
|
287
|
+
k = k.to_sym
|
288
|
+
result[k] = v
|
289
|
+
end
|
290
|
+
end
|
291
|
+
remove_nil_values(result)
|
292
|
+
end
|
293
|
+
|
294
|
+
# @return [Hash, String, nil]
|
295
|
+
def encode_aps_sound(sound)
|
296
|
+
return nil unless sound
|
297
|
+
return sound if sound.is_a?(String) && !sound.empty?
|
298
|
+
unless sound.is_a?(CriticalSound)
|
299
|
+
raise ArgumentError, "APS.sound must be a non-empty string or a CriticalSound"
|
300
|
+
end
|
301
|
+
|
302
|
+
result = {
|
303
|
+
name: check_string("CriticalSound.name", sound.name, non_empty: true),
|
304
|
+
volume: check_numeric("CriticalSound.volume", sound.volume)
|
305
|
+
}
|
306
|
+
|
307
|
+
result[:critical] = 1 if sound.critical
|
308
|
+
raise ArgumentError, "CriticalSound.name is required" if result[:name].nil?
|
309
|
+
|
310
|
+
if (volume = result[:volume])
|
311
|
+
raise ArgumentError, "CriticalSound.volume must be between [0,1]." unless volume >= 0 && volume <= 1
|
312
|
+
end
|
313
|
+
|
314
|
+
remove_nil_values(result)
|
315
|
+
end
|
316
|
+
|
317
|
+
# @return [Hash, nil]
|
318
|
+
def encode_notification(v)
|
319
|
+
return nil unless v
|
320
|
+
raise ArgumentError, "Message.notification must be a Notification" unless v.is_a?(Notification)
|
321
|
+
result = {
|
322
|
+
body: check_string("Notification.body", v.body),
|
323
|
+
title: check_string("Notification.title", v.title),
|
324
|
+
image: check_string("Notification.image", v.image)
|
325
|
+
}
|
326
|
+
remove_nil_values(result)
|
327
|
+
end
|
328
|
+
|
329
|
+
# @return [Hash, nil]
|
330
|
+
def encode_fcm_options(options)
|
331
|
+
return nil unless options
|
332
|
+
raise ArgumentError, "Message.fcm_options must be a FCMOptions." unless options.is_a?(FCMOptions)
|
333
|
+
result = {
|
334
|
+
analytics_label: check_analytics_label("Message.fcm_options", options.analytics_label)
|
335
|
+
}
|
336
|
+
remove_nil_values(result)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Remove nil values and empty collections from the specified hash.
|
340
|
+
# @return [Hash]
|
341
|
+
def remove_nil_values(hash)
|
342
|
+
hash.reject do |_, v|
|
343
|
+
if v.is_a?(Hash) || v.is_a?(Array)
|
344
|
+
v.empty?
|
345
|
+
else
|
346
|
+
v.nil?
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
include Utils
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|