Skip to content

Commit 6e9cd91

Browse files
authored
Implement SendEach, SendEachDryRun, SendEachForMulticast, (#544)
`SendEachForMulticastDryRun` 1. Add `SendEach`, `SendEachDryRun`, `SendEachForMulticast`, `SendEachForMulticastDryRun` 2. Deprecate `SendAll`, `SendAllDryRun`, `SendMulticast`, `SendMulticastDryRun` `SendEach` vs `SendAll` 1. `SendEach` sends one HTTP request to V1 Send endpoint for each message in the array. `SendAll` sends only one HTTP request to V1 Batch Send endpoint to send all messages in the array. 2. `SendEach` calls fcmClient.Send to send each message and constructs a SendResponse with the returned message id or error. `SendEach` uses sync.WaitGroup to execute all fcmClient.Send calls asynchronously and wait for all of them to complete and construct a BatchResponse with all SendResponses. Therefore, unlike `SendAll`, `SendEach` does not always returns an error for a total failure. It can also return a `BatchResponse` with only errors in it. `SendEachForMulticast` calls `SendEach` under the hood.
1 parent 1d24577 commit 6e9cd91

File tree

2 files changed

+589
-2
lines changed

2 files changed

+589
-2
lines changed

messaging/messaging_batch.go

Lines changed: 133 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"mime/multipart"
2828
"net/http"
2929
"net/textproto"
30+
"sync"
3031

3132
"firebase.google.com/go/v4/internal"
3233
)
@@ -87,14 +88,138 @@ type BatchResponse struct {
8788
Responses []*SendResponse
8889
}
8990

