Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 49 additions & 19 deletions internal/database/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,66 @@ import (
)

type Notification struct {
ID string `gorm:"type:text;primaryKey"`
Title string `gorm:"type:text;not null"`
Message string `gorm:"type:text;not null"`
URL *string `gorm:"type:text"`
ID string `gorm:"type:text;primaryKey"`

Title string `gorm:"type:text;not null"`
Body string `gorm:"type:text;not null"`
Comment thread
kantacky marked this conversation as resolved.
ImageURL *string `gorm:"type:text"`
AnalyticsLabel *string `gorm:"type:text"`
APNsBadge *int `gorm:"type:integer"`
APNsSound *string `gorm:"type:text"`
APNsContentAvailable *bool `gorm:"type:boolean"`
AndroidChannelID *string `gorm:"type:text"`
AndroidPriority *string `gorm:"type:text"`
AndroidTTLSeconds *int `gorm:"type:integer"`
WebpushLink *string `gorm:"type:text"`

URL *string `gorm:"type:text"`

NotifyAfter time.Time `gorm:"type:timestamptz;not null;index"`
NotifyBefore time.Time `gorm:"type:timestamptz;not null;index"`
IsNotified bool `gorm:"type:boolean;not null;default:false;index"`
}

func (n *Notification) ToDomain(targetUserIDs []string) domain.Notification {
return domain.Notification{
ID: n.ID,
Title: n.Title,
Message: n.Message,
URL: n.URL,
NotifyAfter: n.NotifyAfter,
NotifyBefore: n.NotifyBefore,
IsNotified: n.IsNotified,
TargetUserIDs: targetUserIDs,
ID: n.ID,
Title: n.Title,
Body: n.Body,
ImageURL: n.ImageURL,
AnalyticsLabel: n.AnalyticsLabel,
APNsBadge: n.APNsBadge,
APNsSound: n.APNsSound,
APNsContentAvailable: n.APNsContentAvailable,
AndroidChannelID: n.AndroidChannelID,
AndroidPriority: n.AndroidPriority,
AndroidTTLSeconds: n.AndroidTTLSeconds,
WebpushLink: n.WebpushLink,
URL: n.URL,
NotifyAfter: n.NotifyAfter,
NotifyBefore: n.NotifyBefore,
IsNotified: n.IsNotified,
TargetUserIDs: targetUserIDs,
}
}

func NotificationFromDomain(n domain.Notification) Notification {
return Notification{
ID: n.ID,
Title: n.Title,
Message: n.Message,
URL: n.URL,
NotifyAfter: n.NotifyAfter,
NotifyBefore: n.NotifyBefore,
IsNotified: n.IsNotified,
ID: n.ID,
Title: n.Title,
Body: n.Body,
ImageURL: n.ImageURL,
AnalyticsLabel: n.AnalyticsLabel,
APNsBadge: n.APNsBadge,
APNsSound: n.APNsSound,
APNsContentAvailable: n.APNsContentAvailable,
AndroidChannelID: n.AndroidChannelID,
AndroidPriority: n.AndroidPriority,
AndroidTTLSeconds: n.AndroidTTLSeconds,
WebpushLink: n.WebpushLink,
URL: n.URL,
NotifyAfter: n.NotifyAfter,
NotifyBefore: n.NotifyBefore,
IsNotified: n.IsNotified,
}
}
27 changes: 20 additions & 7 deletions internal/domain/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@ package domain
import "time"

