firebase-admin-sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|