91+
// SendEach sends the messages in the given array via Firebase Cloud Messaging.
92+
//
93+
// The messages array may contain up to 500 messages. Unlike `SendAll()`, SendEach sends the entire
94+
// array of messages by making a single HTTP call for each message. The responses list
95+
// obtained from the return value corresponds to the order of the input messages. An error
96+
// from SendEach or a `BatchResponse` with all failures indicates a total failure -- i.e.
97+
// none of the messages in the list could be sent. Partial failures or no failures are only
98+
// indicated by a `BatchResponse` return value.
99+
func (c *fcmClient) SendEach(ctx context.Context, messages []*Message) (*BatchResponse, error) {
100+
return c.sendEachInBatch(ctx, messages, false)
101+
}
102+
103+
// SendEachDryRun sends the messages in the given array via Firebase Cloud Messaging in the
104+
// dry run (validation only) mode.
105+
//
106+
// This function does not actually deliver any messages to target devices. Instead, it performs all
107+
// the SDK-level and backend validations on the messages, and emulates the send operation.
108+
//
109+
// The messages array may contain up to 500 messages. Unlike `SendAllDryRun()`, SendEachDryRun sends
110+
// the entire array of messages by making a single HTTP call for each message. The responses list
111+
// obtained from the return value corresponds to the order of the input messages. An error
112+
// from SendEachDryRun or a `BatchResponse` with all failures indicates a total failure -- i.e.
113+
// none of the messages in the list could be sent. Partial failures or no failures are only
114+
// indicated by a `BatchResponse` return value.
115+
func (c *fcmClient) SendEachDryRun(ctx context.Context, messages []*Message) (*BatchResponse, error) {
116+
return c.sendEachInBatch(ctx, messages, true)
117+
}
118+
119+
// SendMulticast sends the given multicast message to all the FCM registration tokens specified.
120+
//
121+
// The tokens array in MulticastMessage may contain up to 500 tokens. SendMulticast uses the
122+
// `SendEach()` function to send the given message to all the target recipients. The
123+
// responses list obtained from the return value corresponds to the order of the input tokens. An error
124+
// from SendEachForMulticast or a `BatchResponse` with all failures indicates a total failure -- i.e.
125+
// none of the messages in the list could be sent. Partial failures or no failures are only
126+
// indicated by a `BatchResponse` return value.
127+
func (c *fcmClient) SendEachForMulticast(ctx context.Context, message *MulticastMessage) (*BatchResponse, error) {
128+
messages, err := toMessages(message)
129+
if err != nil {
130+
return nil, err
131+
}
132+
133+
return c.SendEach(ctx, messages)
134+
}
135+
136+
// SendEachForMulticastDryRun sends the given multicast message to all the specified FCM registration
137+
// tokens in the dry run (validation only) mode.
138+
//
139+
// This function does not actually deliver any messages to target devices. Instead, it performs all
140+
// the SDK-level and backend validations on the messages, and emulates the send operation.
141+
//
142+
// The tokens array in MulticastMessage may contain up to 500 tokens. SendEachForMulticastDryRunn uses the
143+
// `SendEachDryRun()` function to send the given message. The responses list obtained from
144+
// the return value corresponds to the order of the input tokens. An error from SendEachForMulticastDryRun
145+
// or a `BatchResponse` with all failures indicates a total failure -- i.e.
146+
// none of the messages in the list could be sent. Partial failures or no failures are only
147+
// indicated by a `BatchResponse` return value.
148+
func (c *fcmClient) SendEachForMulticastDryRun(ctx context.Context, message *MulticastMessage) (*BatchResponse, error) {
149+
messages, err := toMessages(message)
150+
if err != nil {
151+
return nil, err
152+
}
153+
154+
return c.SendEachDryRun(ctx, messages)
155+
}
156+
157+
func (c *fcmClient) sendEachInBatch(ctx context.Context, messages []*Message, dryRun bool) (*BatchResponse, error) {
158+
if len(messages) == 0 {
159+
return nil, errors.New("messages must not be nil or empty")
160+
}
161+
162+
if len(messages) > maxMessages {
163+
return nil, fmt.Errorf("messages must not contain more than %d elements", maxMessages)
164+
}
165+
166+
var responses []*SendResponse = make([]*SendResponse, len(messages))
167+
var wg sync.WaitGroup
168+
169+
for idx, m := range messages {
170+
if err := validateMessage(m); err != nil {
171+
return nil, fmt.Errorf("invalid message at index %d: %v", idx, err)
172+
}
173+
wg.Add(1)
174+
go func(idx int, m *Message, dryRun bool, responses []*SendResponse) {
175+
defer wg.Done()
176+
var resp string
177+
var err error
178+
if dryRun {
179+
resp, err = c.SendDryRun(ctx, m)
180+
} else {
181+
resp, err = c.Send(ctx, m)
182+
}
183+
if err == nil {
184+
responses[idx] = &SendResponse{
185+
Success: true,
186+
MessageID: resp,
187+
}
188+
} else {
189+
responses[idx] = &SendResponse{
190+
Success: false,
191+
Error: err,
192+
}
193+
}
194+
}(idx, m, dryRun, responses)
195+
}
196+
// Wait for all SendDryRun/Send calls to finish
197+
wg.Wait()
198+
199+
successCount := 0
200+
for _, r := range responses {
201+
if r.Success {
202+
successCount++
203+
}
204+
}
205+
206+
return &BatchResponse{
207+
Responses: responses,
208+
SuccessCount: successCount,
209+
FailureCount: len(responses) - successCount,
210+
}, nil
211+
}
212+
90213
// SendAll sends the messages in the given array via Firebase Cloud Messaging.
91214
//
92215
// The messages array may contain up to 500 messages. SendAll employs batching to send the entire
93-
// array of mssages as a single RPC call. Compared to the `Send()` function,
216+
// array of messages as a single RPC call. Compared to the `Send()` function,
94217
// this is a significantly more efficient way to send multiple messages. The responses list
95218
// obtained from the return value corresponds to the order of the input messages. An error from
96219
// SendAll indicates a total failure -- i.e. none of the messages in the array could be sent.
97220
// Partial failures are indicated by a `BatchResponse` return value.
221+
//
222+
// Deprecated: Use SendEach instead.
98223
func (c *fcmClient) SendAll(ctx context.Context, messages []*Message) (*BatchResponse, error) {
99224
return c.sendBatch(ctx, messages, false)
100225
}
@@ -106,11 +231,13 @@ func (c *fcmClient) SendAll(ctx context.Context, messages []*Message) (*BatchRes
106231
// the SDK-level and backend validations on the messages, and emulates the send operation.
107232
//
108233
// The messages array may contain up to 500 messages. SendAllDryRun employs batching to send the
109-
// entire array of mssages as a single RPC call. Compared to the `SendDryRun()` function, this
234+
// entire array of messages as a single RPC call. Compared to the `SendDryRun()` function, this
110235
// is a significantly more efficient way to validate sending multiple messages. The responses list
111236
// obtained from the return value corresponds to the order of the input messages. An error from
112237
// SendAllDryRun indicates a total failure -- i.e. none of the messages in the array could be sent
113238
// for validation. Partial failures are indicated by a `BatchResponse` return value.
239+
//
240+
// Deprecated: Use SendEachDryRun instead.
114241
func (c *fcmClient) SendAllDryRun(ctx context.Context, messages []*Message) (*BatchResponse, error) {
115242
return c.sendBatch(ctx, messages, true)
116243
}
@@ -122,6 +249,8 @@ func (c *fcmClient) SendAllDryRun(ctx context.Context, messages []*Message) (*Ba
122249
// responses list obtained from the return value corresponds to the order of the input tokens. An
123250
// error from SendMulticast indicates a total failure -- i.e. the message could not be sent to any
124251
// of the recipients. Partial failures are indicated by a `BatchResponse` return value.
252+
//
253+
// Deprecated: Use SendEachForMulticast instead.
125254
func (c *fcmClient) SendMulticast(ctx context.Context, message *MulticastMessage) (*BatchResponse, error) {
126255
messages, err := toMessages(message)
127256
if err != nil {
@@ -142,6 +271,8 @@ func (c *fcmClient) SendMulticast(ctx context.Context, message *MulticastMessage
142271
// the return value corresponds to the order of the input tokens. An error from SendMulticastDryRun
143272
// indicates a total failure -- i.e. none of the messages were sent to FCM for validation. Partial
144273
// failures are indicated by a `BatchResponse` return value.
274+
//
275+
// Deprecated: Use SendEachForMulticastDryRun instead.
145276
func (c *fcmClient) SendMulticastDryRun(ctx context.Context, message *MulticastMessage) (*BatchResponse, error) {
146277
messages, err := toMessages(message)
147278
if err != nil {

0 commit comments

Comments
 (0)