Skip to content

Commit

Permalink
Release 2.7.0 (#135)
Browse files Browse the repository at this point in the history
* Changlog updates (#123)

* Reading FCM error code from details section (#127)

* Adding additional details to the error message when available (#129)

* Implementing support for APNS content-mutable field (#126)

* Implementing support for APNS content-mutable field

* Corrected the mutable-content option name

* Renamed CustomFields to CustomData

* Added comment

* Implementing Functions to Test Error Codes (#131)

* Adding error codes to the auth package

* Added error codes for messaging

* Responding to review feedback; Updated changelog

* Add Go import comments. (#132)

* Bumped version to 2.7.0 (#133)

* Bumped version to 2.7.0

* Updated changelog
  • Loading branch information
hiranya911 committed Apr 17, 2018
1 parent c5db67b commit 7f0a0da
Show file tree
Hide file tree
Showing 14 changed files with 494 additions and 110 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Unreleased

-

# v2.7.0

- [added] Added several new functions for testing errors
(e.g. `auth.IsUserNotFound()`).
- [added] Added support for setting the `mutable-content` property on
FCM messages sent via APNS.
- [changed] Updated the error messages returned by the `messaging`
package. These errors now contain the full details sent by the
back-end server.

# v2.6.1

- [added] Added support for Go 1.6.
Expand Down
2 changes: 1 addition & 1 deletion auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func (c *Client) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken strin
}

if p.IssuedAt*1000 < user.TokensValidAfterMillis {
return nil, fmt.Errorf("ID token has been revoked")
return nil, internal.Error(idTokenRevoked, "ID token has been revoked")
}
return p, nil
}
Expand Down
2 changes: 1 addition & 1 deletion auth/auth_std.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package auth
package auth // import "firebase.google.com/go/auth"

import "golang.org/x/net/context"

Expand Down
2 changes: 1 addition & 1 deletion auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func TestVerifyIDTokenAndCheckRevokedInvalidated(t *testing.T) {

p, err := s.Client.VerifyIDTokenAndCheckRevoked(ctx, tok)
we := "ID token has been revoked"
if p != nil || err == nil || err.Error() != we {
if p != nil || err == nil || err.Error() != we || !IsIDTokenRevoked(err) {
t.Errorf("VerifyIDTokenAndCheckRevoked(ctx, token) =(%v, %v); want = (%v, %v)",
p, err, nil, we)
}
Expand Down
116 changes: 101 additions & 15 deletions auth/user_mgt.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import (
"strings"
"time"

"firebase.google.com/go/internal"
"golang.org/x/net/context"

"google.golang.org/api/googleapi"
"google.golang.org/api/identitytoolkit/v3"
"google.golang.org/api/iterator"
)
Expand Down Expand Up @@ -215,8 +217,10 @@ func (c *Client) DeleteUser(ctx context.Context, uid string) error {

call := c.is.Relyingparty.DeleteAccount(request)
c.setHeader(call)
_, err := call.Context(ctx).Do()
return err
if _, err := call.Context(ctx).Do(); err != nil {
return handleServerError(err)
}
return nil
}

// GetUser gets the user data corresponding to the specified user ID.
Expand Down Expand Up @@ -279,7 +283,7 @@ func (it *UserIterator) fetch(pageSize int, pageToken string) (string, error) {
it.client.setHeader(call)
resp, err := call.Context(it.ctx).Do()
if err != nil {
return "", err
return "", handleServerError(err)
}

for _, u := range resp.Users {
Expand Down Expand Up @@ -345,10 +349,7 @@ func processClaims(p map[string]interface{}) error {
return nil
}

claims, ok := cc.(map[string]interface{})
if !ok {
return fmt.Errorf("unexpected type for custom claims")
}
claims := cc.(map[string]interface{})
for _, key := range reservedClaims {
if _, ok := claims[key]; ok {
return fmt.Errorf("claim %q is reserved and must not be set", key)
Expand All @@ -372,6 +373,83 @@ func processClaims(p map[string]interface{}) error {
return nil
}

// Error handlers.

const (
emailAlredyExists = "email-already-exists"
idTokenRevoked = "id-token-revoked"
insufficientPermission = "insufficient-permission"
phoneNumberAlreadyExists = "phone-number-already-exists"
projectNotFound = "project-not-found"
uidAlreadyExists = "uid-already-exists"
unknown = "unknown-error"
userNotFound = "user-not-found"
)

// IsEmailAlreadyExists checks if the given error was due to a duplicate email.
func IsEmailAlreadyExists(err error) bool {
return internal.HasErrorCode(err, emailAlredyExists)
}

// IsIDTokenRevoked checks if the given error was due to a revoked ID token.
func IsIDTokenRevoked(err error) bool {
return internal.HasErrorCode(err, idTokenRevoked)
}

// IsInsufficientPermission checks if the given error was due to insufficient permissions.
func IsInsufficientPermission(err error) bool {
return internal.HasErrorCode(err, insufficientPermission)
}

// IsPhoneNumberAlreadyExists checks if the given error was due to a duplicate phone number.
func IsPhoneNumberAlreadyExists(err error) bool {
return internal.HasErrorCode(err, phoneNumberAlreadyExists)
}

// IsProjectNotFound checks if the given error was due to a non-existing project.
func IsProjectNotFound(err error) bool {
return internal.HasErrorCode(err, projectNotFound)
}

// IsUIDAlreadyExists checks if the given error was due to a duplicate uid.
func IsUIDAlreadyExists(err error) bool {
return internal.HasErrorCode(err, uidAlreadyExists)
}

// IsUnknown checks if the given error was due to a unknown server error.
func IsUnknown(err error) bool {
return internal.HasErrorCode(err, unknown)
}

// IsUserNotFound checks if the given error was due to non-existing user.
func IsUserNotFound(err error) bool {
return internal.HasErrorCode(err, userNotFound)
}

var serverError = map[string]string{
"CONFIGURATION_NOT_FOUND": projectNotFound,
"DUPLICATE_EMAIL": emailAlredyExists,
"DUPLICATE_LOCAL_ID": uidAlreadyExists,
"EMAIL_EXISTS": emailAlredyExists,
"INSUFFICIENT_PERMISSION": insufficientPermission,
"PHONE_NUMBER_EXISTS": phoneNumberAlreadyExists,
"PROJECT_NOT_FOUND": projectNotFound,
}

func handleServerError(err error) error {
gerr, ok := err.(*googleapi.Error)
if !ok {
// Not a back-end error
return err
}
serverCode := gerr.Message
clientCode, ok := serverError[serverCode]
if !ok {
clientCode = unknown
}
return internal.Error(clientCode, err.Error())
}

// Validators.

func validateDisplayName(val interface{}) error {
Expand Down Expand Up @@ -532,7 +610,7 @@ func (c *Client) createUser(ctx context.Context, user *UserToCreate) (string, er
c.setHeader(call)
resp, err := call.Context(ctx).Do()
if err != nil {
return "", err
return "", handleServerError(err)
}

return resp.LocalId, nil
Expand All @@ -555,20 +633,29 @@ func (c *Client) updateUser(ctx context.Context, uid string, user *UserToUpdate)

call := c.is.Relyingparty.SetAccountInfo(request)
c.setHeader(call)
_, err := call.Context(ctx).Do()

return err
if _, err := call.Context(ctx).Do(); err != nil {
return handleServerError(err)
}
return nil
}

func (c *Client) getUser(ctx context.Context, request *identitytoolkit.IdentitytoolkitRelyingpartyGetAccountInfoRequest) (*UserRecord, error) {
call := c.is.Relyingparty.GetAccountInfo(request)
c.setHeader(call)
resp, err := call.Context(ctx).Do()
if err != nil {
return nil, err
return nil, handleServerError(err)
}
if len(resp.Users) == 0 {
return nil, fmt.Errorf("cannot find user given params: id:%v, phone:%v, email: %v", request.LocalId, request.PhoneNumber, request.Email)
var msg string
if len(request.LocalId) == 1 {
msg = fmt.Sprintf("cannot find user from uid: %q", request.LocalId[0])
} else if len(request.Email) == 1 {
msg = fmt.Sprintf("cannot find user from email: %q", request.Email[0])
} else {
msg = fmt.Sprintf("cannot find user from phone number: %q", request.PhoneNumber[0])
}
return nil, internal.Error(userNotFound, msg)
}

eu, err := makeExportedUser(resp.Users[0])
Expand All @@ -581,8 +668,7 @@ func (c *Client) getUser(ctx context.Context, request *identitytoolkit.Identityt
func makeExportedUser(r *identitytoolkit.UserInfo) (*ExportedUserRecord, error) {
var cc map[string]interface{}
if r.CustomAttributes != "" {
err := json.Unmarshal([]byte(r.CustomAttributes), &cc)
if err != nil {
if err := json.Unmarshal([]byte(r.CustomAttributes), &cc); err != nil {
return nil, err
}
if len(cc) == 0 {
Expand Down
45 changes: 35 additions & 10 deletions auth/user_mgt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,23 +149,21 @@ func TestGetNonExistingUser(t *testing.T) {
s := echoServer([]byte(resp), t)
defer s.Close()

want := "cannot find user given params: id:[%s], phone:[%s], email: [%s]"

we := fmt.Sprintf(want, "id-nonexisting", "", "")
we := `cannot find user from uid: "id-nonexisting"`
user, err := s.Client.GetUser(context.Background(), "id-nonexisting")
if user != nil || err == nil || err.Error() != we {
if user != nil || err == nil || err.Error() != we || !IsUserNotFound(err) {
t.Errorf("GetUser(non-existing) = (%v, %q); want = (nil, %q)", user, err, we)
}

we = fmt.Sprintf(want, "", "", "foo@bar.nonexisting")
we = `cannot find user from email: "foo@bar.nonexisting"`
user, err = s.Client.GetUserByEmail(context.Background(), "foo@bar.nonexisting")
if user != nil || err == nil || err.Error() != we {
if user != nil || err == nil || err.Error() != we || !IsUserNotFound(err) {
t.Errorf("GetUserByEmail(non-existing) = (%v, %q); want = (nil, %q)", user, err, we)
}

we = fmt.Sprintf(want, "", "+12345678901", "")
we = `cannot find user from phone number: "+12345678901"`
user, err = s.Client.GetUserByPhoneNumber(context.Background(), "+12345678901")
if user != nil || err == nil || err.Error() != we {
if user != nil || err == nil || err.Error() != we || !IsUserNotFound(err) {
t.Errorf("GetUserPhoneNumber(non-existing) = (%v, %q); want = (nil, %q)", user, err, we)
}
}
Expand Down Expand Up @@ -642,7 +640,6 @@ func TestInvalidDeleteUser(t *testing.T) {
}

func TestMakeExportedUser(t *testing.T) {

rur := &identitytoolkit.UserInfo{
LocalId: "testuser",
Email: "testuser@example.com",
Expand Down Expand Up @@ -704,11 +701,39 @@ func TestHTTPError(t *testing.T) {
}

want := `googleapi: got HTTP response code 500 with body: {"error":"test"}`
if err.Error() != want {
if err.Error() != want || !IsUnknown(err) {
t.Errorf("GetUser() = %v; want = %q", err, want)
}
}

func TestHTTPErrorWithCode(t *testing.T) {
errorCodes := map[string]func(error) bool{
"CONFIGURATION_NOT_FOUND": IsProjectNotFound,
"DUPLICATE_EMAIL": IsEmailAlreadyExists,
"DUPLICATE_LOCAL_ID": IsUIDAlreadyExists,
"EMAIL_EXISTS": IsEmailAlreadyExists,
"INSUFFICIENT_PERMISSION": IsInsufficientPermission,
"PHONE_NUMBER_EXISTS": IsPhoneNumberAlreadyExists,
"PROJECT_NOT_FOUND": IsProjectNotFound,
}
s := echoServer(nil, t)
defer s.Close()
s.Status = http.StatusInternalServerError

for code, check := range errorCodes {
s.Resp = []byte(fmt.Sprintf(`{"error":{"message":"%s"}}`, code))
u, err := s.Client.GetUser(context.Background(), "some uid")
if u != nil || err == nil {
t.Fatalf("GetUser() = (%v, %v); want = (nil, error)", u, err)
}

want := fmt.Sprintf("googleapi: Error 500: %s", code)
if err.Error() != want || !check(err) {
t.Errorf("GetUser() = %v; want = %q", err, want)
}
}
}

type mockAuthServer struct {
Resp []byte
Header map[string]string
Expand Down
2 changes: 1 addition & 1 deletion db/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package db
package db // import "firebase.google.com/go/db"

import (
"encoding/json"
Expand Down
4 changes: 2 additions & 2 deletions firebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// Package firebase is the entry point to the Firebase Admin SDK. It provides functionality for initializing App
// instances, which serve as the central entities that provide access to various other Firebase services exposed
// from the SDK.
package firebase
package firebase // import "firebase.google.com/go"

import (
"encoding/json"
Expand All @@ -42,7 +42,7 @@ import (
var defaultAuthOverrides = make(map[string]interface{})

// Version of the Firebase Go Admin SDK.
const Version = "2.6.1"
const Version = "2.7.0"

// firebaseEnvName is the name of the environment variable with the Config.
const firebaseEnvName = "FIREBASE_CONFIG"
Expand Down
2 changes: 1 addition & 1 deletion iid/iid.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

// Package iid contains functions for deleting instance IDs from Firebase projects.
package iid
package iid // import "firebase.google.com/go/iid"

import (
"errors"
Expand Down

0 comments on commit 7f0a0da

Please sign in to comment.