From cba18a690d5de6706b8151d47cef59dd893300cb Mon Sep 17 00:00:00 2001 From: Cyrille Hemidy Date: Thu, 27 Jun 2019 22:44:42 +0200 Subject: [PATCH 1/7] add analytics_label in FcmOptions + fix FcmOptions for WebpushFcmOptions --- messaging/messaging.go | 24 +++++++++++++++++++++--- messaging/messaging_test.go | 20 +++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/messaging/messaging.go b/messaging/messaging.go index b9e2698b..f9bd61c8 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -142,6 +142,7 @@ type Message struct { Android *AndroidConfig `json:"android,omitempty"` Webpush *WebpushConfig `json:"webpush,omitempty"` APNS *APNSConfig `json:"apns,omitempty"` + FcmOptions *FcmOptions `json:"fcm_options,omitempty"` Token string `json:"token,omitempty"` Topic string `json:"-"` Condition string `json:"condition,omitempty"` @@ -192,6 +193,7 @@ type AndroidConfig struct { RestrictedPackageName string `json:"restricted_package_name,omitempty"` Data map[string]string `json:"data,omitempty"` // if specified, overrides the Data field on Message type Notification *AndroidNotification `json:"notification,omitempty"` + FcmOptions *AndroidFcmOptions `json:"fcm_options,omitempty"` } // MarshalJSON marshals an AndroidConfig into JSON (for internal use only). @@ -268,6 +270,11 @@ type AndroidNotification struct { ChannelID string `json:"channel_id,omitempty"` } +// AndroidFcmOptions contains additional options for features provided by the FCM Android SDK. +type AndroidFcmOptions struct { + AnalyticsLabel string `json:"analytics_label,omitempty"` +} + // WebpushConfig contains messaging options specific to the WebPush protocol. // // See https://tools.ietf.org/html/rfc8030#section-5 for additional details, and supported @@ -276,7 +283,7 @@ type WebpushConfig struct { Headers map[string]string `json:"headers,omitempty"` Data map[string]string `json:"data,omitempty"` Notification *WebpushNotification `json:"notification,omitempty"` - FcmOptions *WebpushFcmOptions `json:"fcmOptions,omitempty"` + FcmOptions *WebpushFcmOptions `json:"fcm_options,omitempty"` } // WebpushNotificationAction represents an action that can be performed upon receiving a WebPush notification. @@ -392,8 +399,9 @@ type WebpushFcmOptions struct { // See https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html // for more details on supported headers and payload keys. type APNSConfig struct { - Headers map[string]string `json:"headers,omitempty"` - Payload *APNSPayload `json:"payload,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + Payload *APNSPayload `json:"payload,omitempty"` + FcmOptions *APNSFcmOptions `json:"fcm_options,omitempty"` } // APNSPayload is the payload that can be included in an APNS message. @@ -602,6 +610,16 @@ type ApsAlert struct { LaunchImage string `json:"launch-image,omitempty"` } +// APNSFcmOptions contains additional options for features provided by the FCM Aps SDK. +type APNSFcmOptions struct { + AnalyticsLabel string `json:"analytics_label,omitempty"` +} + +// FcmOptions contains additional options. +type FcmOptions struct { + AnalyticsLabel string `json:"analytics_label,omitempty"` +} + // ErrorInfo is a topic management error. type ErrorInfo struct { Index int diff --git a/messaging/messaging_test.go b/messaging/messaging_test.go index a792ba87..1eb8f451 100644 --- a/messaging/messaging_test.go +++ b/messaging/messaging_test.go @@ -82,6 +82,9 @@ var validMessages = []struct { "k1": "v1", "k2": "v2", }, + FcmOptions: &FcmOptions{ + AnalyticsLabel: "Analytics", + }, Topic: "test-topic", }, want: map[string]interface{}{ @@ -89,6 +92,9 @@ var validMessages = []struct { "k1": "v1", "k2": "v2", }, + "fcm_options": map[string]interface{}{ + "analytics_label": "Analytics", + }, "topic": "test-topic", }, }, @@ -153,6 +159,9 @@ var validMessages = []struct { ChannelID: "channel", }, TTL: &ttlWithNanos, + FcmOptions: &AndroidFcmOptions{ + AnalyticsLabel: "Analytics", + }, }, Topic: "test-topic", }, @@ -171,6 +180,9 @@ var validMessages = []struct { "channel_id": "channel", }, "ttl": "1.500000000s", + "fcm_options": map[string]interface{}{ + "analytics_label": "Analytics", + }, }, "topic": "test-topic", }, @@ -260,7 +272,7 @@ var validMessages = []struct { "k1": "v1", "k2": "v2", }, - "fcmOptions": map[string]interface{}{ + "fcm_options": map[string]interface{}{ "link": "https://link.com", }, }, @@ -308,6 +320,9 @@ var validMessages = []struct { "k2": true, }, }, + FcmOptions: &APNSFcmOptions{ + AnalyticsLabel: "Analytics", + }, }, Topic: "test-topic", }, @@ -327,6 +342,9 @@ var validMessages = []struct { "k1": "v1", "k2": true, }, + "fcm_options": map[string]interface{}{ + "analytics_label": "Analytics", + }, }, "topic": "test-topic", }, From 3114a789b379e006154a7da80e0f13ce404d9502 Mon Sep 17 00:00:00 2001 From: Cyrille Hemidy Date: Fri, 28 Jun 2019 23:24:03 +0200 Subject: [PATCH 2/7] fix comment --- messaging/messaging.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messaging/messaging.go b/messaging/messaging.go index f9bd61c8..4a2cafc9 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -615,7 +615,7 @@ type APNSFcmOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` } -// FcmOptions contains additional options. +// FcmOptions contains additional options to use across all platforms. type FcmOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` } From d3db96a5494b37bee367720c26ffb68b5c79391a Mon Sep 17 00:00:00 2001 From: Cyrille Hemidy Date: Fri, 19 Jul 2019 23:16:17 +0200 Subject: [PATCH 3/7] refactor fcm to FCM --- messaging/messaging.go | 18 +++++++++--------- messaging/messaging_test.go | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/messaging/messaging.go b/messaging/messaging.go index 4a2cafc9..6f7b190d 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -142,7 +142,7 @@ type Message struct { Android *AndroidConfig `json:"android,omitempty"` Webpush *WebpushConfig `json:"webpush,omitempty"` APNS *APNSConfig `json:"apns,omitempty"` - FcmOptions *FcmOptions `json:"fcm_options,omitempty"` + FCMOptions *FCMOptions `json:"fcm_options,omitempty"` Token string `json:"token,omitempty"` Topic string `json:"-"` Condition string `json:"condition,omitempty"` @@ -193,7 +193,7 @@ type AndroidConfig struct { RestrictedPackageName string `json:"restricted_package_name,omitempty"` Data map[string]string `json:"data,omitempty"` // if specified, overrides the Data field on Message type Notification *AndroidNotification `json:"notification,omitempty"` - FcmOptions *AndroidFcmOptions `json:"fcm_options,omitempty"` + FCMOptions *AndroidFCMOptions `json:"fcm_options,omitempty"` } // MarshalJSON marshals an AndroidConfig into JSON (for internal use only). @@ -270,8 +270,8 @@ type AndroidNotification struct { ChannelID string `json:"channel_id,omitempty"` } -// AndroidFcmOptions contains additional options for features provided by the FCM Android SDK. -type AndroidFcmOptions struct { +// AndroidFCMOptions contains additional options for features provided by the FCM Android SDK. +type AndroidFCMOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` } @@ -401,7 +401,7 @@ type WebpushFcmOptions struct { type APNSConfig struct { Headers map[string]string `json:"headers,omitempty"` Payload *APNSPayload `json:"payload,omitempty"` - FcmOptions *APNSFcmOptions `json:"fcm_options,omitempty"` + FCMOptions *APNSFCMOptions `json:"fcm_options,omitempty"` } // APNSPayload is the payload that can be included in an APNS message. @@ -610,13 +610,13 @@ type ApsAlert struct { LaunchImage string `json:"launch-image,omitempty"` } -// APNSFcmOptions contains additional options for features provided by the FCM Aps SDK. -type APNSFcmOptions struct { +// APNSFCMOptions contains additional options for features provided by the FCM Aps SDK. +type APNSFCMOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` } -// FcmOptions contains additional options to use across all platforms. -type FcmOptions struct { +// FCMOptions contains additional options to use across all platforms. +type FCMOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` } diff --git a/messaging/messaging_test.go b/messaging/messaging_test.go index 1eb8f451..be6f1701 100644 --- a/messaging/messaging_test.go +++ b/messaging/messaging_test.go @@ -82,7 +82,7 @@ var validMessages = []struct { "k1": "v1", "k2": "v2", }, - FcmOptions: &FcmOptions{ + FCMOptions: &FCMOptions{ AnalyticsLabel: "Analytics", }, Topic: "test-topic", @@ -159,7 +159,7 @@ var validMessages = []struct { ChannelID: "channel", }, TTL: &ttlWithNanos, - FcmOptions: &AndroidFcmOptions{ + FCMOptions: &AndroidFCMOptions{ AnalyticsLabel: "Analytics", }, }, @@ -320,7 +320,7 @@ var validMessages = []struct { "k2": true, }, }, - FcmOptions: &APNSFcmOptions{ + FCMOptions: &APNSFCMOptions{ AnalyticsLabel: "Analytics", }, }, From d2bf2c3e98007c86d011b53e820f41836a187128 Mon Sep 17 00:00:00 2001 From: Cyrille Hemidy Date: Sat, 20 Jul 2019 00:38:08 +0200 Subject: [PATCH 4/7] add image option in notification --- messaging/messaging.go | 3 +++ messaging/messaging_test.go | 40 ++++++++++++++++++++++++++++++++++++ messaging/messaging_utils.go | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/messaging/messaging.go b/messaging/messaging.go index 6f7b190d..4b6fd9d5 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -183,6 +183,7 @@ func (m *Message) UnmarshalJSON(b []byte) error { type Notification struct { Title string `json:"title,omitempty"` Body string `json:"body,omitempty"` + Image string `json:"image,omitempty"` } // AndroidConfig contains messaging options specific to the Android platform. @@ -268,6 +269,7 @@ type AndroidNotification struct { TitleLocKey string `json:"title_loc_key,omitempty"` TitleLocArgs []string `json:"title_loc_args,omitempty"` ChannelID string `json:"channel_id,omitempty"` + Image string `json:"image,omitempty"` } // AndroidFCMOptions contains additional options for features provided by the FCM Android SDK. @@ -613,6 +615,7 @@ type ApsAlert struct { // APNSFCMOptions contains additional options for features provided by the FCM Aps SDK. type APNSFCMOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` + Image string `json:"image,omitempty"` } // FCMOptions contains additional options to use across all platforms. diff --git a/messaging/messaging_test.go b/messaging/messaging_test.go index be6f1701..607a03c8 100644 --- a/messaging/messaging_test.go +++ b/messaging/messaging_test.go @@ -104,6 +104,7 @@ var validMessages = []struct { Notification: &Notification{ Title: "t", Body: "b", + Image: "http://image.jpg", }, Topic: "test-topic", }, @@ -111,6 +112,7 @@ var validMessages = []struct { "notification": map[string]interface{}{ "title": "t", "body": "b", + "image": "http://image.jpg", }, "topic": "test-topic", }, @@ -157,6 +159,7 @@ var validMessages = []struct { BodyLocKey: "blk", BodyLocArgs: []string{"b1", "b2"}, ChannelID: "channel", + Image: "http://image.jpg", }, TTL: &ttlWithNanos, FCMOptions: &AndroidFCMOptions{ @@ -178,6 +181,7 @@ var validMessages = []struct { "body_loc_key": "blk", "body_loc_args": []interface{}{"b1", "b2"}, "channel_id": "channel", + "image": "http://image.jpg", }, "ttl": "1.500000000s", "fcm_options": map[string]interface{}{ @@ -322,6 +326,7 @@ var validMessages = []struct { }, FCMOptions: &APNSFCMOptions{ AnalyticsLabel: "Analytics", + Image: "http://image.jpg", }, }, Topic: "test-topic", @@ -344,6 +349,7 @@ var validMessages = []struct { }, "fcm_options": map[string]interface{}{ "analytics_label": "Analytics", + "image": "http://image.jpg", }, }, "topic": "test-topic", @@ -525,6 +531,16 @@ var invalidMessages = []struct { }, want: "malformed topic name", }, + { + name: "InvalidNotificationImage", + req: &Message{ + Notification: &Notification{ + Image: "image.jpg", + }, + Topic: "topic", + }, + want: `invalid image URL: "image.jpg"`, + }, { name: "InvalidAndroidTTL", req: &Message{ @@ -593,6 +609,18 @@ var invalidMessages = []struct { }, want: "bodyLocKey is required when specifying bodyLocArgs", }, + { + name: "InvalidAndroidImage", + req: &Message{ + Android: &AndroidConfig{ + Notification: &AndroidNotification{ + Image: "image.jpg", + }, + }, + Topic: "topic", + }, + want: `invalid image URL: "image.jpg"`, + }, { name: "APNSMultipleAps", req: &Message{ @@ -688,6 +716,18 @@ var invalidMessages = []struct { }, want: "locKey is required when specifying locArgs", }, + { + name: "InvalidAPNSImage", + req: &Message{ + APNS: &APNSConfig{ + FCMOptions: &APNSFCMOptions{ + Image: "image.jpg", + }, + }, + Topic: "topic", + }, + want: `invalid image URL: "image.jpg"`, + }, { name: "MultipleSoundSpecifications", req: &Message{ diff --git a/messaging/messaging_utils.go b/messaging/messaging_utils.go index 6fd55719..e552e409 100644 --- a/messaging/messaging_utils.go +++ b/messaging/messaging_utils.go @@ -44,6 +44,11 @@ func validateMessage(message *Message) error { } } + // validate Notification + if err := validateNotification(message.Notification); err != nil { + return err + } + // validate AndroidConfig if err := validateAndroidConfig(message.Android); err != nil { return err @@ -58,6 +63,21 @@ func validateMessage(message *Message) error { return validateAPNSConfig(message.APNS) } +func validateNotification(notification *Notification) error { + if notification == nil { + return nil + } + + if notification.Image != "" { + image := notification.Image + _, err := url.ParseRequestURI(image) + if err != nil { + return fmt.Errorf("invalid image URL: %q", image) + } + } + return nil +} + func validateAndroidConfig(config *AndroidConfig) error { if config == nil { return nil @@ -69,6 +89,7 @@ func validateAndroidConfig(config *AndroidConfig) error { if config.Priority != "" && config.Priority != "normal" && config.Priority != "high" { return fmt.Errorf("priority must be 'normal' or 'high'") } + // validate AndroidNotification return validateAndroidNotification(config.Notification) } @@ -86,11 +107,28 @@ func validateAndroidNotification(notification *AndroidNotification) error { if len(notification.BodyLocArgs) > 0 && notification.BodyLocKey == "" { return fmt.Errorf("bodyLocKey is required when specifying bodyLocArgs") } + if notification.Image != "" { + image := notification.Image + _, err := url.ParseRequestURI(image) + if err != nil { + return fmt.Errorf("invalid image URL: %q", image) + } + } return nil } func validateAPNSConfig(config *APNSConfig) error { if config != nil { + // validate FCMOptions + if config.FCMOptions != nil { + if config.FCMOptions.Image != "" { + image := config.FCMOptions.Image + _, err := url.ParseRequestURI(image) + if err != nil { + return fmt.Errorf("invalid image URL: %q", image) + } + } + } return validateAPNSPayload(config.Payload) } return nil From 1bf903696274d03ab3a43f46e35ea93c0d70b7b7 Mon Sep 17 00:00:00 2001 From: Cyrille Hemidy Date: Sat, 20 Jul 2019 00:52:20 +0200 Subject: [PATCH 5/7] fix merge issue --- messaging/messaging.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/messaging/messaging.go b/messaging/messaging.go index 7a622ab7..1aaa2847 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -283,11 +283,6 @@ type AndroidFCMOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` } -// AndroidFCMOptions contains additional options for features provided by the FCM Android SDK. -type AndroidFCMOptions struct { - AnalyticsLabel string `json:"analytics_label,omitempty"` -} - // WebpushConfig contains messaging options specific to the WebPush protocol. // // See https://tools.ietf.org/html/rfc8030#section-5 for additional details, and supported From 6daf27216fe33b8d730aa568cceca5b0ef3a5fb1 Mon Sep 17 00:00:00 2001 From: Cyrille Hemidy Date: Thu, 12 Sep 2019 16:59:26 +0200 Subject: [PATCH 6/7] fix ImageURL and inline error check --- messaging/messaging.go | 2 +- messaging/messaging_test.go | 4 ++-- messaging/messaging_utils.go | 13 +++++-------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/messaging/messaging.go b/messaging/messaging.go index 1aaa2847..6dbf2621 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -275,7 +275,7 @@ type AndroidNotification struct { TitleLocKey string `json:"title_loc_key,omitempty"` TitleLocArgs []string `json:"title_loc_args,omitempty"` ChannelID string `json:"channel_id,omitempty"` - Image string `json:"image,omitempty"` + ImageURL string `json:"image,omitempty"` } // AndroidFCMOptions contains additional options for features provided by the FCM Android SDK. diff --git a/messaging/messaging_test.go b/messaging/messaging_test.go index 39f6c906..4d6a9e09 100644 --- a/messaging/messaging_test.go +++ b/messaging/messaging_test.go @@ -159,7 +159,7 @@ var validMessages = []struct { BodyLocKey: "blk", BodyLocArgs: []string{"b1", "b2"}, ChannelID: "channel", - Image: "http://image.jpg", + ImageURL: "http://image.jpg", }, TTL: &ttlWithNanos, FCMOptions: &AndroidFCMOptions{ @@ -614,7 +614,7 @@ var invalidMessages = []struct { req: &Message{ Android: &AndroidConfig{ Notification: &AndroidNotification{ - Image: "image.jpg", + ImageURL: "image.jpg", }, }, Topic: "topic", diff --git a/messaging/messaging_utils.go b/messaging/messaging_utils.go index e552e409..ea8c7a00 100644 --- a/messaging/messaging_utils.go +++ b/messaging/messaging_utils.go @@ -70,8 +70,7 @@ func validateNotification(notification *Notification) error { if notification.Image != "" { image := notification.Image - _, err := url.ParseRequestURI(image) - if err != nil { + if _, err := url.ParseRequestURI(image); err != nil { return fmt.Errorf("invalid image URL: %q", image) } } @@ -107,10 +106,9 @@ func validateAndroidNotification(notification *AndroidNotification) error { if len(notification.BodyLocArgs) > 0 && notification.BodyLocKey == "" { return fmt.Errorf("bodyLocKey is required when specifying bodyLocArgs") } - if notification.Image != "" { - image := notification.Image - _, err := url.ParseRequestURI(image) - if err != nil { + if notification.ImageURL != "" { + image := notification.ImageURL + if _, err := url.ParseRequestURI(image); err != nil { return fmt.Errorf("invalid image URL: %q", image) } } @@ -123,8 +121,7 @@ func validateAPNSConfig(config *APNSConfig) error { if config.FCMOptions != nil { if config.FCMOptions.Image != "" { image := config.FCMOptions.Image - _, err := url.ParseRequestURI(image) - if err != nil { + if _, err := url.ParseRequestURI(image); err != nil { return fmt.Errorf("invalid image URL: %q", image) } } From 66ecfb073ce8881965ff3003b425f4de09a61e91 Mon Sep 17 00:00:00 2001 From: Cyrille Hemidy Date: Thu, 12 Sep 2019 21:40:22 +0200 Subject: [PATCH 7/7] fix ImageURL and validattion --- messaging/messaging.go | 8 ++++---- messaging/messaging_test.go | 12 ++++++------ messaging/messaging_utils.go | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/messaging/messaging.go b/messaging/messaging.go index 6dbf2621..257e95a9 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -187,9 +187,9 @@ func (m *Message) UnmarshalJSON(b []byte) error { // Notification is the basic notification template to use across all platforms. type Notification struct { - Title string `json:"title,omitempty"` - Body string `json:"body,omitempty"` - Image string `json:"image,omitempty"` + Title string `json:"title,omitempty"` + Body string `json:"body,omitempty"` + ImageURL string `json:"image,omitempty"` } // AndroidConfig contains messaging options specific to the Android platform. @@ -621,7 +621,7 @@ type ApsAlert struct { // APNSFCMOptions contains additional options for features provided by the FCM Aps SDK. type APNSFCMOptions struct { AnalyticsLabel string `json:"analytics_label,omitempty"` - Image string `json:"image,omitempty"` + ImageURL string `json:"image,omitempty"` } // FCMOptions contains additional options to use across all platforms. diff --git a/messaging/messaging_test.go b/messaging/messaging_test.go index 4d6a9e09..c89f0414 100644 --- a/messaging/messaging_test.go +++ b/messaging/messaging_test.go @@ -102,9 +102,9 @@ var validMessages = []struct { name: "NotificationMessage", req: &Message{ Notification: &Notification{ - Title: "t", - Body: "b", - Image: "http://image.jpg", + Title: "t", + Body: "b", + ImageURL: "http://image.jpg", }, Topic: "test-topic", }, @@ -326,7 +326,7 @@ var validMessages = []struct { }, FCMOptions: &APNSFCMOptions{ AnalyticsLabel: "Analytics", - Image: "http://image.jpg", + ImageURL: "http://image.jpg", }, }, Topic: "test-topic", @@ -535,7 +535,7 @@ var invalidMessages = []struct { name: "InvalidNotificationImage", req: &Message{ Notification: &Notification{ - Image: "image.jpg", + ImageURL: "image.jpg", }, Topic: "topic", }, @@ -721,7 +721,7 @@ var invalidMessages = []struct { req: &Message{ APNS: &APNSConfig{ FCMOptions: &APNSFCMOptions{ - Image: "image.jpg", + ImageURL: "image.jpg", }, }, Topic: "topic", diff --git a/messaging/messaging_utils.go b/messaging/messaging_utils.go index ea8c7a00..dcb3a261 100644 --- a/messaging/messaging_utils.go +++ b/messaging/messaging_utils.go @@ -68,8 +68,8 @@ func validateNotification(notification *Notification) error { return nil } - if notification.Image != "" { - image := notification.Image + image := notification.ImageURL + if image != "" { if _, err := url.ParseRequestURI(image); err != nil { return fmt.Errorf("invalid image URL: %q", image) } @@ -106,8 +106,8 @@ func validateAndroidNotification(notification *AndroidNotification) error { if len(notification.BodyLocArgs) > 0 && notification.BodyLocKey == "" { return fmt.Errorf("bodyLocKey is required when specifying bodyLocArgs") } - if notification.ImageURL != "" { - image := notification.ImageURL + image := notification.ImageURL + if image != "" { if _, err := url.ParseRequestURI(image); err != nil { return fmt.Errorf("invalid image URL: %q", image) } @@ -119,8 +119,8 @@ func validateAPNSConfig(config *APNSConfig) error { if config != nil { // validate FCMOptions if config.FCMOptions != nil { - if config.FCMOptions.Image != "" { - image := config.FCMOptions.Image + image := config.FCMOptions.ImageURL + if image != "" { if _, err := url.ParseRequestURI(image); err != nil { return fmt.Errorf("invalid image URL: %q", image) }