type Notification struct {
ID string
Title string
Message string
URL *string
NotifyAfter time.Time
NotifyBefore time.Time
IsNotified bool
ID string

Title string
Body string
ImageURL *string
AnalyticsLabel *string
APNsBadge *int
APNsSound *string
APNsContentAvailable *bool
AndroidChannelID *string
AndroidPriority *string
AndroidTTLSeconds *int
WebpushLink *string

URL *string

NotifyAfter time.Time
NotifyBefore time.Time
IsNotified bool

TargetUserIDs []string
}
48 changes: 33 additions & 15 deletions internal/handler/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,23 @@ func toDomainFCMToken(req api.FCMTokenRequest) domain.FCMToken {

func toAPINotification(n domain.Notification) api.Notification {
return api.Notification{
Id: n.ID,
Title: n.Title,
Body: n.Message,
Url: n.URL,
NotifyAfter: n.NotifyAfter,
NotifyBefore: n.NotifyBefore,
IsNotified: n.IsNotified,
TargetUserIds: n.TargetUserIDs,
Id: n.ID,
Title: n.Title,
Body: n.Body,
ImageUrl: n.ImageURL,
AnalyticsLabel: n.AnalyticsLabel,
ApnsSound: n.APNsSound,
ApnsBadge: n.APNsBadge,
ApnsContentAvailable: n.APNsContentAvailable,
AndroidChannelId: n.AndroidChannelID,
AndroidPriority: n.AndroidPriority,
AndroidTtlSeconds: n.AndroidTTLSeconds,
WebpushLink: n.WebpushLink,
Url: n.URL,
NotifyAfter: n.NotifyAfter,
NotifyBefore: n.NotifyBefore,
IsNotified: n.IsNotified,
TargetUserIds: n.TargetUserIDs,
}
}

Expand All @@ -104,13 +113,22 @@ func toAPINotifications(notifications []domain.Notification) []api.Notification

func toDomainNotification(id string, req api.NotificationRequest) domain.Notification {
return domain.Notification{
ID: id,
Title: req.Title,
Message: req.Body,
URL: req.Url,
NotifyAfter: req.NotifyAfter,
NotifyBefore: req.NotifyBefore,
TargetUserIDs: req.TargetUserIds,
ID: id,
Title: req.Title,
Body: req.Body,
ImageURL: req.ImageUrl,
AnalyticsLabel: req.AnalyticsLabel,
APNsSound: req.ApnsSound,
APNsBadge: req.ApnsBadge,
APNsContentAvailable: req.ApnsContentAvailable,
AndroidChannelID: req.AndroidChannelId,
AndroidPriority: req.AndroidPriority,
AndroidTTLSeconds: req.AndroidTtlSeconds,
WebpushLink: req.WebpushLink,
URL: req.Url,
NotifyAfter: req.NotifyAfter,
NotifyBefore: req.NotifyBefore,
TargetUserIDs: req.TargetUserIds,
}
}

Expand Down
75 changes: 69 additions & 6 deletions internal/service/notification_dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service
import (
"context"
"log"
"time"

"firebase.google.com/go/v4/messaging"
"github.com/fun-dotto/user-api/internal/domain"
Expand Down Expand Up @@ -87,16 +88,34 @@ func (s *NotificationService) sendToTokens(ctx context.Context, n domain.Notific
data["url"] = *n.URL
}

notification := &messaging.Notification{
Title: n.Title,
Body: n.Body,
}
if n.ImageURL != nil {
notification.ImageURL = *n.ImageURL
}

var fcmOptions *messaging.FCMOptions
if n.AnalyticsLabel != nil {
fcmOptions = &messaging.FCMOptions{AnalyticsLabel: *n.AnalyticsLabel}
}

androidConfig := buildAndroidConfig(n)
apnsConfig := buildAPNSConfig(n)
webpushConfig := buildWebpushConfig(n)

totalSuccess := 0
for start := 0; start < len(tokens); start += fcmMulticastBatchSize {
end := min(start+fcmMulticastBatchSize, len(tokens))
msg := &messaging.MulticastMessage{
Tokens: tokens[start:end],
Notification: &messaging.Notification{
Title: n.Title,
Body: n.Message,
},
Data: data,
Tokens: tokens[start:end],
Notification: notification,
Data: data,
Android: androidConfig,
APNS: apnsConfig,
Webpush: webpushConfig,
FCMOptions: fcmOptions,
}
resp, err := s.messagingClient.SendEachForMulticast(ctx, msg)
if err != nil {
Expand All @@ -113,3 +132,47 @@ func (s *NotificationService) sendToTokens(ctx context.Context, n domain.Notific
}
return totalSuccess, nil
}

func buildAndroidConfig(n domain.Notification) *messaging.AndroidConfig {
if n.AndroidChannelID == nil && n.AndroidPriority == nil && n.AndroidTTLSeconds == nil {
return nil
}
cfg := &messaging.AndroidConfig{}
if n.AndroidPriority != nil {
cfg.Priority = *n.AndroidPriority
}
if n.AndroidTTLSeconds != nil {
ttl := time.Duration(*n.AndroidTTLSeconds) * time.Second
cfg.TTL = &ttl
}
if n.AndroidChannelID != nil {
cfg.Notification = &messaging.AndroidNotification{ChannelID: *n.AndroidChannelID}
}
return cfg
}

func buildAPNSConfig(n domain.Notification) *messaging.APNSConfig {
if n.APNsBadge == nil && n.APNsSound == nil && n.APNsContentAvailable == nil {
return nil
}
aps := &messaging.Aps{}
if n.APNsBadge != nil {
aps.Badge = n.APNsBadge
}
if n.APNsSound != nil {
aps.Sound = *n.APNsSound
}
if n.APNsContentAvailable != nil {
aps.ContentAvailable = *n.APNsContentAvailable
}
return &messaging.APNSConfig{Payload: &messaging.APNSPayload{Aps: aps}}
}

func buildWebpushConfig(n domain.Notification) *messaging.WebpushConfig {
if n.WebpushLink == nil {
return nil
}
return &messaging.WebpushConfig{
FCMOptions: &messaging.WebpushFCMOptions{Link: *n.WebpushLink},
}
}
Loading