Skip to content

Commit 74c9bd5

Browse files
Merge dev into master
2 parents 02300a8 + 2670a8e commit 74c9bd5

File tree

6 files changed

+732
-18
lines changed

6 files changed

+732
-18
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ For more information, visit the
2626

2727
## Installation
2828

29-
To install Firebase Admin Go SDK, simply execute the following command
30-
in a terminal from your `$GOPATH`:
29+
The Firebase Admin Go SDK can be installed using the `go install` utility:
3130

3231
```
33-
go get firebase.google.com/go
32+
# Install the latest version:
33+
go install firebase.google.com/go/v4@latest
34+
35+
# Or install a specific version:
36+
go install firebase.google.com/go/[email protected]
3437
```
3538

3639
## Contributing

auth/token_verifier.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ func (tv *tokenVerifier) VerifyToken(ctx context.Context, token string, isEmulat
174174
return payload, nil
175175
}
176176

177-
// Verifying the signature requires syncronized access to a key cache and
177+
// Verifying the signature requires synchronized access to a key cache and
178178
// potentially issues an http request. Therefore we do it last.
179179
if err := tv.verifySignature(ctx, token); err != nil {
180180
return nil, err

firebase.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import (
3939
var defaultAuthOverrides = make(map[string]interface{})
4040

4141
// Version of the Firebase Go Admin SDK.
42-
const Version = "4.11.0"
42+
const Version = "4.12.0"
4343

4444
// firebaseEnvName is the name of the environment variable with the Config.
4545
const firebaseEnvName = "FIREBASE_CONFIG"

integration/messaging/messaging_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,130 @@ func TestSendInvalidToken(t *testing.T) {
105105
}
106106
}
107107

108+
func TestSendEach(t *testing.T) {
109+
messages := []*messaging.Message{
110+
{
111+
Notification: &messaging.Notification{
112+
Title: "Title 1",
113+
Body: "Body 1",
114+
},
115+
Topic: "foo-bar",
116+
},
117+
{
118+
Notification: &messaging.Notification{
119+
Title: "Title 2",
120+
Body: "Body 2",
121+
},
122+
Topic: "foo-bar",
123+
},
124+
{
125+
Notification: &messaging.Notification{
126+
Title: "Title 3",
127+
Body: "Body 3",
128+
},
129+
Token: "INVALID_TOKEN",
130+
},
131+
}
132+
133+
br, err := client.SendEachDryRun(context.Background(), messages)
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
138+
if len(br.Responses) != 3 {
139+
t.Errorf("len(Responses) = %d; want = 3", len(br.Responses))
140+
}
141+
if br.SuccessCount != 2 {
142+
t.Errorf("SuccessCount = %d; want = 2", br.SuccessCount)
143+
}
144+
if br.FailureCount != 1 {
145+
t.Errorf("FailureCount = %d; want = 1", br.FailureCount)
146+
}
147+
148+
for i := 0; i < 2; i++ {
149+
sr := br.Responses[i]
150+
if err := checkSuccessfulSendResponse(sr); err != nil {
151+
t.Errorf("Responses[%d]: %v", i, err)
152+
}
153+
}
154+
155+
sr := br.Responses[2]
156+
if sr.Success {
157+
t.Errorf("Responses[2]: Success = true; want = false")
158+
}
159+
if sr.MessageID != "" {
160+
t.Errorf("Responses[2]: MessageID = %q; want = %q", sr.MessageID, "")
161+
}
162+
if sr.Error == nil || !messaging.IsInvalidArgument(sr.Error) {
163+
t.Errorf("Responses[2]: Error = %v; want = InvalidArgumentError", sr.Error)
164+
}
165+
}
166+
167+
func TestSendEachFiveHundred(t *testing.T) {
168+
var messages []*messaging.Message
169+
const limit = 500
170+
for i := 0; i < limit; i++ {
171+
m := &messaging.Message{
172+
Topic: fmt.Sprintf("foo-bar-%d", i%10),
173+
}
174+
messages = append(messages, m)
175+
}
176+
177+
br, err := client.SendEachDryRun(context.Background(), messages)
178+
if err != nil {
179+
t.Fatal(err)
180+
}
181+
182+
if len(br.Responses) != limit {
183+
t.Errorf("len(Responses) = %d; want = %d", len(br.Responses), limit)
184+
}
185+
if br.SuccessCount != limit {
186+
t.Errorf("SuccessCount = %d; want = %d", br.SuccessCount, limit)
187+
}
188+
if br.FailureCount != 0 {
189+
t.Errorf("FailureCount = %d; want = 0", br.FailureCount)
190+
}
191+
192+
for i := 0; i < limit; i++ {
193+
sr := br.Responses[i]
194+
if err := checkSuccessfulSendResponse(sr); err != nil {
195+
t.Errorf("Responses[%d]: %v", i, err)
196+
}
197+
}
198+
}
199+
200+
func TestSendEachForMulticast(t *testing.T) {
201+
message := &messaging.MulticastMessage{
202+
Notification: &messaging.Notification{
203+
Title: "title",
204+
Body: "body",
205+
},
206+
Tokens: []string{"INVALID_TOKEN", "ANOTHER_INVALID_TOKEN"},
207+
}
208+
209+
br, err := client.SendEachForMulticastDryRun(context.Background(), message)
210+
if err != nil {
211+
t.Fatal(err)
212+
}
213+
214+
if len(br.Responses) != 2 {
215+
t.Errorf("len(Responses) = %d; want = 2", len(br.Responses))
216+
}
217+
if br.SuccessCount != 0 {
218+
t.Errorf("SuccessCount = %d; want = 0", br.SuccessCount)
219+
}
220+
if br.FailureCount != 2 {
221+
t.Errorf("FailureCount = %d; want = 2", br.FailureCount)
222+
}
223+
224+
for i := 0; i < 2; i++ {
225+
sr := br.Responses[i]
226+
if err := checkErrorSendResponse(sr); err != nil {
227+
t.Errorf("Responses[%d]: %v", i, err)
228+
}
229+
}
230+
}
231+
108232
func TestSendAll(t *testing.T) {
109233
messages := []*messaging.Message{
110234
{

messaging/messaging_batch.go

Lines changed: 144 additions & 13 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
)
@@ -80,21 +81,145 @@ type SendResponse struct {
8081
Error error
8182
}
8283

83-
// BatchResponse represents the response from the `SendAll()` and `SendMulticast()` APIs.
84+
// BatchResponse represents the response from the SendAll() and SendMulticast() APIs.
8485
type BatchResponse struct {
8586
SuccessCount int
8687
FailureCount int
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, meaning that
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, meaning
113+
// that 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, meaning
125+
// that 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, meaning that of the messages in the
146+
// 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
96-
// SendAll indicates a total failure -- i.e. none of the messages in the array could be sent.
97-
// Partial failures are indicated by a `BatchResponse` return value.
219+
// SendAll indicates a total failure, meaning that none of the messages in the array could be
220+
// sent. 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,22 +231,26 @@ 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
112-
// SendAllDryRun indicates a total failure -- i.e. none of the messages in the array could be sent
113-
// for validation. Partial failures are indicated by a `BatchResponse` return value.
237+
// SendAllDryRun indicates a total failure, meaning that none of the messages in the array could
238+
// be sent 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
}
117244

118245
// SendMulticast sends the given multicast message to all the FCM registration tokens specified.
119246
//
120247
// The tokens array in MulticastMessage may contain up to 500 tokens. SendMulticast uses the
121-
// `SendAll()` function to send the given message to all the target recipients. The
248+
// SendAll() function to send the given message to all the target recipients. The
122249
// responses list obtained from the return value corresponds to the order of the input tokens. An
123-
// error from SendMulticast indicates a total failure -- i.e. the message could not be sent to any
124-
// of the recipients. Partial failures are indicated by a `BatchResponse` return value.
250+
// error from SendMulticast indicates a total failure, meaning that the message could not be sent
251+
// to any 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 {
@@ -138,10 +267,12 @@ func (c *fcmClient) SendMulticast(ctx context.Context, message *MulticastMessage
138267
// the SDK-level and backend validations on the messages, and emulates the send operation.
139268
//
140269
// The tokens array in MulticastMessage may contain up to 500 tokens. SendMulticastDryRun uses the
141-
// `SendAllDryRun()` function to send the given message. The responses list obtained from
270+
// SendAllDryRun() function to send the given message. The responses list obtained from
142271
// the return value corresponds to the order of the input tokens. An error from SendMulticastDryRun
143-
// indicates a total failure -- i.e. none of the messages were sent to FCM for validation. Partial
144-
// failures are indicated by a `BatchResponse` return value.
272+
// indicates a total failure, meaning that none of the messages were sent to FCM for validation.
273+
// Partial 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)